Refactor components
This commit is contained in:
parent
17011a277f
commit
487ce09979
7 changed files with 117 additions and 63 deletions
|
|
@ -34,6 +34,10 @@ type Component interface {
|
||||||
// Context returns a new InstallationContext to be used during installation from the command line.
|
// Context returns a new InstallationContext to be used during installation from the command line.
|
||||||
// Typically this should just pass through the parent, but might perform other tasks.
|
// Typically this should just pass through the parent, but might perform other tasks.
|
||||||
Context(parent InstallationContext) InstallationContext
|
Context(parent InstallationContext) InstallationContext
|
||||||
|
|
||||||
|
// Base() returns a reference to a base component
|
||||||
|
// This is implemented by an embedding on ComponentBase
|
||||||
|
Base() *ComponentBase
|
||||||
}
|
}
|
||||||
|
|
||||||
// ComponentBase implements base functionality for a component
|
// ComponentBase implements base functionality for a component
|
||||||
|
|
@ -43,6 +47,11 @@ type ComponentBase struct {
|
||||||
Config *config.Config // Config is the configuration of the underlying distillery
|
Config *config.Config // Config is the configuration of the underlying distillery
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Base returns a reference to the ComponentBase
|
||||||
|
func (cb *ComponentBase) Base() *ComponentBase {
|
||||||
|
return cb
|
||||||
|
}
|
||||||
|
|
||||||
// Path returns the path to this component
|
// Path returns the path to this component
|
||||||
func (cb ComponentBase) Path() string {
|
func (cb ComponentBase) Path() string {
|
||||||
return cb.Dir
|
return cb.Dir
|
||||||
|
|
|
||||||
134
env/component.go
vendored
134
env/component.go
vendored
|
|
@ -2,6 +2,8 @@ package env
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/component"
|
"github.com/FAU-CDI/wisski-distillery/component"
|
||||||
|
|
@ -14,9 +16,63 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/component/web"
|
"github.com/FAU-CDI/wisski-distillery/component/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Stacks returns the Stacks of this distillery
|
// components holds the various components of the distillery
|
||||||
|
// It is inlined into the [Distillery] struct, and initialized using [makeComponent].
|
||||||
|
type components struct {
|
||||||
|
// m protects the fields below
|
||||||
|
m sync.Mutex
|
||||||
|
|
||||||
|
// each component is only initialized once
|
||||||
|
web *web.Web
|
||||||
|
self *self.Self
|
||||||
|
resolver *resolver.Resolver
|
||||||
|
dis *dis.Dis
|
||||||
|
ssh *ssh.SSH
|
||||||
|
ts *triplestore.Triplestore
|
||||||
|
sql *sql.SQL
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeComponent makes or returns a component inside the [component] struct of the distillery
|
||||||
|
//
|
||||||
|
// C is the type of component to initialize. It must be backed by a pointer, or makeComponent will panic.
|
||||||
|
//
|
||||||
|
// dis is the distillery to initialize components for
|
||||||
|
// field is a pointer to the appropriate struct field within the distillery components
|
||||||
|
// init is called with a new non-nil component to initialize it. It may be nil, to indicate no initialization is required.
|
||||||
|
//
|
||||||
|
// makeComponent returns the new or existing component instance
|
||||||
|
func makeComponent[C component.Component](dis *Distillery, field *C, init func(C)) C {
|
||||||
|
dis.components.m.Lock()
|
||||||
|
defer dis.components.m.Unlock()
|
||||||
|
|
||||||
|
// get the typeof C and make sure that it is a pointer type!
|
||||||
|
typC := reflect.TypeOf((*C)(nil)).Elem()
|
||||||
|
if typC.Kind() != reflect.Pointer {
|
||||||
|
panic("makeComponent: C must be backed by a pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the component is non-nil, then it has already been initialized
|
||||||
|
if !reflect.ValueOf(*field).IsNil() {
|
||||||
|
return *field
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new element, and call the initializer (if requested)
|
||||||
|
*field = reflect.New(typC.Elem()).Interface().(C)
|
||||||
|
if init != nil {
|
||||||
|
init(*field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the base configuration
|
||||||
|
base := (*field).Base()
|
||||||
|
base.Config = dis.Config
|
||||||
|
base.Dir = filepath.Join(dis.Config.DeployRoot, "core", (*field).Name())
|
||||||
|
|
||||||
|
// and eventually return it
|
||||||
|
return *field
|
||||||
|
}
|
||||||
|
|
||||||
|
// Components returns all components of the distillery
|
||||||
func (dis *Distillery) Components() []component.Component {
|
func (dis *Distillery) Components() []component.Component {
|
||||||
// TODO: Do we want to cache these components?
|
|
||||||
return []component.Component{
|
return []component.Component{
|
||||||
dis.Web(),
|
dis.Web(),
|
||||||
dis.Self(),
|
dis.Self(),
|
||||||
|
|
@ -28,63 +84,43 @@ func (dis *Distillery) Components() []component.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Web returns the web component belonging to this distillery
|
func (dis *Distillery) Web() *web.Web {
|
||||||
func (dis *Distillery) Web() (web web.Web) {
|
return makeComponent(dis, &dis.components.web, nil)
|
||||||
dis.makeComponent(web, &web.ComponentBase)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Self returns the self component belonging to this distillery
|
func (dis *Distillery) Self() *self.Self {
|
||||||
func (dis *Distillery) Self() (self self.Self) {
|
return makeComponent(dis, &dis.components.self, nil)
|
||||||
dis.makeComponent(self, &self.ComponentBase)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolver returns the resolver component belonging to this distillery
|
func (dis *Distillery) Resolver() *resolver.Resolver {
|
||||||
func (dis *Distillery) Resolver() (resolver resolver.Resolver) {
|
return makeComponent(dis, &dis.components.resolver, func(resolver *resolver.Resolver) {
|
||||||
resolver.ConfigName = "prefix.cfg" // TODO: Move into core?
|
resolver.ConfigName = "prefix.cfg" // TODO: Move into core?
|
||||||
resolver.Executable = dis.CurrentExecutable()
|
resolver.Executable = dis.CurrentExecutable()
|
||||||
|
})
|
||||||
dis.makeComponent(resolver, &resolver.ComponentBase)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dis returns the dis component belonging to this distillery
|
func (d *Distillery) Dis() *dis.Dis {
|
||||||
func (dis *Distillery) Dis() (ddis dis.Dis) {
|
return makeComponent(d, &d.components.dis, func(ddis *dis.Dis) {
|
||||||
ddis.Executable = dis.CurrentExecutable()
|
ddis.Executable = d.CurrentExecutable()
|
||||||
|
})
|
||||||
dis.makeComponent(ddis, &ddis.ComponentBase)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSH returns the SSH component belonging to this distillery
|
func (dis *Distillery) SSH() *ssh.SSH {
|
||||||
func (dis *Distillery) SSH() (ssh ssh.SSH) {
|
return makeComponent(dis, &dis.components.ssh, nil)
|
||||||
dis.makeComponent(ssh, &ssh.ComponentBase)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SQL returns the SQL component belonging to this distillery
|
func (dis *Distillery) SQL() *sql.SQL {
|
||||||
func (dis *Distillery) SQL() (sql sql.SQL) {
|
return makeComponent(dis, &dis.components.sql, func(sql *sql.SQL) {
|
||||||
sql.ServerURL = dis.Upstream.SQL
|
sql.ServerURL = dis.Upstream.SQL
|
||||||
sql.PollContext = dis.Context()
|
sql.PollContext = dis.Context()
|
||||||
sql.PollInterval = time.Second
|
sql.PollInterval = time.Second
|
||||||
|
})
|
||||||
dis.makeComponent(sql, &sql.ComponentBase)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Triplestore returns the TriplestoreComponent belonging to this distillery
|
func (dis *Distillery) Triplestore() *triplestore.Triplestore {
|
||||||
func (dis *Distillery) Triplestore() (ts triplestore.Triplestore) {
|
return makeComponent(dis, &dis.components.ts, func(ts *triplestore.Triplestore) {
|
||||||
ts.BaseURL = "http://" + dis.Upstream.Triplestore
|
ts.BaseURL = "http://" + dis.Upstream.Triplestore
|
||||||
ts.PollContext = dis.Context()
|
ts.PollContext = dis.Context()
|
||||||
ts.PollInterval = time.Second
|
ts.PollInterval = time.Second
|
||||||
|
})
|
||||||
dis.makeComponent(ts, &ts.ComponentBase)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeComponent updates the baseComponent belonging to component
|
|
||||||
func (dis *Distillery) makeComponent(component component.Component, base *component.ComponentBase) {
|
|
||||||
base.Config = dis.Config
|
|
||||||
base.Dir = filepath.Join(dis.Config.DeployRoot, "core", component.Name())
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
env/distillery.go
vendored
19
env/distillery.go
vendored
|
|
@ -10,10 +10,21 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/fsx"
|
"github.com/FAU-CDI/wisski-distillery/internal/fsx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Distillery represents a running instance for the distillery
|
// Distillery represents an interface to the running distillery.
|
||||||
type Distillery struct {
|
type Distillery struct {
|
||||||
Config *config.Config
|
// Config holds the configuration of the distillery.
|
||||||
Upstream Upstream // TODO: not sure this belongs here
|
// It is read directly from a configuration file.
|
||||||
|
Config *config.Config
|
||||||
|
|
||||||
|
// Upstream holds information to connect to the various running
|
||||||
|
// distillery components.
|
||||||
|
//
|
||||||
|
// NOTE(twiesing): This is intended to eventually allow full remote management of the distillery.
|
||||||
|
// But for now this will just hold upstream configuration.
|
||||||
|
Upstream Upstream
|
||||||
|
|
||||||
|
// components hold references to the various components of the distillery.
|
||||||
|
components
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upstream are the upstream urls connecting to the various external components.
|
// Upstream are the upstream urls connecting to the various external components.
|
||||||
|
|
@ -23,7 +34,7 @@ type Upstream struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context returns a new Context belonging to this distillery
|
// Context returns a new Context belonging to this distillery
|
||||||
func (dis Distillery) Context() context.Context {
|
func (dis *Distillery) Context() context.Context {
|
||||||
return context.Background()
|
return context.Background()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
env/runtime.go
vendored
2
env/runtime.go
vendored
|
|
@ -3,6 +3,6 @@ package env
|
||||||
import "path/filepath"
|
import "path/filepath"
|
||||||
|
|
||||||
// RuntimeDir returns the path to the runtime directory
|
// RuntimeDir returns the path to the runtime directory
|
||||||
func (dis Distillery) RuntimeDir() string {
|
func (dis *Distillery) RuntimeDir() string {
|
||||||
return filepath.Join(dis.Config.DeployRoot, "runtime")
|
return filepath.Join(dis.Config.DeployRoot, "runtime")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
env/snapshot.go
vendored
12
env/snapshot.go
vendored
|
|
@ -16,25 +16,25 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// SnapshotsDir returns the path that contains all snapshot related data.
|
// SnapshotsDir returns the path that contains all snapshot related data.
|
||||||
func (dis Distillery) SnapshotsDir() string {
|
func (dis *Distillery) SnapshotsDir() string {
|
||||||
return filepath.Join(dis.Config.DeployRoot, "snapshots")
|
return filepath.Join(dis.Config.DeployRoot, "snapshots")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SnapshotsStagingPath returns the path to the directory containing a temporary staging area for snapshots.
|
// SnapshotsStagingPath returns the path to the directory containing a temporary staging area for snapshots.
|
||||||
// Use NewSnapshotStagingDir to generate a new staging area.
|
// Use NewSnapshotStagingDir to generate a new staging area.
|
||||||
func (dis Distillery) SnapshotsStagingPath() string {
|
func (dis *Distillery) SnapshotsStagingPath() string {
|
||||||
return filepath.Join(dis.SnapshotsDir(), "staging")
|
return filepath.Join(dis.SnapshotsDir(), "staging")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SnapshotsArchivePath returns the path to the directory containing all exported archives.
|
// SnapshotsArchivePath returns the path to the directory containing all exported archives.
|
||||||
// Use NewSnapshotArchivePath to generate a path to a new archive in this directory.
|
// Use NewSnapshotArchivePath to generate a path to a new archive in this directory.
|
||||||
func (dis Distillery) SnapshotsArchivePath() string {
|
func (dis *Distillery) SnapshotsArchivePath() string {
|
||||||
return filepath.Join(dis.SnapshotsDir(), "archives")
|
return filepath.Join(dis.SnapshotsDir(), "archives")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSnapshotArchivePath returns the path to a new archive with the provided prefix.
|
// NewSnapshotArchivePath returns the path to a new archive with the provided prefix.
|
||||||
// The path is guaranteed to not exist.
|
// The path is guaranteed to not exist.
|
||||||
func (dis Distillery) NewSnapshotArchivePath(prefix string) (path string) {
|
func (dis *Distillery) NewSnapshotArchivePath(prefix string) (path string) {
|
||||||
// TODO: Consider moving these into a subdirectory with the provided prefix.
|
// TODO: Consider moving these into a subdirectory with the provided prefix.
|
||||||
for path == "" || fsx.Exists(path) {
|
for path == "" || fsx.Exists(path) {
|
||||||
name := dis.newSnapshotName(prefix) + ".tar.gz"
|
name := dis.newSnapshotName(prefix) + ".tar.gz"
|
||||||
|
|
@ -45,7 +45,7 @@ func (dis Distillery) NewSnapshotArchivePath(prefix string) (path string) {
|
||||||
|
|
||||||
// newSnapshot name returns a new basename for a snapshot with the provided prefix.
|
// newSnapshot name returns a new basename for a snapshot with the provided prefix.
|
||||||
// The name is guaranteed to be unique within this process.
|
// The name is guaranteed to be unique within this process.
|
||||||
func (Distillery) newSnapshotName(prefix string) string {
|
func (*Distillery) newSnapshotName(prefix string) string {
|
||||||
suffix, _ := password.Password(64) // silently ignore any errors!
|
suffix, _ := password.Password(64) // silently ignore any errors!
|
||||||
if prefix == "" {
|
if prefix == "" {
|
||||||
prefix = "backup"
|
prefix = "backup"
|
||||||
|
|
@ -57,7 +57,7 @@ func (Distillery) newSnapshotName(prefix string) string {
|
||||||
|
|
||||||
// NewSnapshotStagingDir returns the path to a new snapshot directory.
|
// NewSnapshotStagingDir returns the path to a new snapshot directory.
|
||||||
// The directory is guaranteed to have been freshly created.
|
// The directory is guaranteed to have been freshly created.
|
||||||
func (dis Distillery) NewSnapshotStagingDir(prefix string) (path string, err error) {
|
func (dis *Distillery) NewSnapshotStagingDir(prefix string) (path string, err error) {
|
||||||
for path == "" || os.IsExist(err) {
|
for path == "" || os.IsExist(err) {
|
||||||
path = filepath.Join(dis.SnapshotsStagingPath(), dis.newSnapshotName(prefix))
|
path = filepath.Join(dis.SnapshotsStagingPath(), dis.newSnapshotName(prefix))
|
||||||
err = os.Mkdir(path, os.ModeDir)
|
err = os.Mkdir(path, os.ModeDir)
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,6 @@ var ErrCopyNoDirectory = errors.New("dst is not a directory")
|
||||||
//
|
//
|
||||||
// onCopy, when not nil, is called for each file or directory being copied.
|
// onCopy, when not nil, is called for each file or directory being copied.
|
||||||
func CopyDirectory(dst, src string, onCopy func(dst, src string)) error {
|
func CopyDirectory(dst, src string, onCopy func(dst, src string)) error {
|
||||||
// TODO: Allow copying in parallel? Maybe with a mutex?
|
|
||||||
|
|
||||||
// sanity checks
|
// sanity checks
|
||||||
if SameFile(src, dst) {
|
if SameFile(src, dst) {
|
||||||
return ErrCopySameFile
|
return ErrCopySameFile
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Package hostname provides hostname
|
// Package hostname provides the hostname.
|
||||||
package hostname
|
package hostname
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue