Phase 0 : client API PVE et modèle d'objets génériques
This commit is contained in:
1 parent
5f4b3ab930
commit
69c340e4c3
3 files changed
+480
No files matched your search
@@ -0,0 +1,127 @@
|
||||
// Package model holds the generic, normalized representation of synchronized objects.
|
||||
// Field values are canonical strings or sorted string lists, so that two clusters
|
||||
// can be compared field by field and the result serialized to YAML without loss.
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Kind identifies an object type handled by an adapter.
|
||||
type Kind string
|
||||
|
||||
const (
|
||||
KindDatacenter Kind = "datacenter-options"
|
||||
KindZone Kind = "sdn-zone"
|
||||
KindVNet Kind = "sdn-vnet"
|
||||
KindSubnet Kind = "sdn-subnet"
|
||||
)
|
||||
|
||||
// Kinds lists the kinds in dependency order (creation order).
|
||||
var Kinds = []Kind{KindDatacenter, KindZone, KindVNet, KindSubnet}
|
||||
|
||||
// Object is one configuration object of a cluster.
|
||||
// Field values are either string or []string (see Normalize).
|
||||
type Object struct {
|
||||
Kind Kind `yaml:"kind" json:"kind"`
|
||||
ID string `yaml:"id" json:"id"`
|
||||
Fields map[string]any `yaml:"fields" json:"fields"`
|
||||
}
|
||||
|
||||
// Key is the unique identity of an object inside a cluster.
|
||||
func (o Object) Key() string { return string(o.Kind) + "/" + o.ID }
|
||||
|
||||
// Snapshot is the exported state of one cluster (YAML, versionable in Git).
|
||||
type Snapshot struct {
|
||||
Cluster string `yaml:"cluster"`
|
||||
PVEVersion string `yaml:"pve_version,omitempty"`
|
||||
ExportedAt time.Time `yaml:"exported_at"`
|
||||
Objects []Object `yaml:"objects"`
|
||||
Warnings []string `yaml:"warnings,omitempty"`
|
||||
}
|
||||
|
||||
// Sort orders objects by kind (dependency order) then by id.
|
||||
func Sort(objs []Object) {
|
||||
rank := map[Kind]int{}
|
||||
for i, k := range Kinds {
|
||||
rank[k] = i
|
||||
}
|
||||
sort.SliceStable(objs, func(i, j int) bool {
|
||||
if objs[i].Kind != objs[j].Kind {
|
||||
return rank[objs[i].Kind] < rank[objs[j].Kind]
|
||||
}
|
||||
return objs[i].ID < objs[j].ID
|
||||
})
|
||||
}
|
||||
|
||||
// Normalize turns a YAML/JSON decoded field map into canonical values:
|
||||
// scalars become strings, lists become sorted []string.
|
||||
func Normalize(fields map[string]any) map[string]any {
|
||||
out := make(map[string]any, len(fields))
|
||||
for k, v := range fields {
|
||||
switch t := v.(type) {
|
||||
case []any:
|
||||
l := make([]string, 0, len(t))
|
||||
for _, e := range t {
|
||||
l = append(l, scalar(e))
|
||||
}
|
||||
sort.Strings(l)
|
||||
out[k] = l
|
||||
case []string:
|
||||
l := append([]string(nil), t...)
|
||||
sort.Strings(l)
|
||||
out[k] = l
|
||||
default:
|
||||
out[k] = scalar(v)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func scalar(v any) string {
|
||||
switch t := v.(type) {
|
||||
case nil:
|
||||
return ""
|
||||
case string:
|
||||
return t
|
||||
case bool:
|
||||
if t {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
default:
|
||||
return fmt.Sprint(t)
|
||||
}
|
||||
}
|
||||
|
||||
// Equal compares two canonical values.
|
||||
func Equal(a, b any) bool {
|
||||
la, oka := a.([]string)
|
||||
lb, okb := b.([]string)
|
||||
if oka || okb {
|
||||
if !oka || !okb || len(la) != len(lb) {
|
||||
return false
|
||||
}
|
||||
for i := range la {
|
||||
if la[i] != lb[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return a == b
|
||||
}
|
||||
|
||||
// Format renders a canonical value for humans.
|
||||
func Format(v any) string {
|
||||
if l, ok := v.([]string); ok {
|
||||
return "[" + strings.Join(l, ", ") + "]"
|
||||
}
|
||||
if v == nil {
|
||||
return "<absent>"
|
||||
}
|
||||
return fmt.Sprintf("%q", v)
|
||||
}
|
||||
Reference in new issue
Block a user