The controller plans config, templates and moved providers' provisions, hands bus accounts to the vault and judges the new manifest words, on the feature's SDK and node-engine validator (feature 57, config and provisioning).
127 lines
5.3 KiB
Go
127 lines
5.3 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// A machine's backup lines are placement (design 57 L3, issue 794): the holder of node-backup names them as
|
|
// `${backup:lines}` and `${backup:data}`, which a handed holder has its client fill from the plan and an unhanded
|
|
// one has the controller fill as it fills `${contribution:node-backup:backup}` and `${...:data}`: the same text.
|
|
|
|
const (
|
|
oldBackupText = "# lines\n${contribution:node-backup:backup}"
|
|
oldDataText = "# data\n${contribution:node-backup:data}"
|
|
placedBackupTxt = "# lines\n${backup:lines}"
|
|
placedDataTxt = "# data\n${backup:data}"
|
|
backupHolder = `{"module":"restic","version":"1","moved":true,"claims":[{"name":"node-backup","scope":"node"}],
|
|
"build":{"artifacts":[{"name":"files","kind":"archive","from":"files"}]},
|
|
"resources":[{"id":"declared","type":"file","path":"/etc/backups.conf","template":"files/backups"},
|
|
{"id":"data-declared","type":"file","path":"/etc/data.conf","template":"files/data"}]}`
|
|
)
|
|
|
|
func dataModule(name string) string {
|
|
return `{"module":"` + name + `","version":"1","resources":[{"id":"d","type":"directory","mode":"0700"}],
|
|
"data":{"own":[{"id":"d","path":"${dir:d}","class":"irreplaceable","why":"the ` + name + ` data"}]}}`
|
|
}
|
|
|
|
// backupsOn is a machine of a holder whose files say these texts and the modules that declare data.
|
|
func backupsOn(t *testing.T, lines, data string, modules ...string) Resolution {
|
|
t.Helper()
|
|
all := []Manifest{held(t, backupHolder, builtWith("restic", map[string]string{"backups": lines, "data": data}))}
|
|
names := []string{"restic"}
|
|
for _, name := range modules {
|
|
all = append(all, mustParse(t, dataModule(name)))
|
|
names = append(names, name)
|
|
}
|
|
r, err := Resolve(shelf(all...), names, workstation(), World{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r.Account = "op"
|
|
return r
|
|
}
|
|
|
|
var backupModules = []string{"alpha", "beta", "gamma"}
|
|
|
|
// gammaLeftOut is a machine whose gamma does not place: its lines are kept, best effort.
|
|
func gammaLeftOut(with Rendering) Rendering {
|
|
with.NotPlaced = map[string]string{"gamma": "its placement setting does not read"}
|
|
return with
|
|
}
|
|
|
|
func TestTheBackupPlacementIsTheTextTheOldPlaceholdersRender(t *testing.T) {
|
|
// The old form is an unmoved holder: its placeholders are the controller's alone.
|
|
oldHolder := strings.Replace(backupHolder, `"moved":true,`, "", 1)
|
|
all := []Manifest{held(t, oldHolder, builtWith("restic", map[string]string{"backups": oldBackupText, "data": oldDataText}))}
|
|
for _, name := range backupModules {
|
|
all = append(all, mustParse(t, dataModule(name)))
|
|
}
|
|
old, err := Resolve(shelf(all...), append([]string{"restic"}, backupModules...), workstation(), World{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
old.Account = "op"
|
|
wantLines, wantData := fileOf(t, old, gammaLeftOut(Rendering{}), "restic.declared"), fileOf(t, old, gammaLeftOut(Rendering{}), "restic.data-declared")
|
|
if !strings.Contains(wantLines, "# alpha\npath ") || !strings.Contains(wantLines, "# gamma\n") || !strings.Contains(wantData, "item d") {
|
|
t.Fatalf("the old form renders\n%s\n%s", wantLines, wantData)
|
|
}
|
|
moved := backupsOn(t, placedBackupTxt, placedDataTxt, backupModules...)
|
|
// Not handed: the controller fills the new placeholders with the old text.
|
|
if got := fileOf(t, moved, gammaLeftOut(Rendering{}), "restic.declared"); got != wantLines {
|
|
t.Errorf("not handed, the lines are\n%s\nwant\n%s", got, wantLines)
|
|
}
|
|
if got := fileOf(t, moved, gammaLeftOut(Rendering{}), "restic.data-declared"); got != wantData {
|
|
t.Errorf("not handed, the data is\n%s\nwant\n%s", got, wantData)
|
|
}
|
|
// Handed: the plan carries the same text as placement, and the files are the client's.
|
|
c, _ := composeWith(t, moved, gammaLeftOut(handingTo("store:5000")))
|
|
p := c.Plans["restic"].Placement
|
|
if got := "# lines\n" + p["backup:lines"]; got != wantLines {
|
|
t.Errorf("handed, the plan's lines are\n%s\nwant\n%s", got, wantLines)
|
|
}
|
|
if got := "# data\n" + p["backup:data"]; got != wantData {
|
|
t.Errorf("handed, the plan's data is\n%s\nwant\n%s", got, wantData)
|
|
}
|
|
if len(p) != 2 {
|
|
t.Errorf("the plan's placement is %v", p)
|
|
}
|
|
}
|
|
|
|
func TestAModuleAddedOrTakenAwayChangesTheBackupPlan(t *testing.T) {
|
|
plan := func(modules ...string) map[string]string {
|
|
c, _ := composeWith(t, backupsOn(t, placedBackupTxt, placedDataTxt, modules...), handingTo("store:5000"))
|
|
return c.Plans["restic"].Placement
|
|
}
|
|
two, three := plan("alpha", "beta"), plan("alpha", "beta", "gamma")
|
|
if reflect.DeepEqual(two, three) || strings.Contains(two["backup:lines"], "gamma") || !strings.Contains(three["backup:lines"], "# gamma\n") {
|
|
t.Errorf("two modules give %v, three give %v", two, three)
|
|
}
|
|
}
|
|
|
|
func TestAnUnknownBackupKeyIsRefused(t *testing.T) {
|
|
r := backupsOn(t, "${backup:rows}", placedDataTxt, backupModules...)
|
|
if _, err := r.Compose(handingTo("store:5000")); err == nil || !strings.Contains(err.Error(), "backup:rows") {
|
|
t.Errorf("handed, an unknown key is %v", err)
|
|
}
|
|
if _, err := r.Declaration(Rendering{}); err == nil || !strings.Contains(err.Error(), "backup:rows") {
|
|
t.Errorf("not handed, an unknown key is %v", err)
|
|
}
|
|
}
|
|
|
|
func fileOf(t *testing.T, r Resolution, with Rendering, id string) string {
|
|
t.Helper()
|
|
out, err := r.Declaration(with)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, res := range out {
|
|
if res["id"] == id {
|
|
return res["content"].(string)
|
|
}
|
|
}
|
|
t.Fatalf("%s was not composed", id)
|
|
return ""
|
|
}
|