krisnova / naml

Convert Kubernetes YAML to Golang
Apache License 2.0
1.26k stars 38 forks source link

[Question] What would be the equivalent to helm values.yaml #37

Closed aledbf closed 3 years ago

aledbf commented 3 years ago

The full example uses arguments but, in some scenarios, is not practical. For instance, take this old chart for a docker registry

krisnova commented 3 years ago

So this is going to be a long answer, because this is a good question. 🎉

I am going to do my best to offer blanket statements that can be referenced, and just capture the state of ecosystem. I have a few opinions in here, and I will do my best to call out my opinions from the facts. Ultimately my goal is to be kind and offer a good resource.


What is naml equivalent to Helm's Values.yaml?

Basically it would be some sort of system in Go that you and your team will write.

In my opinion, the reason that Values.yaml exists in Helm is because it offers an abstraction and a means to manage the abstraction.

This is convenient because it allows authors to "stich" or "glue" various configuration together to create the concept of a holistic application configuration.

.
├── Abstraction # <-- Helm calls this Values.yaml
└── KubernetesObjects # <-- Helm calls this "templates"
    ├── ConfigMap # <-- These are Kubernetes objects
    ├── Deployment # <-- Helm calls this deployment.yaml
    ├── RoleBasedAccessControl # <-- More Kubernetes objects
    └── Service # <-- More Kubernetes objects

The "Abstraction" pattern overall is a useful idea. However for some users the .yaml implementation does a bad job at offering traditional features of programming languages.

In other words Helm's Values.yaml is not only just the concept of a higher level abstraction, it also is a prescription on how to abstract the Kubernetes objects.

What is Values.yaml?

It is both an abstraction pattern, as well as the implicit tooling (helm) to manage the abstraction.

  1. Abstraction pattern
  2. Tools to manage the abstraction concretely

To be honest, (my opinion) I don't think it's a very good abstraction pattern, especially when you look at managing large archives of applications, testing, and standardizing applications.

We see this in Linux

This is a hard problem to solve, and Helm took a swing at trying to offer an abstraction as well as a Turing complete configuration paradigm on top. Linux also ran into this problem with package management with .deb, .rpm, and .pkg.tar.zst packages.

That is to say the packages started out just being a static 1:1 mapping of files and a location on the filesystem. Over time more and more Turing complete logic crept into these packages. Now all of the major Linux operating systems have enabled package authors to do basically whatever they want at runtime when it comes time to installing packages.

The world 🌎 needs logic

The lesson here is that static application packaging sounds nice on paper but just don't fucking work in the real world.

naml

This project (naml) for one embraces our new Turing complete package management overlords, and it embraces it with raw Go.

Meaning that in a world where abstractions used to ship with tooling on how to manage the abstraction (EG: helm) we are doing something different. We are offering a (more traditional) approach at managing applications.

I put a lot of thought into the interface, and ultimately the interface is the new constraint for managing your abstraction.


The original concern

With all that said I can now respond to your original issue

The full example uses arguments but, in some scenarios, is not practical. For instance, take this old chart for a docker registry

The paradigm here is that a software engineer would need to be responsible for implementing Install(). Which means we would expect a developer to design, build, and implement their own systems here. This is a bold fucking statement, and I know it is.

So if you had a lot of configuration in your abstraction like in the link you shared. You could do something like this.

type MySampleAppPublic struct {
    ExampleValue string
    ExampleNumber int
    ExampleText string
    ExampleToggle bool
    ExampleVerbose int
    ExampleName string
    ExampleAnnotations map[string]string
    ExampleValues map[int]string
    ExampleValue1 string
    ExampleValue2 string
    ExampleValue3 string
}

With corresponding calling code such as

func main(){
// ...
    publicApp := &app.MySampleAppPublic{
        ExampleValue:       "",
        ExampleNumber:      0,
        ExampleText:        "",
        ExampleToggle:      false,
        ExampleVerbose:     0,
        ExampleName:        "",
        ExampleAnnotations: nil,
        ExampleValues:      nil,
        ExampleValue1:      "",
        ExampleValue2:      "",
        ExampleValue3:      "",
    }

    // Pass the public app fields to the New() function
    a := app.New("my-name", "my-namespace", "my-description", publicApp)
//...

In a perfect world the ultimate design of these systems are up to you and your team.

There are trade offs to how you expose the fields, and considerations about things like

Again -- the entire point of naml is for enterprise application managers who have enough to manage that writing this stuff in code makes sense. If they are at the point of writing code, I am assuming they are at the point where they can craft their own systems to do things like this.

Feel free to re-open this issue if you have any more questions.

Thanks for asking a great question, and good luck helping us all burn down late stage capitalism.

aledbf commented 3 years ago

@kris-nova thank you for taking the time to reply with this level of detail :clap:

To be honest, (my opinion) I don't think it's a very good abstraction pattern, especially when you look at managing large archives of applications, testing, and standardizing applications.

Agree. Right now I need to migrate an existing chart with a bazillion options, and no tests :upside_down_face: