package catalogue import ( "fmt" "regexp" "strings" ) // **The data items of a node's modules, placed, read from the composition** (novox/hq ADR 0571 clause 3, ADR 0233 §22 to // §25): disk-load measures each item every module on its node declares, and the node-backup holder, mounts and nfs-server // read the readings. Each item is its module's declaration with its path resolved to this node's place, its protection // and how it is measured: the same lines the node-backup holder is handed as `${backup:data}`, composed by the same // function, so a reader that composes from the bus and a holder handed the file read one list. // PlacedDataItem is one data item a module on a node declares, as placed on that node. type PlacedDataItem struct { Module string `json:"module"` ID string `json:"item"` Class string `json:"class"` // Path is the item's place on the node; CoveredBy the path whose snapshot keeps it (the item, or the item its dump // writes into), empty when it is not backed up. Path string `json:"path"` CoveredBy string `json:"covered_by,omitempty"` // Protection is backup, redundancy, both or none; Measure walk, dataset or shallow. Protection string `json:"protection,omitempty"` Measure string `json:"measure,omitempty"` } var ( dataModuleHeader = regexp.MustCompile(`^#\s*([a-z0-9][a-z0-9-]*)$`) dataSafePath = regexp.MustCompile(`^/[^\s'"\\$` + "`" + `]*$`) ) // ParseDataItems reads the node-backup holder's data text (`${backup:data}`: a `# ` line, then one line per item, // `item `) into each module's items, in order. A line it does // not read is refused, naming the module: a path with a space or a quote is never handed to a measuring shell. func ParseDataItems(text string) (map[string][]PlacedDataItem, error) { out := map[string][]PlacedDataItem{} module := "" for _, raw := range strings.Split(text, "\n") { line := strings.TrimSpace(raw) if line == "" { continue } if m := dataModuleHeader.FindStringSubmatch(line); m != nil { module = m[1] continue } if strings.HasPrefix(line, "#") || module == "" { continue } f := strings.Fields(line) if len(f) < 5 || len(f) > 7 || f[0] != "item" || !dataSafePath.MatchString(f[3]) || (f[4] != "-" && !dataSafePath.MatchString(f[4])) { return nil, fmt.Errorf("%s declares a data line that is not read: %s", module, line) } it := PlacedDataItem{Module: module, ID: f[1], Class: f[2], Path: f[3], Measure: MeasureWalk} if len(f) >= 6 { it.Protection = f[5] } if len(f) == 7 { it.Measure = f[6] } if it.Measure != MeasureWalk && it.Measure != MeasureDataset && it.Measure != MeasureShallow { return nil, fmt.Errorf("%s declares a data line that is not read: %s", module, line) } if f[4] != "-" { it.CoveredBy = f[4] } out[module] = append(out[module], it) } return out, nil } // DataItems is every data item the resolution's modules declare, placed on its node with the rendering given: the text // the node-backup holder is handed, read. A module left out of the node's declaration keeps its items, as its data is // still on the node (ADR 0262); an item that cannot be placed is said in unplaced and left out. func (r Resolution) DataItems(with Rendering) (items map[string][]PlacedDataItem, unplaced []string, err error) { left := r.LeftOut(with.Settings, with.Adopted) for module, why := range with.NotPlaced { if _, already := left[module]; !already { left[module] = why } } modules := make([]Manifest, 0, len(r.Modules)) for _, m := range r.Modules { if _, isLeft := left[m.Module]; isLeft { m = backupView(m) } modules = append(modules, m) } text, unplaced, err := seatContributions(modules, Manifest{}, BackupSeat+":"+BackupKindData, with, machineFacts(r, with.Names, with.MeshRange), r.Capabilities) if err != nil { return nil, unplaced, err } items, err = ParseDataItems(text) return items, unplaced, err }