-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
109 lines (90 loc) · 2.79 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package main
import (
"encoding/json"
"encoding/xml"
"fmt"
"log"
"os"
"slices"
"github.com/spf13/pflag"
"libvirt.org/go/libvirtxml"
vmSchema "kubevirt.io/api/core/v1"
)
const (
bootAnnotation = "os.vm.kubevirt.io/boot"
)
type BootConfig struct {
BootDevices []DomainBootDevice `json:"boot"`
BootMenu *DomainBootMenu `json:"bootmenu,omitempty"`
}
// https://libvirt.org/formatdomain.html#operating-system-booting
var validateDevs = []string{"fd", "hd", "cdrom", "network"}
type DomainBootDevice struct {
Dev string `json:"dev"`
}
type DomainBootMenu struct {
Enable string `json:"enable"`
Timeout string `json:"timeout"` // milliseconds
}
func onDefineDomain(vmiJSON, domainXML []byte) (string, error) {
vmiSpec := vmSchema.VirtualMachineInstance{}
if err := json.Unmarshal(vmiJSON, &vmiSpec); err != nil {
return "", fmt.Errorf("Failed to unmarshal given VMI spec: %s %s", err, string(vmiJSON))
}
domainSpec := libvirtxml.Domain{}
if err := xml.Unmarshal(domainXML, &domainSpec); err != nil {
return "", fmt.Errorf("Failed to unmarshal given Domain spec: %s %s", err, string(domainXML))
}
annotations := vmiSpec.GetAnnotations()
bootConfigAnnotation, found := annotations[bootAnnotation]
if !found {
return string(domainXML), nil
}
var bootConfig BootConfig
if err := json.Unmarshal([]byte(bootConfigAnnotation), &bootConfig); err != nil {
return "", fmt.Errorf("Failed to unmarshal given bootAnnotation value: %s %s", err, bootConfigAnnotation)
}
if domainSpec.OS == nil {
domainSpec.OS = &libvirtxml.DomainOS{}
}
if len(bootConfig.BootDevices) > 0 {
bootDevices := make([]libvirtxml.DomainBootDevice, 0)
for _, dev := range bootConfig.BootDevices {
if !slices.Contains(validateDevs, dev.Dev) {
continue
}
bootDevices = append(bootDevices, libvirtxml.DomainBootDevice{
Dev: dev.Dev,
})
}
domainSpec.OS.BootDevices = bootDevices
}
if bootConfig.BootMenu != nil {
domainSpec.OS.BootMenu = &libvirtxml.DomainBootMenu{
Enable: bootConfig.BootMenu.Enable,
Timeout: bootConfig.BootMenu.Timeout,
}
}
newDomainXML, err := xml.Marshal(domainSpec)
if err != nil {
return "", fmt.Errorf("Failed to marshal new Domain spec: %s %+v", err, domainSpec)
}
return string(newDomainXML), nil
}
func main() {
var vmiJSON, domainXML string
pflag.StringVar(&vmiJSON, "vmi", "", "VMI to change in JSON format")
pflag.StringVar(&domainXML, "domain", "", "Domain spec in XML format")
pflag.Parse()
logger := log.New(os.Stderr, "boot", log.Ldate)
if vmiJSON == "" || domainXML == "" {
logger.Printf("Bad input vmi=%d, domain=%d", len(vmiJSON), len(domainXML))
os.Exit(1)
}
domainXML, err := onDefineDomain([]byte(vmiJSON), []byte(domainXML))
if err != nil {
logger.Printf("onDefineDomain failed: %s", err)
panic(err)
}
fmt.Println(domainXML)
}