Portable Go module with bundled files that must be read

Issue

I’m trying to create a portable Go module that will be re-used in many different projects. This module, in order to function, needs to be able to read non-Go files that are bundled as part of the module. In this case, they’re some certificate bundles. The selection of which file to load is dynamic and based on input arguments.

What is the best way to specify the path to these files when loading them? I can’t seem to find any Go functions to get a path relative to the module (vs. relative to the executable that is using this module). For example, if my module structure looks like this:

mymodule/
  go.mod
  go.sum
  loadcerts.go
  certs/
    cert_A.pem
    cert_B.pem

And I need to do something like this:

// loadcerts.go

package mymodule

func LoadCerts(useB bool) error {
    newCertPool := x509.NewCertPool()

    // This is just to show that the selection is dynamic, and since
    // there are many different potential files to load, we can't 
    // embed them all
    bundleName := "cert_A.pem"
    if useB {
        bundleName = "cert_B.pem"
    }
    pem, err := ioutil.ReadFile(fmt.Sprintf("./certs/%s", bundleName))
    if err != nil {
        return err
    }
    if ok := newCertPool.AppendCertsFromPEM(pem); !ok {
        return err
    }
    ...
}

Referencing this file with a relative path (./certs/cert1.pem) doesn’t work, since Go uses the executable’s working directory for relative paths, and this imported module is somewhere entirely different.

How can I load this .pem file that is bundled with the portable module, regardless of where this module is being imported to?

Solution

Embed the files in the executable as a file system:

//go:embed certs
var f embed.FS

Read the files from the file system:

pem, err := f.ReadFile(fmt.Sprintf("certs/%s", bundleName))

Answered By – Bayta Darell

Answer Checked By – David Goodson (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.