package remote import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "git.faercol.me/faercol/http-boot-config/config/logger" "git.faercol.me/faercol/http-boot-config/config/prober" "github.com/google/uuid" ) const enrollURL = "/enroll" type enrollEFIOption struct { Name string `json:"name"` Path string `json:"path"` DiskID string `json:"disk_id"` } type enrollPayload struct { Name string `json:"name"` Options map[string]enrollEFIOption `json:"options"` SelectedOption string `json:"selected_option"` } type enrollRespPayload struct { ID string `json:"ID"` } func enrollToServer(ctx context.Context, host string, name string, apps []prober.EfiApp) (uuid.UUID, error) { payload := enrollPayload{ Name: name, Options: make(map[string]enrollEFIOption), } for _, a := range apps { appID := uuid.New() payload.Options[appID.String()] = enrollEFIOption{Name: a.Name, Path: a.Path, DiskID: a.DiskID} if a.Active { payload.SelectedOption = appID.String() } } dat, err := json.Marshal(payload) if err != nil { return uuid.Nil, fmt.Errorf("failed to serialize payload: %w", err) } subCtx, cancel := context.WithTimeout(ctx, reqTimeout) defer cancel() req, err := http.NewRequestWithContext(subCtx, http.MethodPost, host+enrollURL, bytes.NewBuffer(dat)) if err != nil { return uuid.Nil, fmt.Errorf("failed to initialize request: %w", err) } resp, err := http.DefaultClient.Do(req) if err != nil { return uuid.Nil, fmt.Errorf("failed to do query to remote boot server: %w", err) } if resp.StatusCode != http.StatusOK { return uuid.Nil, fmt.Errorf("unexpected returncode %d", resp.StatusCode) } var respPayload enrollRespPayload respDat, err := io.ReadAll(resp.Body) if err != nil { return uuid.Nil, fmt.Errorf("failed to read response from server: %w", err) } if err := json.Unmarshal(respDat, &respPayload); err != nil { return uuid.Nil, fmt.Errorf("failed to deserialize data from server: %w", err) } newID, err := uuid.Parse(respPayload.ID) if err != nil { return uuid.Nil, fmt.Errorf("invalid UUID %q from server: %w", respPayload.ID, err) } return newID, nil } func Enroll(ctx context.Context, host, name string, l *logger.SimpleLogger) error { apps, err := prober.GetEFIApps(l) if err != nil { return fmt.Errorf("failed to get list of available EFI applications: %w", err) } newID, err := enrollToServer(ctx, host, name, apps) if err != nil { return fmt.Errorf("failed to enroll client to remote host: %w", err) } l.Infof("Successfully enrolled new client, ID is %q", newID.String()) return nil }