From 890022ae6409cdb992adbc103f9922175f8c3c86 Mon Sep 17 00:00:00 2001 From: Tom Wiesing Date: Wed, 30 Nov 2022 11:08:46 +0100 Subject: [PATCH] internal: Annotate all components with groups This commit ensures that the compiler has to check every component against the groups they implement by explicitly annotating the appropriate interfaces. --- internal/dis/component.go | 6 ++ internal/dis/component/control/control.go | 4 ++ internal/dis/component/control/home/home.go | 4 ++ internal/dis/component/control/info/info.go | 5 ++ .../dis/component/control/static/static.go | 4 ++ .../component/exporter/extras_bookkeeping.go | 4 ++ .../dis/component/exporter/extras_config.go | 4 ++ .../component/exporter/extras_filesystem.go | 4 ++ .../component/exporter/extras_pathbuilders.go | 4 ++ internal/dis/component/resolver/resolver.go | 4 ++ internal/dis/component/solr/solr.go | 4 ++ internal/dis/component/sql/sql.go | 6 ++ internal/dis/component/ssh2/ssh2.go | 4 ++ .../dis/component/triplestore/triplestore.go | 6 ++ internal/dis/component/web/web.go | 4 ++ .../wisski/ingredient/barrel/drush/cron.go | 4 ++ .../wisski/ingredient/barrel/drush/update.go | 4 ++ internal/wisski/ingredient/barrel/running.go | 4 ++ internal/wisski/ingredient/barrel/ssh/ssh.go | 4 ++ internal/wisski/ingredient/info/info.go | 4 ++ internal/wisski/ingredient/info/snapshots.go | 4 ++ internal/wisski/ingredient/locker/lock.go | 4 ++ .../ingredient/php/extras/pathbuilder.go | 4 ++ .../wisski/ingredient/php/extras/prefixes.go | 4 ++ .../wisski/ingredient/php/extras/stats.go | 4 ++ internal/wisski/ingredient/php/users/users.go | 4 ++ internal/wisski/ingredients.go | 1 + pkg/lazy/pool.go | 23 ++++--- pkg/lazy/pool_anal.go | 61 +++++++++++-------- 29 files changed, 163 insertions(+), 33 deletions(-) diff --git a/internal/dis/component.go b/internal/dis/component.go index a7ad522..71c0f57 100644 --- a/internal/dis/component.go +++ b/internal/dis/component.go @@ -13,6 +13,12 @@ import ( func (dis *Distillery) init() { dis.poolInit.Do(func() { dis.pool.Init = component.Init + lazy.RegisterPoolGroup[component.Backupable](&dis.pool) + lazy.RegisterPoolGroup[component.Snapshotable](&dis.pool) + lazy.RegisterPoolGroup[component.DistilleryFetcher](&dis.pool) + lazy.RegisterPoolGroup[component.Installable](&dis.pool) + lazy.RegisterPoolGroup[component.Provisionable](&dis.pool) + lazy.RegisterPoolGroup[component.Servable](&dis.pool) }) } diff --git a/internal/dis/component/control/control.go b/internal/dis/component/control/control.go index 7215a39..da81c89 100644 --- a/internal/dis/component/control/control.go +++ b/internal/dis/component/control/control.go @@ -16,6 +16,10 @@ type Control struct { Servables []component.Servable } +var ( + _ component.Installable = (*Control)(nil) +) + func (control Control) Path() string { return filepath.Join(control.Still.Config.DeployRoot, "core", "dis") } diff --git a/internal/dis/component/control/home/home.go b/internal/dis/component/control/home/home.go index b338488..6d1698e 100644 --- a/internal/dis/component/control/home/home.go +++ b/internal/dis/component/control/home/home.go @@ -24,6 +24,10 @@ type Home struct { homeBytes lazy.Lazy[[]byte] } +var ( + _ component.Servable = (*Home)(nil) +) + func (*Home) Routes() []string { return []string{"/"} } func (home *Home) Handler(ctx context.Context, route string, io stream.IOStream) (http.Handler, error) { diff --git a/internal/dis/component/control/info/info.go b/internal/dis/component/control/info/info.go index 4d242d9..9c4169f 100644 --- a/internal/dis/component/control/info/info.go +++ b/internal/dis/component/control/info/info.go @@ -26,6 +26,11 @@ type Info struct { SnapshotsLog *logger.Logger } +var ( + _ component.DistilleryFetcher = (*Info)(nil) + _ component.Servable = (*Info)(nil) +) + func (*Info) Routes() []string { return []string{"/dis/"} } func (info *Info) Handler(ctx context.Context, route string, io stream.IOStream) (handler http.Handler, err error) { diff --git a/internal/dis/component/control/static/static.go b/internal/dis/component/control/static/static.go index 6899ca1..bc043d3 100644 --- a/internal/dis/component/control/static/static.go +++ b/internal/dis/component/control/static/static.go @@ -15,6 +15,10 @@ type Static struct { component.Base } +var ( + _ component.Servable = (*Static)(nil) +) + func (*Static) Routes() []string { return []string{"/static/"} } //go:embed dist diff --git a/internal/dis/component/exporter/extras_bookkeeping.go b/internal/dis/component/exporter/extras_bookkeeping.go index 7f792bc..660dd41 100644 --- a/internal/dis/component/exporter/extras_bookkeeping.go +++ b/internal/dis/component/exporter/extras_bookkeeping.go @@ -13,6 +13,10 @@ type Bookkeeping struct { component.Base } +var ( + _ component.Snapshotable = (*Bookkeeping)(nil) +) + // SnapshotNeedsRunning returns if this Snapshotable requires a running instance. func (Bookkeeping) SnapshotNeedsRunning() bool { return false } diff --git a/internal/dis/component/exporter/extras_config.go b/internal/dis/component/exporter/extras_config.go index 4387da1..a8930e4 100644 --- a/internal/dis/component/exporter/extras_config.go +++ b/internal/dis/component/exporter/extras_config.go @@ -12,6 +12,10 @@ type Config struct { component.Base } +var ( + _ = (component.Backupable)((*Config)(nil)) +) + func (*Config) BackupName() string { return "config" } diff --git a/internal/dis/component/exporter/extras_filesystem.go b/internal/dis/component/exporter/extras_filesystem.go index e91f0c4..5a0fd85 100644 --- a/internal/dis/component/exporter/extras_filesystem.go +++ b/internal/dis/component/exporter/extras_filesystem.go @@ -10,6 +10,10 @@ type Filesystem struct { component.Base } +var ( + _ component.Snapshotable = (*Filesystem)(nil) +) + // SnapshotNeedsRunning returns if this Snapshotable requires a running instance. func (Filesystem) SnapshotNeedsRunning() bool { return false } diff --git a/internal/dis/component/exporter/extras_pathbuilders.go b/internal/dis/component/exporter/extras_pathbuilders.go index 315ae85..26591a8 100644 --- a/internal/dis/component/exporter/extras_pathbuilders.go +++ b/internal/dis/component/exporter/extras_pathbuilders.go @@ -14,6 +14,10 @@ type Pathbuilders struct { Instances *instances.Instances } +var ( + _ component.Snapshotable = (*Pathbuilders)(nil) +) + func (Pathbuilders) SnapshotNeedsRunning() bool { return true } func (Pathbuilders) SnapshotName() string { return "pathbuilders" } diff --git a/internal/dis/component/resolver/resolver.go b/internal/dis/component/resolver/resolver.go index e6cccd9..45a6a84 100644 --- a/internal/dis/component/resolver/resolver.go +++ b/internal/dis/component/resolver/resolver.go @@ -26,6 +26,10 @@ type Resolver struct { handler lazy.Lazy[wdresolve.ResolveHandler] // handler } +var ( + _ component.Servable = (*Resolver)(nil) +) + func (resolver *Resolver) Routes() []string { return []string{"/go/", "/wisski/get/"} } func (resolver *Resolver) Handler(ctx context.Context, route string, io stream.IOStream) (http.Handler, error) { diff --git a/internal/dis/component/solr/solr.go b/internal/dis/component/solr/solr.go index 2c23c59..4a670e1 100644 --- a/internal/dis/component/solr/solr.go +++ b/internal/dis/component/solr/solr.go @@ -17,6 +17,10 @@ type Solr struct { PollInterval time.Duration // duration to wait for during wait } +var ( + _ component.Installable = (*Solr)(nil) +) + func (s *Solr) Path() string { return filepath.Join(s.Still.Config.DeployRoot, "core", "solr") } diff --git a/internal/dis/component/sql/sql.go b/internal/dis/component/sql/sql.go index e50e958..ad772e9 100644 --- a/internal/dis/component/sql/sql.go +++ b/internal/dis/component/sql/sql.go @@ -20,6 +20,12 @@ type SQL struct { lazyNetwork lazy.Lazy[string] } +var ( + _ component.Backupable = (*SQL)(nil) + _ component.Snapshotable = (*SQL)(nil) + _ component.Installable = (*SQL)(nil) +) + func (sql *SQL) Path() string { return filepath.Join(sql.Still.Config.DeployRoot, "core", "sql") } diff --git a/internal/dis/component/ssh2/ssh2.go b/internal/dis/component/ssh2/ssh2.go index c8b0d4a..4eefcb8 100644 --- a/internal/dis/component/ssh2/ssh2.go +++ b/internal/dis/component/ssh2/ssh2.go @@ -14,6 +14,10 @@ type SSH2 struct { Instances *instances.Instances } +var ( + _ component.Installable = (*SSH2)(nil) +) + // GlobalKeys returns the global authorized keys func (s *SSH2) GlobalKeys() ([]ssh.PublicKey, error) { file, err := s.Environment.Open(s.Config.GlobalAuthorizedKeysFile) diff --git a/internal/dis/component/triplestore/triplestore.go b/internal/dis/component/triplestore/triplestore.go index 8631a4f..78f1d74 100644 --- a/internal/dis/component/triplestore/triplestore.go +++ b/internal/dis/component/triplestore/triplestore.go @@ -17,6 +17,12 @@ type Triplestore struct { PollInterval time.Duration // duration to wait for during wait } +var ( + _ component.Backupable = (*Triplestore)(nil) + _ component.Snapshotable = (*Triplestore)(nil) + _ component.Installable = (*Triplestore)(nil) +) + func (ts *Triplestore) Path() string { return filepath.Join(ts.Still.Config.DeployRoot, "core", "triplestore") } diff --git a/internal/dis/component/web/web.go b/internal/dis/component/web/web.go index fdaef07..d234a2e 100644 --- a/internal/dis/component/web/web.go +++ b/internal/dis/component/web/web.go @@ -15,6 +15,10 @@ type Web struct { component.Base } +var ( + _ component.Installable = (*Web)(nil) +) + func (web *Web) Path() string { return filepath.Join(web.Still.Config.DeployRoot, "core", "web") } diff --git a/internal/wisski/ingredient/barrel/drush/cron.go b/internal/wisski/ingredient/barrel/drush/cron.go index 4a5c572..8e28fb1 100644 --- a/internal/wisski/ingredient/barrel/drush/cron.go +++ b/internal/wisski/ingredient/barrel/drush/cron.go @@ -45,6 +45,10 @@ type LastCronFetcher struct { Drush *Drush } +var ( + _ ingredient.WissKIFetcher = (*LastCronFetcher)(nil) +) + func (lbr *LastCronFetcher) Fetch(flags ingredient.FetcherFlags, info *status.WissKI) (err error) { if flags.Quick { return diff --git a/internal/wisski/ingredient/barrel/drush/update.go b/internal/wisski/ingredient/barrel/drush/update.go index 7766c64..1f771fd 100644 --- a/internal/wisski/ingredient/barrel/drush/update.go +++ b/internal/wisski/ingredient/barrel/drush/update.go @@ -56,6 +56,10 @@ type LastUpdateFetcher struct { Drush *Drush } +var ( + _ ingredient.WissKIFetcher = (*LastUpdateFetcher)(nil) +) + func (lbr *LastUpdateFetcher) Fetch(flags ingredient.FetcherFlags, info *status.WissKI) (err error) { info.LastUpdate, err = lbr.Drush.LastUpdate(flags.Context) return diff --git a/internal/wisski/ingredient/barrel/running.go b/internal/wisski/ingredient/barrel/running.go index 2454ab9..8100fa7 100644 --- a/internal/wisski/ingredient/barrel/running.go +++ b/internal/wisski/ingredient/barrel/running.go @@ -23,6 +23,10 @@ type RunningFetcher struct { Barrel *Barrel } +var ( + _ ingredient.WissKIFetcher = (*RunningFetcher)(nil) +) + func (rf *RunningFetcher) Fetch(flags ingredient.FetcherFlags, info *status.WissKI) (err error) { info.Running, err = rf.Barrel.Running(flags.Context) return diff --git a/internal/wisski/ingredient/barrel/ssh/ssh.go b/internal/wisski/ingredient/barrel/ssh/ssh.go index 1770865..ead9e0e 100644 --- a/internal/wisski/ingredient/barrel/ssh/ssh.go +++ b/internal/wisski/ingredient/barrel/ssh/ssh.go @@ -17,6 +17,10 @@ type SSH struct { Barrel *barrel.Barrel } +var ( + _ ingredient.WissKIFetcher = (*SSH)(nil) +) + func (ssh *SSH) Keys() ([]ssh.PublicKey, error) { file, err := ssh.Environment.Open(ssh.Barrel.AuthorizedKeysPath()) if environment.IsNotExist(err) { diff --git a/internal/wisski/ingredient/info/info.go b/internal/wisski/ingredient/info/info.go index 5383ce1..e182171 100644 --- a/internal/wisski/ingredient/info/info.go +++ b/internal/wisski/ingredient/info/info.go @@ -20,6 +20,10 @@ type Info struct { Analytics *lazy.PoolAnalytics } +var ( + _ ingredient.WissKIFetcher = (*Info)(nil) +) + // Information fetches information about this WissKI. // TODO: Rework this to be able to determine what kind of information is available. func (wisski *Info) Information(ctx context.Context, quick bool) (info status.WissKI, err error) { diff --git a/internal/wisski/ingredient/info/snapshots.go b/internal/wisski/ingredient/info/snapshots.go index 5b1806d..3e18015 100644 --- a/internal/wisski/ingredient/info/snapshots.go +++ b/internal/wisski/ingredient/info/snapshots.go @@ -11,6 +11,10 @@ type SnapshotsFetcher struct { Info *Info } +var ( + _ ingredient.WissKIFetcher = (*SnapshotsFetcher)(nil) +) + func (lbr *SnapshotsFetcher) Fetch(flags ingredient.FetcherFlags, info *status.WissKI) (err error) { if flags.Quick { return diff --git a/internal/wisski/ingredient/locker/lock.go b/internal/wisski/ingredient/locker/lock.go index 46ca1b3..cf38515 100644 --- a/internal/wisski/ingredient/locker/lock.go +++ b/internal/wisski/ingredient/locker/lock.go @@ -13,6 +13,10 @@ type Locker struct { ingredient.Base } +var ( + _ = (ingredient.WissKIFetcher)((*Locker)(nil)) +) + var Locked = exit.Error{ Message: "WissKI Instance is locked for administrative operations", ExitCode: exit.ExitGeneric, diff --git a/internal/wisski/ingredient/php/extras/pathbuilder.go b/internal/wisski/ingredient/php/extras/pathbuilder.go index 823d444..9137de8 100644 --- a/internal/wisski/ingredient/php/extras/pathbuilder.go +++ b/internal/wisski/ingredient/php/extras/pathbuilder.go @@ -17,6 +17,10 @@ type Pathbuilder struct { PHP *php.PHP } +var ( + _ ingredient.WissKIFetcher = (*Pathbuilder)(nil) +) + //go:embed pathbuilder.php var pathbuilderPHP string diff --git a/internal/wisski/ingredient/php/extras/prefixes.go b/internal/wisski/ingredient/php/extras/prefixes.go index 4e7c4ef..afbab98 100644 --- a/internal/wisski/ingredient/php/extras/prefixes.go +++ b/internal/wisski/ingredient/php/extras/prefixes.go @@ -25,6 +25,10 @@ type Prefixes struct { MStore *mstore.MStore } +var ( + _ ingredient.WissKIFetcher = (*Prefixes)(nil) +) + // NoPrefix checks if this WissKI instance is excluded from generating prefixes. // TODO: Move this to the database! func (prefixes *Prefixes) NoPrefix() bool { diff --git a/internal/wisski/ingredient/php/extras/stats.go b/internal/wisski/ingredient/php/extras/stats.go index 208aa52..f23e5bf 100644 --- a/internal/wisski/ingredient/php/extras/stats.go +++ b/internal/wisski/ingredient/php/extras/stats.go @@ -16,6 +16,10 @@ type Stats struct { PHP *php.PHP } +var ( + _ ingredient.WissKIFetcher = (*Stats)(nil) +) + //go:embed stats.php var statsPHP string diff --git a/internal/wisski/ingredient/php/users/users.go b/internal/wisski/ingredient/php/users/users.go index b73d75c..6ec2853 100644 --- a/internal/wisski/ingredient/php/users/users.go +++ b/internal/wisski/ingredient/php/users/users.go @@ -18,6 +18,10 @@ type Users struct { PHP *php.PHP } +var ( + _ ingredient.WissKIFetcher = (*Users)(nil) +) + //go:embed users.php var usersPHP string diff --git a/internal/wisski/ingredients.go b/internal/wisski/ingredients.go index fa06748..fe45b7e 100644 --- a/internal/wisski/ingredients.go +++ b/internal/wisski/ingredients.go @@ -14,6 +14,7 @@ import ( func (wisski *WissKI) init() { wisski.poolInit.Do(func() { wisski.pool.Init = ingredient.Init + lazy.RegisterPoolGroup[ingredient.WissKIFetcher](&wisski.pool) }) } diff --git a/pkg/lazy/pool.go b/pkg/lazy/pool.go index 97740e2..d49aa3e 100644 --- a/pkg/lazy/pool.go +++ b/pkg/lazy/pool.go @@ -2,6 +2,8 @@ package lazy import ( "reflect" + + "github.com/tkw1536/goprogram/lib/reflectx" ) // Pool represents a pool of laziliy initialized and potentially referencing Component instances. @@ -18,12 +20,23 @@ type Pool[Component any, InitParams any] struct { // Init is called on every component to be initialized. Init func(Component, InitParams) Component - // Analytics are written on the first retrieval operation on this Pool - Analytics PoolAnalytics + // Analytics are written on the first retrieval operation on this Pool. + // + // Contains all groups and structs that are referenced during initialization. + // To add extra groups, call RegisterPoolGroup. + Analytics PoolAnalytics + extraGroups []reflect.Type all Lazy[[]Component] } +// RegisterPoolGroup registers the given group type to be added to the pools' analytics. +// +// Only groups not referenced during initialization need to be registered explicitly. +func RegisterPoolGroup[Group any, Component any, InitParams any](p *Pool[Component, InitParams]) { + p.extraGroups = append(p.extraGroups, reflectx.TypeOf[Group]()) +} + // All initializes or returns all components stored in this pool. // // The All function should return an array of calls to [Make] with the provided context. @@ -48,7 +61,7 @@ func (p *Pool[Component, InitParams]) All(Params InitParams, All func(context *P context.process(all) // write out analytics - context.anal(&p.Analytics) + context.anal(&p.Analytics, p.extraGroups) return all }) } @@ -58,10 +71,6 @@ type PoolContext[Component any] struct { init func(Component) Component // initializes a new component all func(context *PoolContext[Component]) []Component // initializes all components - // cache for metas - - // function to return all components - metaCache map[reflect.Type]meta[Component] cache map[string]Component // cached components queue []delayedInit[Component] // init queue diff --git a/pkg/lazy/pool_anal.go b/pkg/lazy/pool_anal.go index 806994d..a2e4f03 100644 --- a/pkg/lazy/pool_anal.go +++ b/pkg/lazy/pool_anal.go @@ -29,7 +29,7 @@ type PoolAnalyticsGroup struct { } // anal writes analytics about this context to anal -func (context *PoolContext[Component]) anal(anal *PoolAnalytics) { +func (context *PoolContext[Component]) anal(anal *PoolAnalytics, groups []reflect.Type) { anal.Components = make(map[string]*PoolAnalyticsComponent, len(context.metaCache)) anal.Groups = make(map[string]*PoolAnalyticsGroup) @@ -51,6 +51,10 @@ func (context *PoolContext[Component]) anal(anal *PoolAnalytics) { } } + // collect interfaces to analyze + ifaces := make([]reflect.Type, len(groups)) + copy(ifaces, groups) + // take all of the components out of the cache for _, meta := range context.metaCache { anal.Components[meta.Name].Type = meta.Name @@ -59,34 +63,39 @@ func (context *PoolContext[Component]) anal(anal *PoolAnalytics) { }) anal.Components[meta.Name].IFields = collection.MapValues(meta.IFields, func(key string, iface reflect.Type) string { - name := nameOf(iface) - - if _, ok := anal.Groups[name]; !ok { - types := collection.FilterClone(tpPointers, func(tp reflect.Type) bool { - return tp.AssignableTo(iface) - }) - - anal.Groups[name] = &PoolAnalyticsGroup{ - Type: name, - Components: collection.MapSlice(types, func(tp reflect.Type) string { - cname := nameOf(tp.Elem()) - anal.Components[cname].Groups = append(anal.Components[cname].Groups, name) - return cname - }), - } - - mcount := iface.NumMethod() - anal.Groups[name].Methods = make(map[string]string, mcount) - for i := 0; i < mcount; i++ { - method := iface.Method(i) - anal.Groups[name].Methods[method.Name] = method.Type.String() - } - } - - return name + ifaces = append(ifaces, iface) + return nameOf(iface) }) } + // and analyze all ifaces + for _, iface := range ifaces { + name := nameOf(iface) + if _, ok := anal.Groups[name]; ok { + continue + } + + types := collection.FilterClone(tpPointers, func(tp reflect.Type) bool { + return tp.AssignableTo(iface) + }) + + anal.Groups[name] = &PoolAnalyticsGroup{ + Type: name, + Components: collection.MapSlice(types, func(tp reflect.Type) string { + cname := nameOf(tp.Elem()) + anal.Components[cname].Groups = append(anal.Components[cname].Groups, name) + return cname + }), + } + + mcount := iface.NumMethod() + anal.Groups[name].Methods = make(map[string]string, mcount) + for i := 0; i < mcount; i++ { + method := iface.Method(i) + anal.Groups[name].Methods[method.Name] = method.Type.String() + } + } + for _, comp := range anal.Components { slices.Sort(comp.Groups) }