153 lines
5.7 KiB
Go
153 lines
5.7 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/Pingu-Studio/MaidnCLI/internal/bootstrap"
|
|
"github.com/Pingu-Studio/MaidnCLI/internal/config"
|
|
"github.com/Pingu-Studio/MaidnCLI/internal/forgejo"
|
|
"github.com/Pingu-Studio/MaidnCLI/internal/openbao"
|
|
"github.com/Pingu-Studio/MaidnCLI/internal/ui"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var bootstrapConfigPath string
|
|
var bootstrapOutputPath string
|
|
var bootstrapMode string
|
|
var bootstrapYes bool
|
|
var bootstrapPromptDemocraticCSI bool
|
|
var bootstrapPromptOperationalSecrets bool
|
|
var bootstrapInitializeOpenBaoRecovery bool
|
|
var bootstrapPublishAppFrom string
|
|
var bootstrapMergeBootstrapPR bool
|
|
var bootstrapManageNetworkBridges bool
|
|
|
|
var bootstrapCmd = &cobra.Command{
|
|
Use: "bootstrap",
|
|
Short: "Bootstrap Talos and Flux from config or an interactive wizard.",
|
|
RunE: runBootstrap,
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(bootstrapCmd)
|
|
bootstrapCmd.Flags().StringVar(&bootstrapConfigPath, "config", "", "Path to bootstrap config YAML")
|
|
bootstrapCmd.Flags().StringVar(&bootstrapOutputPath, "out", "maidn-bootstrap.yaml", "Path to save generated config")
|
|
bootstrapCmd.Flags().StringVar(&bootstrapMode, "mode", string(bootstrap.Reconcile), "Lifecycle mode: reconcile or rebuild")
|
|
bootstrapCmd.Flags().BoolVar(&bootstrapYes, "yes", false, "Confirm destructive rebuild")
|
|
bootstrapCmd.Flags().BoolVar(&bootstrapPromptDemocraticCSI, "prompt-democratic-csi", false, "Prompt for and save Democratic CSI settings in --config")
|
|
bootstrapCmd.Flags().BoolVar(&bootstrapPromptOperationalSecrets, "prompt-operational-secrets", false, "Prompt for and encrypt operational secrets for --config")
|
|
bootstrapCmd.Flags().BoolVar(&bootstrapInitializeOpenBaoRecovery, "initialize-openbao-recovery", false, "Create and save a separate OpenBao recovery age identity for --config")
|
|
bootstrapCmd.Flags().StringVar(&bootstrapPublishAppFrom, "publish-app-from", "", "Push this app checkout's current branch and create a Forgejo delivery PR")
|
|
bootstrapCmd.Flags().BoolVar(&bootstrapMergeBootstrapPR, "merge-bootstrap-pr", false, "Merge the generated Flux repository migration PR before bootstrapping")
|
|
bootstrapCmd.Flags().BoolVar(&bootstrapManageNetworkBridges, "manage-network-bridges", false, "Persist Terraform management for existing Talos network bridges")
|
|
}
|
|
|
|
func runBootstrap(cmd *cobra.Command, args []string) error {
|
|
var cfg config.Config
|
|
var err error
|
|
if bootstrapMergeBootstrapPR {
|
|
if bootstrapConfigPath == "" {
|
|
return fmt.Errorf("--merge-bootstrap-pr requires --config")
|
|
}
|
|
cfg, err = config.Load(bootstrapConfigPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
manager := forgejo.NewRepoManager(cfg.Git.BaseURL, cfg.Git.Token, cfg.Git.Owner, cfg.Git.Username, "", "", cfg.Flux.Branch, "")
|
|
if err := manager.MergePullRequest(cfg.Flux.RepoName, "maidn/bootstrap-"+cfg.ClusterID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if bootstrapPublishAppFrom != "" {
|
|
if bootstrapConfigPath == "" {
|
|
return fmt.Errorf("--publish-app-from requires --config")
|
|
}
|
|
cfg, err = config.Load(bootstrapConfigPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
branch, err := forgejo.CurrentBranch(bootstrapPublishAppFrom)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
owner, repo, err := forgejo.RepositoryFromURL(cfg.Delivery.AppRepoURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
manager := forgejo.NewRepoManager(cfg.Git.BaseURL, cfg.Git.Token, owner, cfg.Git.Username, "", "", cfg.Delivery.AppRepoRef, "")
|
|
created, err := manager.EnsureRepository(repo, "Application source for Maidn CI/CD delivery")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hasDeliveryBranch, err := manager.HasRemoteBranch(cfg.Delivery.AppRepoURL, cfg.Delivery.AppRepoRef)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if created || !hasDeliveryBranch {
|
|
if err := manager.PushRef(bootstrapPublishAppFrom, cfg.Delivery.AppRepoURL, "HEAD", cfg.Delivery.AppRepoRef); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := manager.PushBranch(bootstrapPublishAppFrom, cfg.Delivery.AppRepoURL, branch); err != nil {
|
|
return err
|
|
}
|
|
return manager.CreatePullRequest(repo, "feat: migrate delivery to Tekton", branch, cfg.Delivery.AppRepoRef)
|
|
}
|
|
|
|
if bootstrapConfigPath != "" {
|
|
if bootstrapPromptDemocraticCSI || bootstrapPromptOperationalSecrets || bootstrapInitializeOpenBaoRecovery || bootstrapManageNetworkBridges {
|
|
cfg, err = config.LoadRaw(bootstrapConfigPath)
|
|
if err == nil {
|
|
if bootstrapPromptDemocraticCSI {
|
|
cfg = ui.PromptDemocraticCSI(cfg)
|
|
}
|
|
if bootstrapManageNetworkBridges {
|
|
cfg.Talos.Cluster.ManageNetworkBridges = true
|
|
}
|
|
cfg, err = config.Resolve(cfg)
|
|
}
|
|
if err == nil && bootstrapPromptOperationalSecrets {
|
|
var secrets map[string]map[string]string
|
|
secrets, err = ui.PromptOperationalSecrets(cfg)
|
|
if err == nil {
|
|
var authorization string
|
|
authorization, err = bootstrap.NewWebhookAuthorization()
|
|
if err == nil {
|
|
secrets["cicd/forgejo-webhook"] = map[string]string{"authorization": authorization}
|
|
err = bootstrap.WriteOperationalSecrets(cfg.SOPS.OperationalSecretsPath, cfg.SOPS.AgeKeyPath, secrets)
|
|
}
|
|
}
|
|
}
|
|
if err == nil && bootstrapInitializeOpenBaoRecovery {
|
|
var recipient string
|
|
recipient, err = openbao.EnsureRecoveryIdentity(cfg.SOPS.RecoveryIdentityPath)
|
|
if err == nil {
|
|
cfg.SOPS.RecoveryRecipient = recipient
|
|
}
|
|
}
|
|
if err == nil {
|
|
err = config.Save(bootstrapConfigPath, cfg)
|
|
}
|
|
} else {
|
|
cfg, err = config.Load(bootstrapConfigPath)
|
|
}
|
|
} else {
|
|
cfg, err = ui.RunBootstrapWizard(config.Config{})
|
|
if err == nil {
|
|
cfg, err = config.Resolve(cfg)
|
|
}
|
|
if err == nil {
|
|
err = config.Save(bootstrapOutputPath, cfg)
|
|
if err == nil {
|
|
fmt.Printf("[INFO] Saved config to %s\n", bootstrapOutputPath)
|
|
}
|
|
}
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
runner := bootstrap.Runner{Config: cfg, Mode: bootstrap.Mode(bootstrapMode), ConfirmRebuild: bootstrapYes}
|
|
return runner.Run()
|
|
}
|