resource

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 10, 2024 License: Apache-2.0 Imports: 14 Imported by: 0

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.

Documentation

Index

Constants

View Source
const (
	// ActionUnknown is used to indicate a failure happening before determining the required action.
	ActionUnknown = "unkwnown"

	// ActionCreate refers to an action that creates a resource.
	ActionCreate = "create"

	// ActionUpdate refers to an action that affects an existing resource.
	ActionUpdate = "update"
)

Actions reported on results when applying resources.

Variables

View Source
var DefaultHTTPSource = &HTTPSource{Client: http.DefaultClient}

DefaultHTTPSource is a SourceHTTP that uses the default HTTP client.

Functions

func FileMode added in v0.1.1

func FileMode(mode fs.FileMode) *fs.FileMode

FileMode is a helper function to create a *fs.FileMode inline.

Types

type ApplyResult

type ApplyResult struct {
	// contains filtered or unexported fields
}

ApplyResult is the result of applying a resource.

func (ApplyResult) Err

func (r ApplyResult) Err() error

Err returns an error if the application of a resource failed.

func (ApplyResult) String

func (r ApplyResult) String() string

String returns the string representation of the result of applying a resource.

type ApplyResults

type ApplyResults []ApplyResult

ApplyResults is the colection of results when applying a collection of resources.

type Context

type Context interface {
	context.Context

	// Provider obtains a provider from the context, and sets it in the target.
	// The target must be a pointer to a provider type.
	// It returns false, and doesn't set the target if no provider is found with
	// the given name and target type.
	Provider(name string, target any) (found bool)

	// Fact returns the value of a fact for a given name and true if it is found.
	// It not found, it returns an empty string and false.
	Fact(name string) (value string, found bool)
}

Context is the context of execution when applying resources. It also implements `context.Context`.

type EnvFacter

type EnvFacter struct {
	// Prefix used to find facts in environment variables. If not
	// set, "FACT" is used.
	Prefix string
}

EnvFacter is a facter that gets facts from environment variables. Facts can be defined in environment variables starting with the "FACT" prefix. For example the "runtime" fact could be set with "FACT_runtime". A different setting can be selected using the Prefix attribute.

func (*EnvFacter) Fact

func (f *EnvFacter) Fact(name string) (string, bool)

Fact returns the value of a fact obtained from the environment if it exists. If not, it returns an empty string and false.

type Facter

type Facter interface {
	// Fact returns the value of a fact for a given name and true if it is found.
	// It not found, it returns an empty string and false.
	Fact(name string) (value string, found bool)
}

Facter is the interface implemented by facters. Facters provide, facts, with information about the execution context, they can be queried through the manager.

type File

type File struct {
	// Provider is the name of the provider to use, defaults to "file".
	Provider string
	// Path is the path of the file.
	Path string
	// Absent is set to true to indicate that the file should not exist. If it
	// exists, the file is removed.
	Absent bool
	// Mode is the file mode and permissions of the file. If not set, defaults to 0644
	// for files and 0755 for directories.
	Mode *fs.FileMode
	// Directory is set to true to indicate that the file is a directory.
	Directory bool
	// CreateParent is set to true if parent path should be created too.
	CreateParent bool
	// Force forces destructive operations, such as removing a file to replace it
	// with a directory, or the other way around. These operations will fail if
	// force is not set.
	Force bool
	// Content is the content for the file.
	// TODO: Support directory contents.
	Content FileContent
	// KeepExistingContent keeps content of file if it exists.
	KeepExistingContent bool
	// MD5 is the expected md5 sum of the content of the file. If the current content
	// of the file matches this checksum, the file is not updated.
	MD5 string
}

File is a resource that manages a file.

func (*File) Create

func (f *File) Create(ctx Context) error

func (*File) Get

func (f *File) Get(ctx Context) (current ResourceState, err error)

func (*File) String

func (f *File) String() string

func (*File) Update

func (f *File) Update(ctx Context) error

type FileContent

type FileContent func(Context, io.Writer) error

FileContent defines the content of a file. It recives an apply context to obtain information from the execution, and a writer where to write the content.

func FileContentLiteral

func FileContentLiteral(content string) FileContent

FileContentLiteral returns a literal file content.

type FileProvider

type FileProvider struct {
	Prefix string
}

FileProvider is a provider of files. It can be configured with the prefix path where files should be managed.

type FileState

type FileState struct {
	// contains filtered or unexported fields
}

func (*FileState) Found

func (f *FileState) Found() bool

func (*FileState) NeedsUpdate

func (f *FileState) NeedsUpdate(resource Resource) (bool, error)

type HTTPSource

type HTTPSource struct {
	// Client is the client used to make HTTP requests. If no client is configured,
	// the default one is used.
	Client *http.Client
}

