go2chef

package module
v0.0.0-...-f22496c Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2023 License: Apache-2.0 Imports: 9 Imported by: 0

README

go2chef: "just enough Go to get to Chef"

What is go2chef?

go2chef is a Go tool for bootstrapping Chef installations in a flexible and self-contained way. With go2chef, our goal is to make bootstrapping any node in a Chef deployment as simple as "get go2chef onto a machine and run it"

Requirements

Build Dependencies

go2chef requires Go 1.12+ for appropriate module support. Library dependencies are enumerated in the go.mod file in the repository root.

go2chef has no runtime dependencies.

Quickstart Example

Installing

go2chef is packaged in Fedora as of Fedora 32. It can be installed with:

$ sudo dnf install go2chef
Building

Build go2chef using make on Unix platforms:

$ make all		# build all variants to build/$GOOS/$GOARCH/go2chef
$ make linux		# ...or build platform-specific binaries
$ make darwin
$ make windows

On Windows, just build it using go build:

PS> mkdir build/windows/amd64
PS> go build -o build/windows/amd64/go2chef.exe ./bin
Configuring

Create a configuration file. For example, to install Chef and then download and install a custom chefctl.rb bundle from a tarball on Fedora:

{
  "steps": [
    {
      "type": "go2chef.step.install.linux.dnf",
      "name": "install chef",
      "version": "15.2.20-1.el7.x86_64",
      "source": {
        "type": "go2chef.source.http",
        "url": "https://packages.chef.io/files/stable/chef/15.2.20/el/8/chef-15.2.20-1.el7.x86_64.rpm"
      }
    },
    {
      "type": "go2chef.step.bundle",
      "name": "install chefctl",
      "source": {
        "type": "go2chef.source.local",
        "path": "./chefctl.tar.gz",
        "archive": true
      }
    }
  ]
}
Executing
  1. Copy the appropriate binary from build/$GOOS/$GOARCH/go2chef, the config file, and the chefctl bundle to a single directory on the target host

  2. Execute go2chef with the config

    $ cd path/to/copy
    $ ./go2chef --local-config config.json
    
scripts/remote.go

A remote execution script is provided in scripts/remote.go. Example usage:

$ make windows && go run scripts/remote.go --binary build/windows/amd64/go2chef.exe -B examples/bundles/chefctl -B examples/bundles/chefrepo -B examples/bundles/whoami_exec --target 10.0.10.187 -W -c examples/config_install_msi.json

Design

go2chef has four basic building blocks, all of which are implemented using a plugin model:

  • Configuration Sources (go2chef.ConfigSource): fetch go2chef configuration from remote sources
  • Loggers (go2chef.Logger): send log messages and structured events to logging backends via a common plugin API
  • Steps (go2chef.Step): implement the building blocks of a go2chef workflow. Every action that needs to be taken to set up your Chef environment can be integrated into a go2chef step. See "Steps" for more details
  • Sources (go2chef.Source): implement a common API for retrieval of remote resources needed for Step execution
Configuration Sources

Configuration sources are the plugins which allow you to customize how go2chef retrieves its runtime configuration. We provide a couple configuration plugins out-of-the-box:

  • go2chef.config_source.local: loads configuration from a JSON file accessible on the filesystem. (this is the default configuration source)
  • go2chef.config_source.http: loads configuration source in JSON format from an HTTP(S) endpoint. Enable using go2chef --config-source go2chef.config_source.http
  • go2chef.config_source.embed: loads configuration source from an embedded variable. This probably isn't what you want, but if it is, have it.

New configuration sources can be registered with go2chef.RegisterConfigSource.

Loggers

Loggers are the plugins which allow go2chef users to report run information for monitoring and analysis, and provide plugin authors with a single API for logging and events.

For Users

Logging plugins are configured using the loggers key in go2chef configuration. An example configuration setting up the default go2chef.logger.stdlib looks like:

{
  "loggers": [
    {
      "type": "go2chef.logger.stdlib",
      "name": "stdlib",
      "level": "DEBUG",
      "debugging": 1,
      "verbosity": 1
    }
  ]
}

The loggers key is an array so that you can log to multiple places, which may be useful for the following scenarios:

  1. You want your raw log messages to go to syslog, but you also want to send specific events to a separate logging service using a custom plugin to trigger some downstream action (i.e. changing asset service state).
  2. You want to log to file and stderr and syslog at varying levels of verbosity (and so on and so forth)
For Developers

Logging plugins may skip parts of the interface specification by stubbing out the unneeded methods as no-ops.

The go2chef.MultiLogger implementation synchronously dumps messages out to backends at the moment, so delays in message sending in a Logger plugin may slow down execution of go2chef as well.

Steps

Steps are the plugins which actually "do stuff" in go2chef. These can do pretty much anything you want if you implement it, but we've intentionally limited the built-in plugins to the following initially:

  • Sanity checking: make sure that the runtime environment is sane before trying to install Chef -- are we root? Is the clock set right? Is there disk space?
  • Bundle exec: provide a simple abstraction for fetching and running some arbitrary scripts/binaries before/after installation. Do things like set up required certs, install chefctl.rb, etc.
  • Installers: provide installer implementations for each platform (and sub-platforms thereof, if necessary).

