resource
is a Go package for declarative configuration
management
in a system, in the line of
Puppet or Chef, but embedded in Go binaries.
It is intended to be idempotent, and stateless, two executions of the same code in the same system should produce the same result.
Some use cases:
The framework for resource
is based on the following concepts:
Some extras that are being considered or in development:
You can start using this package by importing it.
import "github.com/elastic/go-resource"
Find here an example that creates some files for a docker compose scenario that starts the Elastic Stack:
package main
import (
"embed"
"log"
"github.com/elastic/go-resource"
)
//go:embed _static
var static embed.FS
var (
// Define a source of files from an embedded file system
// You can include additional functions for templates.
templateFuncs = template.FuncMap{
"semverLessThan": semverLessThan,
}
staticSource = resource.NewSourceFS(static).WithTemplateFuncs(templateFuncs)
// Define the resources.
stackResources = []resource.Resource{
// Files can be defined as static files, or as templates.
&resource.File{
Provider: "stack-file",
Path: "Dockerfile.package-registry",
Content: staticSource.Template("_static/Dockerfile.package-registry.tmpl"),
},
&resource.File{
Provider: "stack-file",
Path: "docker-compose.yml",
Content: staticSource.File("_static/docker-compose-stack.yml"),
},
&resource.File{
Provider: "stack-file",
Path: "elasticsearch.yml",
Content: staticSource.Template("_static/elasticsearch.yml.tmpl"),
},
&resource.File{
Provider: "stack-file",
Path: "kibana.yml",
Content: staticSource.Template("_static/kibana.yml.tmpl"),
},
&resource.File{
Provider: "stack-file",
Path: "package-registry.yml",
Content: staticSource.File("_static/package-registry.yml"),
},
}
)
func main() {
// Instantiate a new manager.
manager := resource.NewManager()
// Install some facts in the manager. These facts can be
// used by template files or other resources.
manager.AddFacter(resource.StaticFacter{
"registry_base_image": packageRegistryBaseImage,
"elasticsearch_version": stackVersion,
"kibana_version": stackVersion,
})
// Configure a file provider to decide the prefix path where
// files will be installed.
manager.RegisterProvider("stack-file", &resource.FileProvider{
Prefix: stackDir,
})
// Apply the defined resources.
results, err := manager.Apply(stackResources)
// If there are errors, they can be individually inspected in the
// returned results.
if err != nil {
for _, result := range results {
if err := result.Err(); err != nil {
log.Println(err)
}
}
log.Println(err)
}
}
The main
function can be also implemented using the Main
helper:
func main() {
stackMain := resource.Main{
Facters: []Facter{
resource.StaticFacter{
"registry_base_image": packageRegistryBaseImage,
"elasticsearch_version": stackVersion,
"kibana_version": stackVersion,
}),
// Add a facter to get variables from environment.
// The value in the last facter has precedence.
&EnvFacter{},
},
Providers: map[string]Provider{
"stack-file": &resource.FileProvider{
Prefix: stackDir,
})
},
Resources: stackResources,
}
// Run the main helper, it will print errors as needed.
err := stackMain.Run()
if err != nil {
log.Fatal(err)
}
}
You can find this complete example and others in TBD.
This project started during an ON Week, a time we give each other in Elastic to explore ideas or learn new things, in alignment with our Source Code.