package main import ( "errors" "fmt" "isle/bootstrap" "isle/jsonutil" "os" "regexp" "sort" "github.com/mediocregopher/mediocre-go-lib/v2/mlog" ) var hostNameRegexp = regexp.MustCompile(`^[a-z][a-z0-9\-]*$`) func validateHostName(name string) error { if !hostNameRegexp.MatchString(name) { return errors.New("a host's name must start with a letter and only contain letters, numbers, and dashes") } return nil } var subCmdHostsList = subCmd{ name: "list", descr: "Lists all hosts in the network, and their IPs", checkLock: true, do: func(subCmdCtx subCmdCtx) error { flags := subCmdCtx.flagSet(false) logLevelStr := flags.StringP( "log-level", "l", "info", `Maximum log level which should be output. Values can be "debug", "info", "warn", "error", "fatal". Does not apply to sub-processes`, ) if err := flags.Parse(subCmdCtx.args); err != nil { return fmt.Errorf("parsing flags: %w", err) } ctx := subCmdCtx.ctx logLevel := mlog.LevelFromString(*logLevelStr) if logLevel == nil { return fmt.Errorf("couldn't parse log level %q", *logLevelStr) } logger := subCmdCtx.logger.WithMaxLevel(logLevel.Int()) hostBootstrap, err := loadHostBootstrap() if err != nil { return fmt.Errorf("loading host bootstrap: %w", err) } hostsMap, err := hostBootstrap.GetGarageBootstrapHosts(ctx, logger) if err != nil { return fmt.Errorf("retrieving hosts from garage: %w", err) } type host struct { Name string VPN struct { IP string } Storage bootstrap.GarageHost `json:",omitempty"` } hosts := make([]host, 0, len(hostsMap)) for _, h := range hostsMap { host := host{ Name: h.Name, Storage: h.Garage, } host.VPN.IP = h.IP().String() hosts = append(hosts, host) } sort.Slice(hosts, func(i, j int) bool { return hosts[i].Name < hosts[j].Name }) return jsonutil.WriteIndented(os.Stdout, hosts) }, } var subCmdHostsDelete = subCmd{ name: "delete", descr: "Deletes a host from the network", checkLock: true, do: func(subCmdCtx subCmdCtx) error { flags := subCmdCtx.flagSet(false) hostName := flags.StringP( "hostname", "h", "", "Name of the host to delete", ) if err := flags.Parse(subCmdCtx.args); err != nil { return fmt.Errorf("parsing flags: %w", err) } if *hostName == "" { return errors.New("--hostname is required") } hostBootstrap, err := loadHostBootstrap() if err != nil { return fmt.Errorf("loading host bootstrap: %w", err) } client := hostBootstrap.GlobalBucketS3APIClient() return bootstrap.RemoveGarageBootstrapHost(subCmdCtx.ctx, client, *hostName) }, } var subCmdHosts = subCmd{ name: "hosts", descr: "Sub-commands having to do with configuration of hosts in the network", do: func(subCmdCtx subCmdCtx) error { return subCmdCtx.doSubCmd( subCmdHostsDelete, subCmdHostsList, ) }, }