Many Step implementations will require some sort of remote resource retrieval; rather than leaving it up to each implementation to bring its own support code for downloads, we provide it to you using Sources (described next).

Sources

Source plugins implement a common API for resource retrieval for go2chef. This allows all steps to configure remote resource retrieval with the same idiom:

{
  "steps": [
    {
      "type": "go2chef.step.install.linux.apt",
      "name": "install chef",
      "source": {
        "type": "go2chef.source.http",
        "url": "https://example.com/chef-15.deb"
      }
    }
  ]
}

A source key inside a step configuration block defines how the remote resources for that step should be retrieved.

Code Layout
bin/        # go2chef binary source code
build/      # temporary directory for build outputs
cli/        # CLI implementation
plugin/     # plugins directory
  config/   # configuration source plugins
  logger/   # logger plugins
  source/   # source plugins
  step/     # step plugins
*.go        # go code for the base go2chef module

Contribute

See the CONTRIBUTING file for how to help out.

License

go2chef is Apache 2.0 licensed.

Documentation

Index

Constants

View Source
const (
	LogLevelError = iota
	LogLevelInfo
	LogLevelDebug
)

Log level constants

Variables

View Source
var (
	// AutoRegisterPlugins is a central place for plugins to check
	// whether they should auto-register. Normally they should.
	AutoRegisterPlugins = true
	// EarlyLogger is the logger used for pre-config logging. You can
	// substitute it if your use case requires.
	EarlyLogger = log.New(os.Stderr, "GO2CHEF ", log.LstdFlags)
)
View Source
var (
	// ErrConfigHasNoNameKey is thrown when a config block doesn't have a `name` key
	ErrConfigHasNoNameKey = errors.New("config map has no `name` key")
	// ErrConfigHasNoTypeKey is thrown when a config block doesn't have a `type` key
	ErrConfigHasNoTypeKey = errors.New("config map has no `type` key")
)
View Source
var GlobalConfiguration = plugconf.NewPlugConf()

Functions

func GetName

func GetName(config map[string]interface{}) (string, error)

GetName gets the name attribute from a config map

func GetNameType

func GetNameType(config map[string]interface{}) (string, string, error)

GetNameType gets the name and type attributes from a config map

func GetType

func GetType(config map[string]interface{}) (string, error)

GetType gets the type attribute from a config map

func InitGlobalLogger

func InitGlobalLogger(loggers []Logger)

InitGlobalLogger initializes the global logger

func InitializeConfigSourceFlags

func InitializeConfigSourceFlags(set *pflag.FlagSet)

InitializeConfigSourceFlags initializes a flag set with the flags required by the currently registered configuration source plugins.

func LoadGlobalConfiguration

func LoadGlobalConfiguration(config map[string]interface{}) error

func LogLevelToString

func LogLevelToString(l int) (string, error)

LogLevelToString translates a log level value to a string

func PathExists

func PathExists(path string) (bool, error)

PathExists returns whether the given file or directory exists

func RegisterConfigSource

func RegisterConfigSource(name string, cs ConfigSource)

RegisterConfigSource registers a new configuration source plugin

func RegisterLogger

func RegisterLogger(name string, l LoggerLoader)

RegisterLogger registers a new logging plugin

func RegisterSource

func RegisterSource(name string, s SourceLoader)

RegisterSource registers a new source plugin

func RegisterStep

func RegisterStep(name string, s StepLoader)

RegisterStep registers a new step plugin with go2chef

func ShutdownGlobalLogger

func ShutdownGlobalLogger()

ShutdownGlobalLogger shuts down the global logger

func StringToLogLevel

func StringToLogLevel(s string) (int, error)

StringToLogLevel translates a string to a log level

Types

type Component

type Component interface {
	fmt.Stringer
	SetName(string)
	Name() string
	Type() string
}

Component defines the interface for go2chef components (plugins)

type Config

type Config struct {
	Loggers []Logger
	Steps   []Step
}

Config defines the configuration for all of go2chef

func GetConfig

func GetConfig(configSourceName string, earlyLogger Logger) (*Config, error)

GetConfig loads and resolves the configuration

type ConfigSource

type ConfigSource interface {
	// InitFlags sets up command line flags
	InitFlags(set *pflag.FlagSet)
	// ReadConfig actually reads in the configuration
	ReadConfig() (map[string]interface{}, error)
}

ConfigSource defines the interface for configuration sources. These can be implemented however you like.

func GetConfigSource

func GetConfigSource(name string) ConfigSource

GetConfigSource gets a specified configuration source plugin

type ErrChefAlreadyInstalled

type ErrChefAlreadyInstalled struct {
	Installed string
	Requested string
}

ErrChefAlreadyInstalled represents errors where Chef is already installed and provides a mechanism to pass metadata regarding the installed and requested versions back up the error chain.

