mirror of
https://github.com/mudler/luet.git
synced 2025-09-17 23:58:48 +00:00
Move bus implementation to a separate repo, hook to events in luet
This commit is contained in:
46
pkg/bus/events.go
Normal file
46
pkg/bus/events.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package bus
|
||||
|
||||
import (
|
||||
"github.com/mudler/go-pluggable"
|
||||
)
|
||||
|
||||
var (
|
||||
// Package events
|
||||
|
||||
// EventPackageInstall is the event fired when a new package is being installed
|
||||
EventPackageInstall pluggable.EventType = "package.install"
|
||||
// EventPackageUnInstall is the event fired when a new package is being uninstalled
|
||||
EventPackageUnInstall pluggable.EventType = "package.uninstall"
|
||||
|
||||
// Package build
|
||||
|
||||
// EventPackagePreBuild is the event fired before a package is being built
|
||||
EventPackagePreBuild pluggable.EventType = "package.pre.build"
|
||||
// EventPackagePreBuildArtifact is the event fired before a package artifact is being built
|
||||
EventPackagePreBuildArtifact pluggable.EventType = "package.pre.build_artifact"
|
||||
// EventPackagePostBuildArtifact is the event fired after a package artifact was built
|
||||
EventPackagePostBuildArtifact pluggable.EventType = "package.post.build_artifact"
|
||||
// EventPackagePostBuild is the event fired after a package was built
|
||||
EventPackagePostBuild pluggable.EventType = "package.post.build"
|
||||
|
||||
// Repository events
|
||||
|
||||
// EventRepositoryPreBuild is the event fired before a repository is being built
|
||||
EventRepositoryPreBuild pluggable.EventType = "repository.pre.build"
|
||||
// EventRepositoryPostBuild is the event fired after a repository was built
|
||||
EventRepositoryPostBuild pluggable.EventType = "repository.post.build"
|
||||
)
|
||||
|
||||
// Manager is the bus instance manager, which subscribes plugins to events emitted by Luet
|
||||
var Manager *pluggable.Manager = pluggable.NewManager(
|
||||
[]pluggable.EventType{
|
||||
EventPackageInstall,
|
||||
EventPackageUnInstall,
|
||||
EventPackagePreBuild,
|
||||
EventPackagePreBuildArtifact,
|
||||
EventPackagePostBuildArtifact,
|
||||
EventPackagePostBuild,
|
||||
EventRepositoryPreBuild,
|
||||
EventRepositoryPostBuild,
|
||||
},
|
||||
)
|
@@ -34,6 +34,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
bus "github.com/mudler/luet/pkg/bus"
|
||||
. "github.com/mudler/luet/pkg/config"
|
||||
"github.com/mudler/luet/pkg/helpers"
|
||||
. "github.com/mudler/luet/pkg/logger"
|
||||
@@ -170,6 +171,8 @@ func (a *PackageArtifact) WriteYaml(dst string) error {
|
||||
return errors.Wrap(err, "While marshalling for PackageArtifact YAML")
|
||||
}
|
||||
|
||||
bus.Manager.Publish(bus.EventPackagePreBuildArtifact, a)
|
||||
|
||||
mangle, err := NewPackageArtifactFromYaml(data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Generated invalid artifact")
|
||||
@@ -191,6 +194,7 @@ func (a *PackageArtifact) WriteYaml(dst string) error {
|
||||
return errors.Wrap(err, "While writing PackageArtifact YAML")
|
||||
}
|
||||
//a.CompileSpec.GetPackage().SetPath(p)
|
||||
bus.Manager.Publish(bus.EventPackagePostBuildArtifact, a)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -26,6 +26,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
bus "github.com/mudler/luet/pkg/bus"
|
||||
|
||||
"github.com/mudler/luet/pkg/helpers"
|
||||
. "github.com/mudler/luet/pkg/logger"
|
||||
pkg "github.com/mudler/luet/pkg/package"
|
||||
@@ -596,6 +598,14 @@ func (cs *LuetCompiler) compile(concurrency int, keepPermissions bool, p Compila
|
||||
targetAssertion := p.GetSourceAssertion().Search(p.GetPackage().GetFingerPrint())
|
||||
targetPackageHash := cs.ImageRepository + ":" + targetAssertion.Hash.PackageHash
|
||||
|
||||
bus.Manager.Publish(bus.EventPackagePreBuild, struct {
|
||||
CompileSpec CompilationSpec
|
||||
Assert solver.PackageAssert
|
||||
}{
|
||||
CompileSpec: p,
|
||||
Assert: *targetAssertion,
|
||||
})
|
||||
|
||||
// - If image is set we just generate a plain dockerfile
|
||||
// Treat last case (easier) first. The image is provided and we just compute a plain dockerfile with the images listed as above
|
||||
if p.GetImage() != "" {
|
||||
@@ -636,6 +646,14 @@ func (cs *LuetCompiler) compile(concurrency int, keepPermissions bool, p Compila
|
||||
Debug(pkgTag, " :arrow_right_hook: :whale: Builder image from", buildImageHash)
|
||||
Debug(pkgTag, " :arrow_right_hook: :whale: Package image name", currentPackageImageHash)
|
||||
|
||||
bus.Manager.Publish(bus.EventPackagePreBuild, struct {
|
||||
CompileSpec CompilationSpec
|
||||
Assert solver.PackageAssert
|
||||
}{
|
||||
CompileSpec: compileSpec,
|
||||
Assert: assertion,
|
||||
})
|
||||
|
||||
lastHash = currentPackageImageHash
|
||||
if compileSpec.GetImage() != "" {
|
||||
Debug(pkgTag, " :wrench: Compiling "+compileSpec.GetPackage().HumanReadableString()+" from image")
|
||||
@@ -655,6 +673,15 @@ func (cs *LuetCompiler) compile(concurrency int, keepPermissions bool, p Compila
|
||||
// deperrs = append(deperrs, err)
|
||||
// break // stop at first error
|
||||
}
|
||||
|
||||
bus.Manager.Publish(bus.EventPackagePostBuild, struct {
|
||||
CompileSpec CompilationSpec
|
||||
Artifact Artifact
|
||||
}{
|
||||
CompileSpec: compileSpec,
|
||||
Artifact: artifact,
|
||||
})
|
||||
|
||||
departifacts = append(departifacts, artifact)
|
||||
Info(pkgTag, ":white_check_mark: Done")
|
||||
}
|
||||
@@ -673,6 +700,14 @@ func (cs *LuetCompiler) compile(concurrency int, keepPermissions bool, p Compila
|
||||
artifact.SetDependencies(departifacts)
|
||||
artifact.SetSourceAssertion(p.GetSourceAssertion())
|
||||
|
||||
bus.Manager.Publish(bus.EventPackagePostBuild, struct {
|
||||
CompileSpec CompilationSpec
|
||||
Artifact Artifact
|
||||
}{
|
||||
CompileSpec: p,
|
||||
Artifact: artifact,
|
||||
})
|
||||
|
||||
return artifact, err
|
||||
} else {
|
||||
return departifacts[len(departifacts)-1], nil
|
||||
|
@@ -24,6 +24,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mudler/luet/pkg/bus"
|
||||
compiler "github.com/mudler/luet/pkg/compiler"
|
||||
"github.com/mudler/luet/pkg/config"
|
||||
"github.com/mudler/luet/pkg/helpers"
|
||||
@@ -451,6 +452,7 @@ func (l *LuetInstaller) install(syncedRepos Repositories, cp pkg.Packages, s *Sy
|
||||
if err != nil && !l.Options.Force {
|
||||
return errors.Wrap(err, "Failed creating package")
|
||||
}
|
||||
bus.Manager.Publish(bus.EventPackageInstall, c)
|
||||
}
|
||||
var toFinalize []pkg.Package
|
||||
if !l.Options.NoDeps {
|
||||
@@ -655,6 +657,8 @@ func (l *LuetInstaller) uninstall(p pkg.Package, s *System) error {
|
||||
return errors.Wrap(err, "Failed removing package from database")
|
||||
}
|
||||
|
||||
bus.Manager.Publish(bus.EventPackageUnInstall, p)
|
||||
|
||||
Info(":recycle:", p.GetFingerPrint(), "Removed :heavy_check_mark:")
|
||||
return nil
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mudler/luet/pkg/bus"
|
||||
"github.com/mudler/luet/pkg/compiler"
|
||||
"github.com/mudler/luet/pkg/config"
|
||||
"github.com/mudler/luet/pkg/helpers"
|
||||
@@ -426,6 +427,14 @@ func (r *LuetSystemRepository) Write(dst string, resetRevision bool) error {
|
||||
r.Name, r.Revision, r.LastUpdate,
|
||||
))
|
||||
|
||||
bus.Manager.Publish(bus.EventRepositoryPreBuild, struct {
|
||||
Repo LuetSystemRepository
|
||||
Path string
|
||||
}{
|
||||
Repo: *r,
|
||||
Path: dst,
|
||||
})
|
||||
|
||||
// Create tree and repository file
|
||||
archive, err := config.LuetCfg.GetSystem().TempDir("archive")
|
||||
if err != nil {
|
||||
@@ -506,6 +515,14 @@ func (r *LuetSystemRepository) Write(dst string, resetRevision bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
bus.Manager.Publish(bus.EventRepositoryPostBuild, struct {
|
||||
Repo LuetSystemRepository
|
||||
Path string
|
||||
}{
|
||||
Repo: *r,
|
||||
Path: dst,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -1,70 +0,0 @@
|
||||
// Copyright © 2020 Ettore Di Giacinto <mudler@mocaccino.org>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"github.com/chuckpreslar/emission"
|
||||
"github.com/codegangsta/inject"
|
||||
)
|
||||
|
||||
type Bus struct {
|
||||
inject.Injector
|
||||
emission.Emitter
|
||||
}
|
||||
|
||||
func NewBus() *Bus {
|
||||
return &Bus{
|
||||
Injector: inject.New(),
|
||||
Emitter: *emission.NewEmitter(),
|
||||
}
|
||||
}
|
||||
|
||||
// On Binds a callback to an event, mapping the arguments on a global level
|
||||
func (a *Bus) Listen(event EventType, listener interface{}) *Bus {
|
||||
a.On(string(event), func() { a.Invoke(listener) })
|
||||
return a
|
||||
}
|
||||
|
||||
// Emit Emits an event, it does accept only the event as argument, since
|
||||
// the callback will have access to the service mapped by the injector
|
||||
func (a *Bus) Publish(t EventType, e *Event) *Bus {
|
||||
a.Map(e)
|
||||
a.Emit(string(t))
|
||||
return a
|
||||
}
|
||||
|
||||
// Once Binds a callback to an event, mapping the arguments on a global level
|
||||
// It is fired only once.
|
||||
func (a *Bus) OnlyOnce(event EventType, listener interface{}) *Bus {
|
||||
a.Once(string(event), func() { a.Invoke(listener) })
|
||||
return a
|
||||
}
|
||||
|
||||
// EmitSync Emits an event in a syncronized manner,
|
||||
// it does accept only the event as argument, since
|
||||
// the callback will have access to the service mapped by the injector
|
||||
func (a *Bus) EmitSync(event interface{}) *Bus {
|
||||
a.EmitSync(event)
|
||||
return a
|
||||
}
|
||||
|
||||
func (b *Bus) PropagateEvent(p Plugin) func(e *Event) {
|
||||
return func(e *Event) {
|
||||
resp, _ := p.Run(*e)
|
||||
b.Map(&resp)
|
||||
b.Emit(p.Name)
|
||||
}
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
// Copyright © 2020 Ettore Di Giacinto <mudler@mocaccino.org>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package plugin
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
var (
|
||||
PackageInstalled EventType = "package.install"
|
||||
)
|
||||
|
||||
type EventType string
|
||||
|
||||
type Event struct {
|
||||
Name EventType `json:"name"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
type EventResponse struct {
|
||||
State string `json:"state"`
|
||||
Data string `json:"data"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func (e Event) JSON() (string, error) {
|
||||
dat, err := json.Marshal(e)
|
||||
return string(dat), err
|
||||
}
|
||||
|
||||
func (r EventResponse) Unmarshal(i interface{}) error {
|
||||
return json.Unmarshal([]byte(r.Data), i)
|
||||
}
|
||||
|
||||
func (r EventResponse) Errored() bool {
|
||||
return len(r.Error) != 0
|
||||
}
|
||||
|
||||
func NewEvent(name EventType, obj interface{}) (*Event, error) {
|
||||
dat, err := json.Marshal(obj)
|
||||
return &Event{Name: name, Data: string(dat)}, err
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
// Copyright © 2020 Ettore Di Giacinto <mudler@mocaccino.org>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package plugin
|
||||
|
||||
type Manager struct {
|
||||
Plugins []Plugin
|
||||
Events []EventType
|
||||
}
|
||||
|
||||
func (m Manager) Subscribe(b *Bus) {
|
||||
for _, p := range m.Plugins {
|
||||
for _, e := range m.Events {
|
||||
b.Listen(e, b.PropagateEvent(p))
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
// Copyright © 2020 Ettore Di Giacinto <mudler@mocaccino.org>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os/exec"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// External binaries to be hooked on events, with common js input, and common js output
|
||||
type Plugin struct {
|
||||
Name string
|
||||
Executable string
|
||||
}
|
||||
|
||||
func (p Plugin) Run(e Event) (EventResponse, error) {
|
||||
r := EventResponse{}
|
||||
k, err := e.JSON()
|
||||
if err != nil {
|
||||
return r, errors.Wrap(err, "while marshalling evet")
|
||||
}
|
||||
cmd := exec.Command(p.Executable, string(e.Name), k)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
r.Error = err.Error()
|
||||
return r, errors.Wrap(err, "while executing plugin")
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(out, &r); err != nil {
|
||||
r.Error = err.Error()
|
||||
return r, errors.Wrap(err, "while unmarshalling response")
|
||||
}
|
||||
return r, nil
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
// Copyright © 2020 Ettore Di Giacinto <mudler@mocaccino.org>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package plugin_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/mudler/luet/cmd"
|
||||
config "github.com/mudler/luet/pkg/config"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestPlugin(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
LoadConfig(config.LuetCfg)
|
||||
RunSpecs(t, "Plugin Suite")
|
||||
}
|
@@ -1,92 +0,0 @@
|
||||
// Copyright © 2020 Ettore Di Giacinto <mudler@mocaccino.org>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package plugin_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
. "github.com/mudler/luet/pkg/plugin"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Plugin", func() {
|
||||
Context("event subscription", func() {
|
||||
var pluginFile *os.File
|
||||
var err error
|
||||
var b *Bus
|
||||
var m *Manager
|
||||
|
||||
BeforeEach(func() {
|
||||
pluginFile, err = ioutil.TempFile(os.TempDir(), "tests")
|
||||
Expect(err).Should(BeNil())
|
||||
defer os.Remove(pluginFile.Name()) // clean up
|
||||
b = NewBus()
|
||||
m = &Manager{}
|
||||
})
|
||||
|
||||
It("gets the json event name", func() {
|
||||
d1 := []byte("#!/bin/bash\necho \"{ \\\"state\\\": \\\"$1\\\" }\"\n")
|
||||
err := ioutil.WriteFile(pluginFile.Name(), d1, 0550)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
m.Plugins = []Plugin{{Name: "test", Executable: pluginFile.Name()}}
|
||||
m.Events = []EventType{PackageInstalled}
|
||||
m.Subscribe(b)
|
||||
|
||||
ev, err := NewEvent(PackageInstalled, map[string]string{"foo": "bar"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
var received map[string]string
|
||||
var resp *EventResponse
|
||||
b.Listen("test", func(r *EventResponse) {
|
||||
resp = r
|
||||
r.Unmarshal(&received)
|
||||
})
|
||||
|
||||
b.Publish(PackageInstalled, ev)
|
||||
Expect(resp.Errored()).ToNot(BeTrue())
|
||||
Expect(resp.State).Should(Equal(string(PackageInstalled)))
|
||||
})
|
||||
|
||||
It("gets the json event payload", func() {
|
||||
d1 := []byte("#!/bin/bash\necho $2\n")
|
||||
err := ioutil.WriteFile(pluginFile.Name(), d1, 0550)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
m.Plugins = []Plugin{{Name: "test", Executable: pluginFile.Name()}}
|
||||
m.Events = []EventType{PackageInstalled}
|
||||
m.Subscribe(b)
|
||||
|
||||
foo := map[string]string{"foo": "bar"}
|
||||
ev, err := NewEvent(PackageInstalled, foo)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
var received map[string]string
|
||||
var resp *EventResponse
|
||||
b.Listen("test", func(r *EventResponse) {
|
||||
resp = r
|
||||
r.Unmarshal(&received)
|
||||
})
|
||||
|
||||
b.Publish(PackageInstalled, ev)
|
||||
Expect(resp.Errored()).ToNot(BeTrue())
|
||||
Expect(received).Should(Equal(foo))
|
||||
})
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user