func (d *Daemon) APIExtensions() []string {
	return d.apiExtensions
}
APIExtensions returns the extensions available to the current daemon
package daemon
import (
	"context"
	"crypto/x509"
	"database/sql/driver"
	"fmt"
	"net/http"
	"os/user"
	"path/filepath"
	"time"
	"github.com/pborman/uuid"
	dqlite "github.com/CanonicalLtd/go-dqlite"
	raftmembership "github.com/CanonicalLtd/raft-membership"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/pkg/errors"
	"github.com/spoke-d/task"
	"github.com/spoke-d/thermionic/internal/cert"
	"github.com/spoke-d/thermionic/internal/clock"
	"github.com/spoke-d/thermionic/internal/cluster"
	clusterevents "github.com/spoke-d/thermionic/internal/cluster/events"
	"github.com/spoke-d/thermionic/internal/cluster/heartbeat"
	"github.com/spoke-d/thermionic/internal/cluster/membership"
	"github.com/spoke-d/thermionic/internal/cluster/upgraded"
	"github.com/spoke-d/thermionic/internal/config"
	"github.com/spoke-d/thermionic/internal/db"
	querycluster "github.com/spoke-d/thermionic/internal/db/cluster"
	"github.com/spoke-d/thermionic/internal/db/database"
	"github.com/spoke-d/thermionic/internal/discovery"
	"github.com/spoke-d/thermionic/internal/endpoints"
	"github.com/spoke-d/thermionic/internal/fsys"
	"github.com/spoke-d/thermionic/internal/node"
	"github.com/spoke-d/thermionic/internal/operations"
	"github.com/spoke-d/thermionic/internal/retrier"
	"github.com/spoke-d/thermionic/internal/schedules"
	"github.com/spoke-d/thermionic/internal/state"
	"github.com/spoke-d/thermionic/pkg/api"
)
type Endpoints interface {
	Up() error
	Down() error
	NetworkAddress() string
	NetworkPrivateKey() []byte
	NetworkCert() *cert.Info
	NetworkPublicKey() []byte
	NetworkUpdateAddress(string) error
	PprofUpdateAddress(string) error
	NetworkUpdateCert(*cert.Info) error
}
type OS interface {
	Init(fsys.FileSystem) error
	LocalDatabasePath() string
	GlobalDatabaseDir() string
	GlobalDatabasePath() string
	LocalNoncePath() string
	VarDir() string
	Hostname() (string, error)
	HostNames() ([]string, error)
	User() (*user.User, error)
}
type Node interface {
	database.DBAccessor
	db.NodeOpener
	db.NodeTransactioner
	Dir() string
	Close() error
}
type Cluster interface {
	database.DBAccessor
	db.ClusterOpener
	db.ClusterTransactioner
	db.ClusterExclusiveLocker
	NodeID(int64)
	SchemaVersion() int
	Close() error
}
type Gateway interface {
	Init(*cert.Info) error
	RaftNodes() ([]db.RaftNode, error)
	Raft() RaftInstance
	DB() Node
	IsDatabaseNode() bool
	Cert() *cert.Info
	LeaderAddress() (string, error)
	Reset(cert *cert.Info) error
	Clustered() bool
	WaitLeadership() error
	WaitUpgradeNotification()
	HandlerFuncs() map[string]http.HandlerFunc
	ServerStore() querycluster.ServerStore
	DialFunc() dqlite.DialFunc
	Context() context.Context
	Kill()
	Shutdown() error
}
type RaftInstance interface {
	MembershipChanger() raftmembership.Changer
}
type Actor interface {
	ID() string
	Types() []string
	Write([]byte) error
	Close()
	NoForward() bool
	Done() bool
}
type ActorGroup interface {
	Add(a Actor)
	Prune() bool
	Walk(func(Actor) error) error
}
type Service interface {
	Get(Daemon, *http.Request) Response
	Put(Daemon, *http.Request) Response
	Post(Daemon, *http.Request) Response
	Delete(Daemon, *http.Request) Response
	Patch(Daemon, *http.Request) Response
	Name() string
}
type Operations interface {
	Add(*operations.Operation) error
	GetOpByPartialID(string) (operations.Op, error)
	DeleteOp(string) error
	WaitOp(string, time.Duration) (bool, error)
	Walk(func(operations.Op) error) error
}
type Schedules interface {
	Add(*schedules.Task) error
	Remove(string) error
	GetTsk(string) (schedules.Tsk, error)
	Walk(func(schedules.Tsk) error) error
}
type SchedulerTask interface {
	Run() (task.Func, task.Schedule)
}
type Response interface {
	Render(http.ResponseWriter) error
}
type EventBroadcaster interface {
	Dispatch(map[string]interface{}) error
}
type Daemon struct {
	clientCerts                           []x509.Certificate
	clusterConfigSchema, nodeConfigSchema config.Schema
	version                               string
	nonce                                 string
	discoverable                          bool
	apiMetrics                            APIMetrics
	apiExtensions                         []string
	db                                    Node
	cluster                               Cluster
	gateway                               Gateway
	operations                            Operations
	schedules                             Schedules
	os                                    OS
	networkAddress string
	debugAddress   string
	tasks *task.Group
	setupChan    chan struct{}
	registerChan chan struct{}
	shutdownChan chan struct{}
	endpoints        Endpoints
	actorGroup       ActorGroup
	eventBroadcaster EventBroadcaster
	fileSystem fsys.FileSystem
	logger     log.Logger
	sleeper    clock.Sleeper
	raftLatency float64
	apiServices         []api.Service
	apiInternalServices []api.Service
}
func New(
	clientCerts []x509.Certificate,
	version string,
	networkAddress, debugAddress string,
	clusterConfigSchema, nodeConfigSchema config.Schema,
	apiExtensions []string,
	apiMetrics APIMetrics,
	apiServices, apiInternalServices []api.Service,
	actorGroup ActorGroup,
	eventBroadcaster EventBroadcaster,
	options ...Option,
) *Daemon {
	opts := newOptions()
	for _, option := range options {
		option(opts)
	}
	return &Daemon{
		clientCerts:         clientCerts,
		version:             version,
		networkAddress:      networkAddress,
		debugAddress:        debugAddress,
		apiMetrics:          apiMetrics,
		apiExtensions:       apiExtensions,
		clusterConfigSchema: clusterConfigSchema,
		nodeConfigSchema:    nodeConfigSchema,
		tasks:               task.NewGroup(),
		setupChan:           make(chan struct{}),
		registerChan:        make(chan struct{}),
		shutdownChan:        make(chan struct{}),
		apiServices:         apiServices,
		apiInternalServices: apiInternalServices,
		actorGroup:          actorGroup,
		eventBroadcaster:    eventBroadcaster,
		os:                  opts.os,
		fileSystem:          opts.fileSystem,
		logger:              opts.logger,
		sleeper:             opts.sleeper,
		raftLatency:         opts.raftLatency,
		discoverable:        opts.discoverable,
	}
}
func (d *Daemon) Init() error {
	if err := d.init(); err != nil {
		level.Error(d.logger).Log("msg", "failed to start the daemon", "err", err)
		d.Stop()
		return errors.WithStack(err)
	}
	return nil
}
func (d *Daemon) Register(tasks []SchedulerTask) error {
	heartbeatTask := heartbeat.New(
		makeHeartbeatGatewayShim(d.gateway),
		d.cluster,
		heartbeat.DatabaseEndpoint,
		heartbeat.WithLogger(log.WithPrefix(d.logger, "component", "heartbeat")),
	)
	d.tasks.Add(heartbeatTask.Run())
	eventsTask := clusterevents.New(
		d.endpoints,
		d.cluster,
		NewEventHook(d.eventBroadcaster, d.logger),
		clusterevents.WithLogger(log.WithPrefix(d.logger, "component", "events")),
	)
	d.tasks.Add(eventsTask.Run())
	operationsTask := operations.New(
		d.cluster,
		operations.WithLogger(log.WithPrefix(d.logger, "component", "operations")),
	)
	d.tasks.Add(operationsTask.Run())
	d.operations = operationsTask
	schedulesTask := schedules.New(
		makeDaemonSchedulesShim(d),
		schedules.WithLogger(log.WithPrefix(d.logger, "component", "schedules")),
	)
	d.tasks.Add(schedulesTask.Run())
	d.schedules = schedulesTask
	if d.discoverable {
		discoveryTask := discovery.New(
			makeDaemonDiscoveryShim(d),
			discovery.WithLogger(log.WithPrefix(d.logger, "component", "discovery")),
		)
		d.tasks.Add(discoveryTask.Run())
	}
	for _, task := range tasks {
		d.tasks.Add(task.Run())
	}
	d.tasks.Start()
	level.Info(d.logger).Log("msg", "registered tasks")
	close(d.registerChan)
	return nil
}
func (d *Daemon) Kill() {
	if d.gateway != nil {
		d.gateway.Kill()
	}
}
func (d *Daemon) Stop() error {
	level.Info(d.logger).Log("msg", "starting shutdown sequence")
	var errs []error
	trackError := func(err error) {
		if err != nil {
			errs = append(errs, err)
		}
	}
	if d.endpoints != nil {
		trackError(d.endpoints.Down())
	}
	trackError(d.tasks.Stop(3 * time.Second))
	if d.cluster != nil {
		level.Info(d.logger).Log("msg", "closing the database")
		if err := d.cluster.Close(); errors.Cause(err) == driver.ErrBadConn {
			level.Debug(d.logger).Log("msg", "could not close remote database", "err", err)
		} else {
			trackError(err)
		}
	}
	if d.db != nil {
		trackError(d.db.Close())
	}
	if d.gateway != nil {
		trackError(d.gateway.Shutdown())
	}
	var err error
	if n := len(errs); n > 0 {
		format := "%v"
		if n > 1 {
			format += fmt.Sprintf(" (and %d more errors)", n)
		}
		err = errors.Errorf(format, errs[0])
	}
	if err != nil {
		level.Error(d.logger).Log("msg", "failed to cleanly shutdown", "err", err)
	}
	return errors.WithStack(err)
}
func (d *Daemon) Gateway() Gateway {
	return d.gateway
}
func (d *Daemon) Cluster() Cluster {
	return d.cluster
}
func (d *Daemon) Node() Node {
	return d.db
}
func (d *Daemon) ClientCerts() []x509.Certificate {
	return d.clientCerts
}
func (d *Daemon) ClusterCerts() []x509.Certificate {
	networkCert := d.endpoints.NetworkCert()
	keyPair := networkCert.KeyPair()
	certs := keyPair.Certificate
	if len(certs) == 0 {
		return make([]x509.Certificate, 0)
	}
	cert, _ := x509.ParseCertificate(certs[0])
	return []x509.Certificate{
		*cert,
	}
}
func (d *Daemon) ClusterConfigSchema() config.Schema {
	return d.clusterConfigSchema
}
func (d *Daemon) NodeConfigSchema() config.Schema {
	return d.nodeConfigSchema
}
func (d *Daemon) ActorGroup() ActorGroup {
	return d.actorGroup
}
func (d *Daemon) EventBroadcaster() EventBroadcaster {
	return d.eventBroadcaster
}
func (d *Daemon) SetupChan() <-chan struct{} {
	return d.setupChan
}
func (d *Daemon) RegisterChan() <-chan struct{} {
	return d.registerChan
}
func (d *Daemon) ShutdownChan() <-chan struct{} {
	return d.shutdownChan
}
func (d *Daemon) State() *state.State {
	return state.NewState(
		state.WithNode(d.db),
		state.WithCluster(d.cluster),
		state.WithOS(d.os),
	)
}
func (d *Daemon) Version() string {
	return d.version
}
func (d *Daemon) Endpoints() Endpoints {
	return d.endpoints
}
func (d *Daemon) APIMetrics() APIMetrics {
	return d.apiMetrics
}