feat(setup): generate local installation configuration
This commit is contained in:
@@ -18,7 +18,9 @@ import (
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/config"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/output"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/pi"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/project"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/serverops"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/setup"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/workspaceops"
|
||||
)
|
||||
|
||||
@@ -28,6 +30,8 @@ When --installation is omitted, tht uses THOTHII_INSTALLATION or discovers one v
|
||||
descriptor in the current project tree.
|
||||
|
||||
Commands:
|
||||
setup [--configure-only] [--installation-id ID] [--profile local|server]
|
||||
Create or validate the local non-secret installation configuration.
|
||||
status Show the Compose service state.
|
||||
doctor Validate Docker, Compose, rendered configuration, line endings, volumes, and health.
|
||||
logs Show the latest 200 sanitized service log lines (bounded; no follow mode).
|
||||
@@ -86,6 +90,9 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||
fmt.Fprint(stdout, usage)
|
||||
return 0
|
||||
}
|
||||
if command == "setup" {
|
||||
return setupCommand(installationPath, commandArgs, stdout, stderr)
|
||||
}
|
||||
return commandUsageError(stderr, fmt.Sprintf("unknown command %q", command))
|
||||
}
|
||||
workingDirectory, err := os.Getwd()
|
||||
@@ -198,6 +205,120 @@ func isBootstrapCommand(command string) bool {
|
||||
return command == "help" || command == "setup" || command == "version"
|
||||
}
|
||||
|
||||
func setupCommand(installationPath string, args []string, stdout, stderr io.Writer) int {
|
||||
request, err := parseSetupArgs(args)
|
||||
if err != nil {
|
||||
return commandUsageError(stderr, err.Error())
|
||||
}
|
||||
workingDirectory, err := os.Getwd()
|
||||
if err != nil {
|
||||
return commandUsageError(stderr, "current directory is unavailable")
|
||||
}
|
||||
root, err := project.Discover(workingDirectory)
|
||||
if err != nil {
|
||||
return commandUsageError(stderr, err.Error())
|
||||
}
|
||||
request.ProjectRoot = root.Path
|
||||
if installationPath != "" {
|
||||
id, pathErr := installationIDFromPath(root.Path, installationPath)
|
||||
if pathErr != nil {
|
||||
return commandUsageError(stderr, pathErr.Error())
|
||||
}
|
||||
if request.InstallationID != "" && request.InstallationID != id {
|
||||
return commandUsageError(stderr, "--installation and --installation-id must identify the same installation")
|
||||
}
|
||||
request.InstallationID = id
|
||||
}
|
||||
result, err := setup.EnsureFiles(request, os.Stdin, stdout)
|
||||
if err != nil {
|
||||
return commandUsageError(stderr, err.Error())
|
||||
}
|
||||
fmt.Fprintf(stdout, "Configuration is ready: %s\n", result.DescriptorPath)
|
||||
return 0
|
||||
}
|
||||
|
||||
func installationIDFromPath(root, installationPath string) (string, error) {
|
||||
if filepath.Base(installationPath) != "thothii-installation.yaml" {
|
||||
return "", errors.New("--installation must name thothii-installation.yaml below deploy/<installation-id>")
|
||||
}
|
||||
relative, err := filepath.Rel(filepath.Join(root, "deploy"), installationPath)
|
||||
if err != nil {
|
||||
return "", errors.New("--installation must be below this project's deploy directory")
|
||||
}
|
||||
parts := strings.Split(filepath.Clean(relative), string(filepath.Separator))
|
||||
if len(parts) != 2 || parts[0] == "." || parts[0] == ".." || parts[1] != "thothii-installation.yaml" {
|
||||
return "", errors.New("--installation must be below this project's deploy/<installation-id> directory")
|
||||
}
|
||||
return parts[0], nil
|
||||
}
|
||||
|
||||
func parseSetupArgs(args []string) (setup.Request, error) {
|
||||
request := setup.Request{}
|
||||
for len(args) > 0 {
|
||||
flag := args[0]
|
||||
args = args[1:]
|
||||
switch flag {
|
||||
case "--configure-only":
|
||||
if request.ConfigureOnly {
|
||||
return setup.Request{}, errors.New("--configure-only may be supplied once")
|
||||
}
|
||||
request.ConfigureOnly = true
|
||||
case "--non-interactive":
|
||||
if request.NonInteractive {
|
||||
return setup.Request{}, errors.New("--non-interactive may be supplied once")
|
||||
}
|
||||
request.NonInteractive = true
|
||||
case "--create-secret-templates":
|
||||
if request.Answers.CreateSecretTemplates {
|
||||
return setup.Request{}, errors.New("--create-secret-templates may be supplied once")
|
||||
}
|
||||
request.Answers.CreateSecretTemplates = true
|
||||
default:
|
||||
if len(args) == 0 {
|
||||
return setup.Request{}, fmt.Errorf("%s requires a value", flag)
|
||||
}
|
||||
value := args[0]
|
||||
args = args[1:]
|
||||
var target *string
|
||||
switch flag {
|
||||
case "--installation-id":
|
||||
target = &request.InstallationID
|
||||
case "--profile":
|
||||
target = &request.Profile
|
||||
case "--workspace-remote":
|
||||
target = &request.Answers.WorkspaceRemote
|
||||
case "--workspace-branch":
|
||||
target = &request.Answers.WorkspaceBranch
|
||||
case "--workspace-access":
|
||||
target = &request.Answers.WorkspaceAccess
|
||||
case "--dwh-rest-url":
|
||||
target = &request.Answers.DWHRESTURL
|
||||
case "--llm-url":
|
||||
target = &request.Answers.LLMURL
|
||||
case "--secrets-file":
|
||||
target = &request.Answers.SecretsFile
|
||||
case "--pi-auth-file":
|
||||
target = &request.Answers.PiAuthFile
|
||||
case "--git-credentials-file":
|
||||
target = &request.Answers.GitCredentialsFile
|
||||
case "--git-ca-file":
|
||||
target = &request.Answers.GitCAFile
|
||||
case "--git-ssh-key-file":
|
||||
target = &request.Answers.GitSSHKeyFile
|
||||
case "--git-known-hosts-file":
|
||||
target = &request.Answers.GitKnownHostsFile
|
||||
default:
|
||||
return setup.Request{}, fmt.Errorf("unknown setup option %q", flag)
|
||||
}
|
||||
if *target != "" {
|
||||
return setup.Request{}, fmt.Errorf("%s may be supplied once", flag)
|
||||
}
|
||||
*target = value
|
||||
}
|
||||
}
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func writeRemovalTargets(outputWriter io.Writer, project string, targets []serverops.Container) {
|
||||
fmt.Fprintf(outputWriter, "Removal targets for installation project %s:\n", project)
|
||||
if len(targets) == 0 {
|
||||
|
||||
Reference in New Issue
Block a user