HTTPSource is a file source that can be used to obtain contents from http resources.

func (*HTTPSource) Get

func (s *HTTPSource) Get(location string) FileContent

Get obtains the content with an http request to the given location.

type Main

type Main struct {
	// Facters is the list of facters used by this command.
	Facters []Facter

	// Providers is the list of providers used by this command.
	Providers map[string]Provider

	// Resources is the list of resources managed by this command.
	Resources Resources
}

Main is a helper to generate single binaries to manage a collection of resources.

func (*Main) Run

func (c *Main) Run() error

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager manages application of resources, it contains references to providers and facters.

func NewManager

func NewManager() *Manager

NewManager instantiates a new empty manager.

func (*Manager) AddFacter

func (m *Manager) AddFacter(facter Facter)

AddFacter adds a facter to the manager. Facters added later have precedence.

func (*Manager) Apply

func (m *Manager) Apply(resources Resources) (ApplyResults, error)

Apply applies a collection of resources. Depending on their current state, resources are created or updated.

func (*Manager) ApplyCtx

func (m *Manager) ApplyCtx(ctx context.Context, resources Resources) (ApplyResults, error)

ApplyCtx applies a collection of resources with a context that is passed to resource operations. Depending on their current state, resources are created or updated.

func (*Manager) Context

func (m *Manager) Context(ctx context.Context) Context

Context returns a resource context that wraps the given context and the manager.

func (*Manager) Fact

func (m *Manager) Fact(name string) (string, bool)

Fact returns the value of a fact for a given name and true if it is found. It not found, it returns an empty string and false. If a fact is available in multiple facters, the value in the last added facter is returned.

func (*Manager) Provider

func (m *Manager) Provider(name string, target any) bool

Provider obtains a provider from the context, and sets it in the target. The target must be a pointer to a provider type. It returns false, and doesn't set the target if no provider is found with the given name and target type.

func (*Manager) RegisterProvider

func (m *Manager) RegisterProvider(name string, provider Provider)

Register provider registers a provider in the Manager.

type Migration

type Migration func(*Manager) (ApplyResults, error)

type Migrator

type Migrator struct {
	// contains filtered or unexported fields
}

func NewMigrator

func NewMigrator(versioner Versioner) *Migrator

func (*Migrator) AddMigration

func (m *Migrator) AddMigration(version uint, migration Migration)

func (*Migrator) RunMigrations

func (m *Migrator) RunMigrations(manager *Manager) (ApplyResults, error)

type Provider

type Provider interface {
}

Provider is the interface implemented by providers.

type Resource

type Resource interface {
	// Get gets the current state of a resource. An error is returned if the state couldn't
	// be determined. An error here interrupts execution.
	Get(Context) (current ResourceState, err error)

	// Create implements the creation of the resource. It can return an error, that is reported
	// as part of the execution result.
	Create(Context) error

	// Update implements the upodate of an existing resource. Ir can return an error, that
	// is reported as part of the execution result.
	Update(Context) error
}

Resource implements management for a resource.

type ResourceState

type ResourceState interface {
	// Found returns true if the resource exists.
	Found() bool

	// NeedsUpdate returns true if the resource needs update when compared with the given
	// resource definition.
	NeedsUpdate(definition Resource) (bool, error)
}

ResourceState is the state of a resource.

type Resources

type Resources []Resource

Resources is a collection of resources.

type SourceFS

type SourceFS struct {
	fs.FS
	// contains filtered or unexported fields
}

SourceFS is an abstracted file system that can be used to obtail file contents.

func NewSourceFS

func NewSourceFS(root fs.FS) *SourceFS

NewSourceFS returns a new SourceFS with the root file system.

func (*SourceFS) File

func (s *SourceFS) File(path string) FileContent

File returns the file content for a given path in the source file system.

func (*SourceFS) Template

func (s *SourceFS) Template(path string) FileContent

Template returns the file content for a given path in the source file system. If the file contains a template, this template is executed. The template can use the `fact(string) string` function, as well as other functions defined with `WithTemplateFuncs`.

func (*SourceFS) WithTemplateFuncs

func (s *SourceFS) WithTemplateFuncs(fmap template.FuncMap) *SourceFS

WithTemplateFuncs sets and returns a set of functions that can be used by templates in this source file system.

type StaticFacter

type StaticFacter map[string]string

StaticFacter is a facter implemented as map.

func (StaticFacter) Fact

func (f StaticFacter) Fact(name string) (value string, found bool)

Fact returns the value of a fact for a given name and true if it is found. It not found, it returns an empty string and false.

type Versioner

type Versioner interface {
	Current() uint
	Set(uint) error
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL