5 changed files with 156 additions and 0 deletions
			
			
		| @ -0,0 +1,2 @@ | ||||
| // Package ceph deals with ceph config
 | ||||
| package ceph | ||||
| @ -0,0 +1,38 @@ | ||||
| package ceph | ||||
| 
 | ||||
| import ( | ||||
| 	"io" | ||||
| 	"net/netip" | ||||
| 
 | ||||
| 	"github.com/gofrs/uuid/v5" | ||||
| 
 | ||||
| 	"asciigoat.org/ini/basic" | ||||
| ) | ||||
| 
 | ||||
| // Config represents a ceph.conf file
 | ||||
| type Config struct { | ||||
| 	Global GlobalConfig `ini:"global"` | ||||
| } | ||||
| 
 | ||||
| // GlobalConfig represents the [global] section of a ceph.conf file
 | ||||
| type GlobalConfig struct { | ||||
| 	FSID           uuid.UUID    `ini:"fsid"` | ||||
| 	Monitors       []string     `ini:"mon_initial_members,comma"` | ||||
| 	MonitorsAddr   []netip.Addr `ini:"mon_host,comma"` | ||||
| 	ClusterNetwork netip.Prefix `ini:"cluster_network"` | ||||
| } | ||||
| 
 | ||||
| // NewConfigFromReader parses the ceph.conf file
 | ||||
| func NewConfigFromReader(r io.Reader) (*Config, error) { | ||||
| 	doc, err := basic.Decode(r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	cfg, err := newConfigFromDocument(doc) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return cfg, nil | ||||
| } | ||||
| @ -0,0 +1,110 @@ | ||||
| package ceph | ||||
| 
 | ||||
| import ( | ||||
| 	"io/fs" | ||||
| 	"net/netip" | ||||
| 
 | ||||
| 	"asciigoat.org/ini/basic" | ||||
| 	"asciigoat.org/ini/parser" | ||||
| 
 | ||||
| 	"darvaza.org/core" | ||||
| ) | ||||
| 
 | ||||
| var sectionMap = map[string]func(*Config, *basic.Section) error{ | ||||
| 	"global": loadGlobalConfSection, | ||||
| } | ||||
| 
 | ||||
| func loadConfSection(out *Config, src *basic.Section) error { | ||||
| 	h, ok := sectionMap[src.Key] | ||||
| 	if !ok { | ||||
| 		return core.Wrapf(fs.ErrInvalid, "unknown section %q", src.Key) | ||||
| 	} | ||||
| 
 | ||||
| 	return h(out, src) | ||||
| } | ||||
| 
 | ||||
| func loadGlobalConfSection(out *Config, src *basic.Section) error { | ||||
| 	var cfg GlobalConfig | ||||
| 
 | ||||
| 	for _, field := range src.Fields { | ||||
| 		if err := loadGlobalConfField(&cfg, field); err != nil { | ||||
| 			return core.Wrap(err, "global") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	out.Global = cfg | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // revive:disable:cyclomatic
 | ||||
| // revive:disable:cognitive-complexity
 | ||||
| 
 | ||||
| func loadGlobalConfField(cfg *GlobalConfig, field basic.Field) error { | ||||
| 	// revive:enable:cyclomatic
 | ||||
| 	// revive:enable:cognitive-complexity
 | ||||
| 
 | ||||
| 	// TODO: refactor when asciigoat's ini parser learns to do reflection
 | ||||
| 
 | ||||
| 	switch field.Key { | ||||
| 	case "fsid": | ||||
| 		if !core.IsZero(cfg.FSID) { | ||||
| 			return core.Wrapf(fs.ErrInvalid, "duplicate field %q", field.Key) | ||||
| 		} | ||||
| 
 | ||||
| 		err := cfg.FSID.UnmarshalText([]byte(field.Value)) | ||||
| 		switch { | ||||
| 		case err != nil: | ||||
| 			return core.Wrap(err, field.Key) | ||||
| 		default: | ||||
| 			return nil | ||||
| 		} | ||||
| 	case "mon_host": | ||||
| 		entries, _ := parser.SplitCommaArray(field.Value) | ||||
| 		for _, s := range entries { | ||||
| 			var addr netip.Addr | ||||
| 
 | ||||
| 			if err := addr.UnmarshalText([]byte(s)); err != nil { | ||||
| 				return core.Wrap(err, field.Key) | ||||
| 			} | ||||
| 
 | ||||
| 			cfg.MonitorsAddr = append(cfg.MonitorsAddr, addr) | ||||
| 		} | ||||
| 		return nil | ||||
| 	case "mon_initial_members": | ||||
| 		entries, _ := parser.SplitCommaArray(field.Value) | ||||
| 		cfg.Monitors = append(cfg.Monitors, entries...) | ||||
| 		return nil | ||||
| 	case "cluster_network": | ||||
| 		if !core.IsZero(cfg.ClusterNetwork) { | ||||
| 			err := core.Wrap(fs.ErrInvalid, "fields before the first section") | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		err := cfg.ClusterNetwork.UnmarshalText([]byte(field.Value)) | ||||
| 		switch { | ||||
| 		case err != nil: | ||||
| 			return core.Wrap(err, field.Key) | ||||
| 		default: | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func newConfigFromDocument(doc *basic.Document) (*Config, error) { | ||||
| 	var out Config | ||||
| 
 | ||||
| 	if len(doc.Global) > 0 { | ||||
| 		err := core.Wrap(fs.ErrInvalid, "fields before the first section") | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	for i := range doc.Sections { | ||||
| 		src := &doc.Sections[i] | ||||
| 		if err := loadConfSection(&out, src); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return &out, nil | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue