Install the suite, for real, in containers
The plan now happens. "inbuxa install --local --domain example.test --install-deps --yes" on a machine with nothing on it ends with a mail server, a console and a webmail running, an administrator and a first mailbox created, and the records the domain needs written out. The sequence is the one ihasmail-oneshot worked out against a running server, which is why its JMAP client and its Docker handling came across nearly whole: bring the server up in bootstrap mode with a credential that lives in an override file for that step only, complete bootstrap, bring the rest up without it -- so no recovery credential outlives the setup -- exempt the front ends from the auto-ban, restart for the settings that need it, create the first account, and write down the password nothing else holds. New here: three services rather than two. The console is static files that learn their server's address at start, and the webmail is given the first-party OAuth client secret that the server is given too. Twenty checks in the lab, from a bare Debian 13. The two worth having are the ones that catch an install that looks fine and is not: nothing in the running server carries a recovery admin any more, and the account the installer created can sign in to the webmail it installed. Two bugs the lab caught, both of which would have shipped: - the private addresses were worked out on a copy of the stack, so the server was told the webmail speaks from "", and refused it. - the console image rewrites index.html when it starts, so a read-only root filesystem left it restarting forever. The webmail keeps read_only; the console cannot have it until that rewrite moves.
This commit is contained in:
+46
-3
@@ -12,9 +12,12 @@ import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.coffeylabs.org/inbuxa/inbuxa-installer/internal/apply"
|
||||
"git.coffeylabs.org/inbuxa/inbuxa-installer/internal/deps"
|
||||
"git.coffeylabs.org/inbuxa/inbuxa-installer/internal/host"
|
||||
"git.coffeylabs.org/inbuxa/inbuxa-installer/internal/plan"
|
||||
@@ -131,13 +134,53 @@ func install(args []string) int {
|
||||
return 0
|
||||
}
|
||||
if !*yes {
|
||||
fmt.Fprintln(os.Stderr, "\nnothing has happened yet: applying is not built in this build (pass --dry-run to silence this)")
|
||||
fmt.Fprintln(os.Stderr, "\nNothing has happened yet. Pass --yes to carry this out.")
|
||||
return 1
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "\napply is not built yet")
|
||||
return 1
|
||||
|
||||
fmt.Println("\nApplying:")
|
||||
log := &printer{}
|
||||
res, err := apply.Run(context.Background(), p, log)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "\nstopped: "+err.Error())
|
||||
return 1
|
||||
}
|
||||
fmt.Println("\nChecking it works:")
|
||||
problems := apply.Verify(context.Background(), res,
|
||||
o.Shape(plan.Console) != plan.Skip, o.Shape(plan.Webmail) != plan.Skip, log)
|
||||
for _, why := range problems {
|
||||
fmt.Fprintln(os.Stderr, " problem: "+why)
|
||||
}
|
||||
|
||||
fmt.Printf("\nDone. %s\n", res.Dir)
|
||||
fmt.Printf(" administrator %s\n", res.AdminUser)
|
||||
if res.FirstUser != "" {
|
||||
fmt.Printf(" first mailbox %s\n", res.FirstUser)
|
||||
}
|
||||
fmt.Printf(" passwords %s\n", filepath.Join(res.Dir, "credentials.txt"))
|
||||
if res.ConsoleURL != "" {
|
||||
fmt.Printf(" console %s\n", res.ConsoleURL)
|
||||
}
|
||||
if res.WebmailURL != "" {
|
||||
fmt.Printf(" webmail %s\n", res.WebmailURL)
|
||||
}
|
||||
if res.ZoneFile != "" {
|
||||
fmt.Printf(" dns records %s\n", res.ZoneFile)
|
||||
}
|
||||
if len(problems) > 0 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// printer is apply's log on the flag path: steps as lines, everything a
|
||||
// command says indented under the step that ran it.
|
||||
type printer struct{}
|
||||
|
||||
func (printer) Step(format string, a ...any) { fmt.Printf(" "+format+"\n", a...) }
|
||||
func (printer) Info(format string, a ...any) { fmt.Printf(" "+format+"\n", a...) }
|
||||
func (printer) Out() io.Writer { return os.Stdout }
|
||||
|
||||
// depsCmd is the offer on its own: what the chosen shapes need that this
|
||||
// machine does not have, and -- with --install -- the doing of it. It exists
|
||||
// separately from install because an operator preparing a machine should be
|
||||
|
||||
Reference in New Issue
Block a user