Categorygithub.com/elastic/go-resource
modulepackage
0.2.0
Repository: https://github.com/elastic/go-resource.git
Documentation: pkg.go.dev

# README

resource - standalone configuration management

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:

  • Define test or development scenarios in a declarative way.
  • Configure systems with a single static binary, in a cloudinit fashion.
  • [TBD] Configure infrastructure from serverless functions.

resource framework

The framework for resource is based on the following concepts:

  • Facts: information obtained from the environment to customize the execution and not dependant on the defined resources (OS information, environment variables...).
  • Resources: the actual resources to manage. Their semantics would be: Get/Create/Update.
  • Providers: implementations of the resources, they can contain configuration. There can be multiple instances of the same provider, resources should be able to select which one to use, with a default one.
  • Manager: processes all defined resources, generates a plan and executes it.

Some extras that are being considered or in development:

  • Conditions: To run resources depending on facts.
  • Dependencies: To control the order of execution of resources.
  • Migrations: allow to version configurations, and implement migration (and rollback?) processes that cannot be managed by resources themselves.
  • Modules: Parameterizable collections of resources.

Getting started

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.

Space, Time

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.

# Functions

FileContentLiteral returns a literal file content.
FileMode is a helper function to create a *fs.FileMode inline.
NewManager instantiates a new empty manager.
No description provided by the author
NewSourceFS returns a new SourceFS with the root file system.

# Constants

ActionCreate refers to an action that creates a resource.
ActionUnknown is used to indicate a failure happening before determining the required action.
ActionUpdate refers to an action that affects an existing resource.

# Variables

DefaultHTTPSource is a SourceHTTP that uses the default HTTP client.

# Structs

ApplyResult is the result of applying a resource.
EnvFacter is a facter that gets facts from environment variables.
File is a resource that manages a file.
FileProvider is a provider of files.
No description provided by the author
HTTPSource is a file source that can be used to obtain contents from http resources.
Main is a helper to generate single binaries to manage a collection of resources.
Manager manages application of resources, it contains references to providers and facters.
No description provided by the author
SourceFS is an abstracted file system that can be used to obtail file contents.

# Interfaces

Context is the context of execution when applying resources.
Facter is the interface implemented by facters.
Provider is the interface implemented by providers.
Resource implements management for a resource.
ResourceState is the state of a resource.
No description provided by the author

# Type aliases

ApplyResults is the colection of results when applying a collection of resources.
FileContent defines the content of a file.
No description provided by the author
Resources is a collection of resources.
StaticFacter is a facter implemented as map.