Skip to content
Permalink
Browse files

Add post create hooks

  • Loading branch information...
subuk committed Sep 10, 2019
1 parent 80989c5 commit e3b1010e720ff277edba76c12fca1d1632275e91
Showing with 151 additions and 11 deletions.
  1. +6 −1 bootstrap/bootstrap.go
  2. +16 −2 compute/compute.go
  3. +40 −0 compute/event.go
  4. +15 −8 config/config.go
  5. +68 −0 filesystem/scripted_compute_event_broker.go
  6. +6 −0 sample-subscribe-script.sh
@@ -52,7 +52,12 @@ func Web(configFilename string) {
os.Exit(1)
}
netRepo := libvirt.NewNetworkRepository(connectionPool, cfg.Bridges)
compute := libcompute.New(machineRepo, volumeRepo, volumePoolRepo, hostInfoRepo, keyRepo, netRepo)

epub := filesystem.NewScriptedComputeEventBroker(logger.With().Str("component", "compute-event-broker").Logger())
for _, sub := range cfg.Subscribes {
epub.Subscribe(sub.Event, sub.Script, sub.Mandatory)
}
compute := libcompute.New(epub, machineRepo, volumeRepo, volumePoolRepo, hostInfoRepo, keyRepo, netRepo)

webenv := web.New(cfg, logger, compute)
server := http.Server{
@@ -8,17 +8,27 @@ import (

var ErrArchNotsupported = errors.New("requested arch not supported")

type Event interface {
Name() string
Plain() map[string]string
}

type EventPublisher interface {
Publish(event Event) error
}

type Service struct {
virt VirtualMachineRepository
vol VolumeRepository
volpool VolumePoolRepository
host HostInfoRepository
key KeyRepository
net NetworkRepository
epub EventPublisher
}

func New(virt VirtualMachineRepository, vol VolumeRepository, volpool VolumePoolRepository, host HostInfoRepository, key KeyRepository, net NetworkRepository) *Service {
return &Service{virt: virt, vol: vol, volpool: volpool, host: host, key: key, net: net}
func New(epub EventPublisher, virt VirtualMachineRepository, vol VolumeRepository, volpool VolumePoolRepository, host HostInfoRepository, key KeyRepository, net NetworkRepository) *Service {
return &Service{epub: epub, virt: virt, vol: vol, volpool: volpool, host: host, key: key, net: net}
}

func (service *Service) VirtualMachineList() ([]*VirtualMachine, error) {
@@ -121,6 +131,10 @@ func (service *Service) VirtualMachineCreate(params VirtualMachineCreateParams)
if err != nil {
return nil, util.NewError(err, "cannot create virtual machine")
}
if err := service.epub.Publish(NewEventVirtualMachineCreated(vm)); err != nil {
service.virt.Delete(vm.Id) // Ignore error
return nil, util.NewError(err, "cannot publish event virtual machine created")
}
return vm, nil
}

@@ -0,0 +1,40 @@
package compute

import (
"fmt"
)

type EventVirtualMachineCreated struct {
vm *VirtualMachine
}

func NewEventVirtualMachineCreated(vm *VirtualMachine) *EventVirtualMachineCreated {
return &EventVirtualMachineCreated{vm: vm}
}

func (e *EventVirtualMachineCreated) Name() string {
return "vm_created"
}

func (e *EventVirtualMachineCreated) Plain() map[string]string {
data := map[string]string{
"event": e.Name(),
"vm_id": e.vm.Id,
"vm_cpus": fmt.Sprintf("%d", e.vm.VCpus),
"vm_memory_mib": fmt.Sprintf("%d", e.vm.MemoryMiB()),
"vm_volume_count": fmt.Sprintf("%d", len(e.vm.Volumes)),
"vm_interface_count": fmt.Sprintf("%d", len(e.vm.Interfaces)),
}
for idx, volume := range e.vm.Volumes {
data[fmt.Sprintf("vm_volume_%d_path", idx)] = volume.Path
data[fmt.Sprintf("vm_volume_%d_format", idx)] = volume.Format.String()
data[fmt.Sprintf("vm_volume_%d_device", idx)] = volume.Device.String()
data[fmt.Sprintf("vm_volume_%d_type", idx)] = volume.Type.String()
}
for idx, iface := range e.vm.Interfaces {
data[fmt.Sprintf("vm_interface_%d_mac", idx)] = iface.Mac
data[fmt.Sprintf("vm_interface_%d_network", idx)] = iface.Network
data[fmt.Sprintf("vm_interface_%d_type", idx)] = iface.Type.String()
}
return data
}
@@ -35,15 +35,22 @@ type ImageConfig struct {
OsArch string `hcl:"os_arch"`
}

type SubscribeConfig struct {
Event string `hcl:",key"`
Script string `hcl:"script"`
Mandatory bool `hcl:"mandatory"`
}

type Config struct {
LibvirtUri string `hcl:"libvirt_uri"`
LibvirtConfigDriveSuffix string `hcl:"libvirt_config_drive_suffix"`
LibvirtConfigDrivePool string `hcl:"libvirt_config_drive_pool"`
LibvirtConfigDriveWriteFormat string `hcl:"libvirt_config_drive_write_format"`
Images []ImageConfig `hcl:"image"`
Bridges []string `hcl:"bridges"`
KeyFile string `hcl:"key_file"`
Web WebConfig `hcl:"web"`
LibvirtUri string `hcl:"libvirt_uri"`
LibvirtConfigDriveSuffix string `hcl:"libvirt_config_drive_suffix"`
LibvirtConfigDrivePool string `hcl:"libvirt_config_drive_pool"`
LibvirtConfigDriveWriteFormat string `hcl:"libvirt_config_drive_write_format"`
Images []ImageConfig `hcl:"image"`
Bridges []string `hcl:"bridges"`
KeyFile string `hcl:"key_file"`
Web WebConfig `hcl:"web"`
Subscribes []SubscribeConfig `hcl:"subscribe"`
}

func Default() *Config {
@@ -0,0 +1,68 @@
package filesystem

import (
"os"
"os/exec"
"strings"
"subuk/vmango/compute"
"subuk/vmango/util"

"github.com/rs/zerolog"
)

type scriptedComputeEventBrokerSubscribtion struct {
Event string
Script string
Mandatory bool
}

type ScriptedComputeEventBroker struct {
logger zerolog.Logger
subs []scriptedComputeEventBrokerSubscribtion
}

func NewScriptedComputeEventBroker(logger zerolog.Logger) *ScriptedComputeEventBroker {
return &ScriptedComputeEventBroker{
logger: logger,
subs: []scriptedComputeEventBrokerSubscribtion{},
}
}

func (epub *ScriptedComputeEventBroker) Subscribe(event, script string, mandatory bool) {
epub.subs = append(epub.subs, scriptedComputeEventBrokerSubscribtion{
Event: event,
Script: script,
Mandatory: mandatory,
})
}

func (epub *ScriptedComputeEventBroker) Publish(event compute.Event) error {
for _, sub := range epub.subs {
if sub.Event != event.Name() {
continue
}
cmd := exec.Command("sh", "-c", sub.Script)
env := os.Environ()
for key, value := range event.Plain() {
env = append(env, "VMANGO_"+strings.ToUpper(key)+"="+value)
}
cmd.Env = env
epub.logger.Info().
Str("script", sub.Script).
Str("event", event.Name()).
Msg("running script")

out, err := cmd.CombinedOutput()
if err != nil {
if sub.Mandatory {
return util.NewError(err, "cannot run mandatory script: %s", strings.TrimSpace(string(out)))
}
epub.logger.Warn().Err(err).
Str("out", string(out)).
Str("script", sub.Script).
Str("event", event.Name()).
Msg("cannot run script")
}
}
return nil
}
@@ -0,0 +1,6 @@
#!/bin/sh

echo ARGS: $@
echo "VM ${VMANGO_VM_ID} has been created with root volume ${VMANGO_VM_VOLUME_0_PATH}"
echo "Env:"
env | grep VMANGO_

0 comments on commit e3b1010

Please sign in to comment.
You can’t perform that action at this time.