1. 22

https://gist.github.com/lf94/d16b55fa7c79d989e211367a44d1a9cc#file-sml-L83 in case anyone misses it in action

  1.  

  2. 6

    If you want configuration management and typed functional programming you should look at Propeller, which uses Haskell. Take a look at the sample config:

    https://git.joeyh.name/index.cgi/propellor.git/tree/joeyconfig.hs

    1. 2

      Ooh man this is fantastic! Yes this is exactly in the realm of what I was aiming for. Thank you for sharing

    2. 3

      I just moved to Nix and went all declarative. It can be a PITA sometimes, but mostly I’m quite happy.

      1. 1

        Cute! Python and ruby are traditional for these roles because the flexibility offered is useful when providing a lot of functionality on a variety of platforms. They also allow loading changes at run time. I don’t think SML fits this, right?

        1. 6

          the flexibility offered is useful when providing a lot of functionality on a variety of platforms.

          I’m not sure what’s meant by this, since every language provides the same possible functionality… The problem with using Python and Ruby still for this task is you won’t know if your program is even near correct until you run it. Then when considering monkey-patching and code with so many side-effects it makes your head spin… ouch…

          They also allow loading changes at run time.

          I’m not sure what you mean? It’s rare for these types of programs to be long-running, so “changes at run-time” sounds odd to me.

          1. 4

            you won’t know if your program is even near correct until you run it

            You are concatenating strings to generate shellscript commands. I get that this PoC vs mature software, but Ansible is significantly more robust than that.

            I can see the advantage for larger deployments, though. One of the biggest problems I had with Ansible and similar tools is that was impossible to check if a dependency was still providing the same guarantees/functionalities without reading the dependency’s code, or just straight out running it.

            However, that is only part of running stuff reliably on the ops space. A lot of what I would like to have pre-checked before an ‘apply’ is information that is completely outside of the language, it has to be queried, because it’s all external system’s states.

            Terraform is an example of a tool that does that better. Part of the correctness is achieved just in the code/types, but you need to run a plan to know the full picture. And even then, things can still fail.

            1. 3

              A lot of what I would like to have pre-checked before an ‘apply’ is information that is completely outside of the language, it has to be queried, because it’s all external system’s states.

              I’m not sure then if you read the source? Because that’s one part of what it does - for example, checking if a user exists. It can easily be modified to stop the entire program or add additional checks.

              “Ansible is significantly more robust” in what way? I would like to understand this more. I’ve found Ansible not as robust as it was promised to be.

              This isn’t a PoC, I’m using this with great success right now 🙂

              You are concatenating strings to generate shellscript commands

              I’m not sure why you’ve quoted this as a point? Underlying shells excel at providing a process calculus, so use it. People are already trained to understand most of bash / sh too. Remembering yet another shell language is not the goal.

              1. 6

                Fair enough, you are in fact querying external state. But you’re doing that at runtime. If any of the ensure calls fail, you wouldn’t know it at compile time. In fact, if any command fails, it seems like your program would just error out and leave you with a system in an inconsistent state. Ansible has the same issue, to be fair.

                Terraform goes a bit further, by having separate plan and apply phases, so you can have static checking AND query external state simultaneously, which your code doesn’t seem to do.

                By the way, the snippet doesn’t seem to run anything at all, it just builds the script and prints it out? Not that there’s anything wrong with that, I’m just not familiar with the language.

                But back to the point: your code does one cool thing, which is provide strong static guarantees about what dependencies say they’ll do. In your specific case you don’t show complex dependencies, but it seems like it could scale. Ansible does indeed suck at that. But terraform and, if I’m not mistaken, puppet, provide similar guarantees, albeit with less expressive type systems.

                It also does query external state. But not in an integrated way with the static guarantees, which the above mentioned tools do. It’s nice to know that if I call ensure with an unsupported input, shit won’t compile. But if the ssh key is missing, or the remote host is unavailable, looks like everything will compile and the deployment will still fail at runtime, leaving the system in a potentially inconsistent state.

                I hope that this makes clear what I feel is missing from your idea. To be fair, it’s missing from Ansible too.

                “Ansible is significantly more robust” in what way?

                A lot of primitive Ansible modules will use proper, well developed and tested, libraries, to interact with resources, which will provide better validation and more meaningful error. The k8s module, for instance, won’t just concatenate strings to build a kubectl command.

                I’m not sure why you’ve quoted this as a point?

                Because if you misspell a command, or a command part, everything will compile and still break at runtime.

                This isn’t a PoC, I’m using this with great success right now

                To deploy tens of databases across 5 different AWS regions and 3 environments, plus double digit VMs in both cloud and on-prem environments? Because I saw Ansible doing that, and pretty reliably. It wasn’t fun to support the code, but it worked. If you’re comparing this with Ansible as a finished tool, those are the stakes, and that was a relatively small platform, for internal use only. All the type safety in the world (and as I hopefully stablished above, type safety that is only internally consistent is good, but not adopting a new tool good) won’t make me switch if I have to concatenate kubectl and awscli commands by hand.

                1. 3

                  Thank you a lot for elaborating <3 I’m genuinely interested in your criticisms and commentary!

                  But you’re doing that at runtime

                  You’re touching on a very interesting idea here…! Very very interesting. I guess you’d have to query your external resources first, and then generate types? :o! That would be quite sick and very possible! Otherwise yes I expect most if not all tools are doing this at run-time.

                  A lot of primitive Ansible modules will use proper, well developed and tested, libraries, to interact with resources,

                  Ah yeah, totally agree. This is something that comes with time… Still I feel I got to a reliable point for my own usage quite fast.

                  To deploy tens of databases across 5 different AWS regions and 3 environments, plus double digit VMs in both cloud and on-prem environments?

                  Nah I’m not using it for that, it’s why the title is “Reflecting on personal Ansible usage”, which was mostly setting up a bunch of software (roles) that are exposed via nginx. I’d love to see someone expand replicare further to suit this need just to show it’s possible though 🙂

                  it just builds the script and prints it out

                  Yep! The invocation is cat replicare.sml | poly -q | sh But I am trying to get a shebang line so that it’s a self-contained shell-program… I wanted to keep the program as “pure” as possible though

                  Again thanks for the great discussion, I don’t want to come off as defense but more inquisitive!

                  1. 1

                    That would be quite sick and very possible

                    I literally cannot conceive this without a plan/apply model, but hey, the world shouldn’t be limited by my imagination=P

                    Yep! The invocation is cat replicare.sml | poly -q | sh

                    That’s an interesting approach for small use cases, but you’re gonna reach one crucial limitation sooner or later: bash sucks balls. Especially as a language to write long programs in, which is what your tool is generating. You could still delegate stuff to subprocesses directly, though, but you’d need a good subprocess lib. Or you could stay small, there’s room for tools of all sizes.