package bootstrap import ( "fmt" "io/fs" "path/filepath" "strings" "gopkg.in/yaml.v3" ) const ( hostsDirPath = "hosts" ) // NebulaHost describes the nebula configuration of a Host which is relevant for // other hosts to know. type NebulaHost struct { IP string `yaml:"ip"` PublicAddr string `yaml:"public_addr,omitempty"` } // GarageHost describes a single garage instance in the GarageHost. type GarageHostInstance struct { ID string `yaml:"id"` RPCPort int `yaml:"rpc_port"` S3APIPort int `yaml:"s3_api_port"` } // GarageHost describes the garage configuration of a Host which is relevant for // other hosts to know. type GarageHost struct { Instances []GarageHostInstance `yaml:"instances"` } // Host consolidates all information about a single host from the bootstrap // file. type Host struct { Name string `yaml:"name"` Nebula NebulaHost `yaml:"nebula"` Garage *GarageHost `yaml:"garage,omitempty"` } func loadHosts(bootstrapFS fs.FS) (map[string]Host, error) { hosts := map[string]Host{} readAsYaml := func(into interface{}, path string) error { b, err := fs.ReadFile(bootstrapFS, path) if err != nil { return fmt.Errorf("reading file from fs: %w", err) } return yaml.Unmarshal(b, into) } globPath := filepath.Join(hostsDirPath, "*.yml") hostPaths, err := fs.Glob(bootstrapFS, globPath) if err != nil { return nil, fmt.Errorf("listing host files at %q in fs: %w", globPath, err) } for _, hostPath := range hostPaths { hostName := filepath.Base(hostPath) hostName = strings.TrimSuffix(hostName, filepath.Ext(hostName)) var host Host if err := readAsYaml(&host, hostPath); err != nil { return nil, fmt.Errorf("reading %q as yaml: %w", hostPath, err) } hosts[hostName] = host } if len(hosts) == 0 { return nil, fmt.Errorf("failed to load any hosts from fs") } return hosts, nil }