func (*ErrChefAlreadyInstalled) Error

func (e *ErrChefAlreadyInstalled) Error() string

Error returns the error string

type ErrComponentDoesNotExist

type ErrComponentDoesNotExist struct {
	Component string
}

ErrComponentDoesNotExist represents errors where a requested component (plugin) hasn't been registered.

func (*ErrComponentDoesNotExist) Error

func (e *ErrComponentDoesNotExist) Error() string

Error returns the error string

type Event

type Event struct {
	Event       string
	Component   string
	Message     string
	ExtraFields *ExtraLoggingFields
}

Event provides a more structured way to log information from go2chef plugins.

func NewEvent

func NewEvent(event, component, message string) *Event

NewEvent returns a new event using the provided parameters

func NewEventWithExtraFields

func NewEventWithExtraFields(event, component, message string, extrafields *ExtraLoggingFields) *Event

NewEvent returns a new event using the provided parameters

type ExtraLoggingFields

type ExtraLoggingFields struct {
	StepName    string
	StepType    string
	StepCount   int
	ElapsedTime int
}

type Logger

type Logger interface {
	Component

	SetLevel(lvl int)
	SetDebug(dbg int)
	Debugf(dbg int, fmt string, args ...interface{})
	Infof(fmt string, args ...interface{})
	Errorf(fmt string, args ...interface{})

	// WriteEvent writes an event object to this logger
	WriteEvent(e *Event)
	// Shutdown allows go2chef to wait for loggers to finish writes
	// if necessary (i.e. to remote endpoints)
	Shutdown()
}

Logger defines the interface for logging components.

func GetGlobalLogger

func GetGlobalLogger() Logger

GetGlobalLogger gets an instance of the global logger

func GetLogger

func GetLogger(name string, config map[string]interface{}) (Logger, error)

GetLogger gets a new instance of the Logger type specified by `name` and returns it configured as with config map[string]interface{}

func GetLoggers

func GetLoggers(config map[string]interface{}) ([]Logger, error)

GetLoggers extracts an array of loggers from a config map

type LoggerLoader

type LoggerLoader func(map[string]interface{}) (Logger, error)

LoggerLoader defines the call signature for functions which return fully configured Logger instances

type MultiLogger

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

MultiLogger is a fan-out logger for use as the central logging broker in go2chef

func NewMultiLogger

func NewMultiLogger(loggers []Logger) *MultiLogger

NewMultiLogger returns a MultiLogger with the provided list of loggers set up to receive logs.

func (*MultiLogger) Debugf

func (m *MultiLogger) Debugf(dbg int, s string, v ...interface{})

Debugf logs a formatted message at DEBUG level

func (*MultiLogger) Errorf

func (m *MultiLogger) Errorf(s string, v ...interface{})

Errorf logs a formatted message at ERROR level

func (*MultiLogger) Infof

func (m *MultiLogger) Infof(s string, v ...interface{})

Infof logs a formatted message at INFO level

func (*MultiLogger) Name

func (m *MultiLogger) Name() string

Name returns the name of this logger

func (*MultiLogger) SetDebug

func (m *MultiLogger) SetDebug(d int)

SetDebug sets the logger's debug level threshold

func (*MultiLogger) SetLevel

func (m *MultiLogger) SetLevel(l int)

SetLevel sets the logger's overall level threshold

func (*MultiLogger) SetName

func (m *MultiLogger) SetName(string)

SetName is a no-op for this logger

func (*MultiLogger) Shutdown

func (m *MultiLogger) Shutdown()

Shutdown shuts down all loggers on this MultiLogger

func (*MultiLogger) String

func (m *MultiLogger) String() string

func (*MultiLogger) Type

func (m *MultiLogger) Type() string

Type returns the type of this logger

func (*MultiLogger) WriteEvent

func (m *MultiLogger) WriteEvent(e *Event)

WriteEvent writes an event to all loggers on this MultiLogger

type Source

type Source interface {
	Component
	DownloadToPath(path string) error
}

Source defines the interface for source download components

func GetSource

func GetSource(name string, config map[string]interface{}) (Source, error)

GetSource gets the specified source plugin configured with the provided config map

func GetSourceFromStepConfig

func GetSourceFromStepConfig(config map[string]interface{}) (Source, error)

GetSourceFromStepConfig gets a Source from a Step's config map. If there is no `source` key, then it will return a nil Source and no error.

type SourceLoader

type SourceLoader func(map[string]interface{}) (Source, error)

SourceLoader represents factory functions for Sources

type Step

type Step interface {
	Component
	Download() error
	Execute() error
}

Step defines the interface for go2chef execution steps

func GetStep

func GetStep(stepType string, config map[string]interface{}) (Step, error)

GetStep gets a new step given a type and configuration

func GetSteps

func GetSteps(config map[string]interface{}) ([]Step, error)

GetSteps extracts an array of steps from a config map

type StepLoader

type StepLoader func(map[string]interface{}) (Step, error)

StepLoader defines the function call interface for step loaders

Jump to

Keyboard shortcuts

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