Need help understanding this call chain

Im new to golang and Im playing with writing a custom terraform module (terraform is written in go)

I went through the beginner golang tuts so I have some understanding of pointers, structs, methods, closures, etc, but Im still a little befuddled by this. I’m getting a little ahead of myself but I’m appreciate any assistance in helping understand what Im looking at here:

package main
import (
  "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
  "github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
  "terraform-provider-hashicups/hashicups"
)
func main() {
  /////// This is what I want to understand- this whole call chain
  plugin.Serve(&plugin.ServeOpts{
    ProviderFunc: func() *schema.Provider {
      return hashicups.Provider()
    },
  })
}

Is this what is happening?..

  1. Im calling the Serve() method of the plugin module
  2. Im passing the same instance of the plugin object to its own Serve() method? And modifying the plugin objects state right? Sort of like a singleton?
  3. Im overriding the default ServeOpts struct of the plugin object with this one im passing in?
  4. The ServerOpts struct has a ProviderFunc field which holds a function. Im overriding that function with this one im passing in. The function Im passing is an anonymous function, the definition of which specifies it returns a pointer to schema.Provider and Im returning my own hashicups.Provider() there

The ProviderFunc is a function type in the module as well- I dont think I totally understand the utility of having it be a type

Still have a lot to learn but Id like to get some grasp of whats actually happening here

There is no plugin object; plugin is just a namespace like System in C#, or java.lang in Java. There’s no analogy to Python because in Python, everything is an object. You’re passing a *ServeOpts value to a Serve function.

I’m not sure what you mean by “overriding,” you’re setting the values of the fields, but you’re otherwise correct.

There are two reasons I can think of naming a function type:

  1. Just for the name of the type itself, like how time.Duration is just an int64. There’s no real type-safety with it (e.g. the compiler will let you write time.Second * time.Microsecond, but it doesn’t make sense to do that) but wherever you see the time.Duration type in use, you know that durations are in nanoseconds. You should not see (many) functions in go expecting a milliseconds parameter, or others with seconds as a float64, etc… The standard is to use time.Duration like 3*time.Second, 10*time.Minute, etc.

  2. Adding methods to functions, for example, you can make a func(b []byte) (int, error) implement the io.Reader interface like this:

    type ReaderFunc func(b []byte) (int, error)
    
    func (f ReaderFunc) Read(b []byte) (int, error) {
        return f(b)
    }
    
    // ...
    
    _, _ = io.Copy(&bytes.Buffer{}, ReaderFunc(func(_ []byte) (int, error) {
        // This is a stupid example, just showing that
        // functions can implement interfaces, too.
        return 0, io.EOF
    })
    
1 Like