Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions cli/cmd/init_install_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ type InitInstallConfigOpts struct {
CodesphereOpenBaoEngine string
CodesphereOpenBaoUser string
CodesphereOpenBaoPassword string

OpenfgaBackupsEnabled bool
OpenfgaBackupsDestinationPath string
OpenfgaBackupsEndpointURL string
OpenfgaBackupsSchedule string
OpenfgaBackupsRetentionPolicy string
OpenfgaBackupsAccessKeyID string
OpenfgaBackupsSecretAccessKey string
}

func (c *InitInstallConfigCmd) RunE(_ *cobra.Command, args []string) error {
Expand Down Expand Up @@ -191,6 +199,15 @@ func AddInitInstallConfigCmd(init *cobra.Command, opts *util.GlobalOptions) {
c.cmd.Flags().StringVar(&c.Opts.CodesphereOpenBaoUser, "openbao-user", "admin", "Username for OpenBao authentication")
c.cmd.Flags().StringVar(&c.Opts.CodesphereOpenBaoPassword, "openbao-password", "", "Password for OpenBao authentication")

// OpenFGA database backups
c.cmd.Flags().BoolVar(&c.Opts.OpenfgaBackupsEnabled, "openfga-backups-enabled", false, "Enable OpenFGA database backups")
c.cmd.Flags().StringVar(&c.Opts.OpenfgaBackupsDestinationPath, "openfga-backups-destination", "", "Backup destination (S3 URL, e.g. s3://backup-openfga)")
c.cmd.Flags().StringVar(&c.Opts.OpenfgaBackupsEndpointURL, "openfga-backups-endpoint", "", "S3-compatible endpoint URL (e.g. https://storage.googleapis.com)")
c.cmd.Flags().StringVar(&c.Opts.OpenfgaBackupsSchedule, "openfga-backups-schedule", "", "Backup schedule (6-field cron, empty for chart default)")
c.cmd.Flags().StringVar(&c.Opts.OpenfgaBackupsRetentionPolicy, "openfga-backups-retention", "", "Retention policy (e.g. 7d, empty for chart default)")
c.cmd.Flags().StringVar(&c.Opts.OpenfgaBackupsAccessKeyID, "openfga-backups-access-key-id", "", "S3 access key ID for OpenFGA backups")
c.cmd.Flags().StringVar(&c.Opts.OpenfgaBackupsSecretAccessKey, "openfga-backups-secret-access-key", "", "S3 secret access key for OpenFGA backups")

util.MarkFlagRequired(c.cmd, "config")
util.MarkFlagRequired(c.cmd, "vault")

Expand Down Expand Up @@ -533,6 +550,32 @@ func (c *InitInstallConfigCmd) updateConfigFromOpts(config *files.RootConfig, va
}
}

// OpenFGA database backups
if c.Opts.OpenfgaBackupsEnabled {
if config.Codesphere.OpenfgaBackups == nil {
config.Codesphere.OpenfgaBackups = &files.OpenfgaBackupsConfig{}
}
config.Codesphere.OpenfgaBackups.Enabled = true
if c.Opts.OpenfgaBackupsDestinationPath != "" {
config.Codesphere.OpenfgaBackups.DestinationPath = c.Opts.OpenfgaBackupsDestinationPath
}
if c.Opts.OpenfgaBackupsEndpointURL != "" {
config.Codesphere.OpenfgaBackups.EndpointURL = c.Opts.OpenfgaBackupsEndpointURL
}
if c.Opts.OpenfgaBackupsSchedule != "" {
config.Codesphere.OpenfgaBackups.Schedule = c.Opts.OpenfgaBackupsSchedule
}
if c.Opts.OpenfgaBackupsRetentionPolicy != "" {
config.Codesphere.OpenfgaBackups.RetentionPolicy = c.Opts.OpenfgaBackupsRetentionPolicy
}
if c.Opts.OpenfgaBackupsAccessKeyID != "" {
vault.SetSecret(files.SecretEntry{Name: files.SecretOpenfgaDbBackupAccessKeyId, Fields: &files.SecretFields{Password: c.Opts.OpenfgaBackupsAccessKeyID}})
}
if c.Opts.OpenfgaBackupsSecretAccessKey != "" {
vault.SetSecret(files.SecretEntry{Name: files.SecretOpenfgaDbBackupSecretAccessKey, Fields: &files.SecretFields{Password: c.Opts.OpenfgaBackupsSecretAccessKey}})
}
}

// Plans
if c.Opts.CodesphereHostingPlanCPUTenth != 0 || c.Opts.CodesphereHostingPlanMemoryMb != 0 ||
c.Opts.CodesphereHostingPlanStorageMb != 0 || c.Opts.CodesphereHostingPlanTempStorageMb != 0 {
Expand Down
41 changes: 41 additions & 0 deletions cli/cmd/init_install_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,47 @@ var _ = Describe("UpdateConfigFromOpts", func() {

Expect(config.Postgres.ServerAddress).To(Equal("postgres.example.com:5432"))
})

It("sets openfga backups config and vault secrets when enabled", func() {
config := &files.RootConfig{}
vault := &files.InstallVault{}
command := &InitInstallConfigCmd{Opts: &InitInstallConfigOpts{
OpenfgaBackupsEnabled: true,
OpenfgaBackupsDestinationPath: "s3://backup-openfga",
OpenfgaBackupsEndpointURL: "https://storage.googleapis.com",
OpenfgaBackupsSchedule: "0 */30 * * * *",
OpenfgaBackupsRetentionPolicy: "7d",
OpenfgaBackupsAccessKeyID: "access-id",
OpenfgaBackupsSecretAccessKey: "secret-key",
}}

command.updateConfigFromOpts(config, vault)

Expect(config.Codesphere.OpenfgaBackups).NotTo(BeNil())
Expect(config.Codesphere.OpenfgaBackups.Enabled).To(BeTrue())
Expect(config.Codesphere.OpenfgaBackups.DestinationPath).To(Equal("s3://backup-openfga"))
Expect(config.Codesphere.OpenfgaBackups.EndpointURL).To(Equal("https://storage.googleapis.com"))
Expect(config.Codesphere.OpenfgaBackups.Schedule).To(Equal("0 */30 * * * *"))
Expect(config.Codesphere.OpenfgaBackups.RetentionPolicy).To(Equal("7d"))

accessKey := vault.GetSecret(files.SecretOpenfgaDbBackupAccessKeyId)
Expect(accessKey).NotTo(BeNil())
Expect(accessKey.Fields.Password).To(Equal("access-id"))
secretKey := vault.GetSecret(files.SecretOpenfgaDbBackupSecretAccessKey)
Expect(secretKey).NotTo(BeNil())
Expect(secretKey.Fields.Password).To(Equal("secret-key"))
})

It("does not set openfga backups config when not enabled", func() {
config := &files.RootConfig{}
command := &InitInstallConfigCmd{Opts: &InitInstallConfigOpts{
OpenfgaBackupsDestinationPath: "s3://ignored",
}}

command.updateConfigFromOpts(config, &files.InstallVault{})

Expect(config.Codesphere.OpenfgaBackups).To(BeNil())
})
})

var _ = Describe("ValidateConfig", func() {
Expand Down
75 changes: 41 additions & 34 deletions docs/oms_init_install-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,40 +50,47 @@ $ oms init install-config --validate -c config.yaml --vault prod.vault.yaml
### Options

```
--acme-dns01-provider string DNS provider for DNS-01 solver (e.g., cloudflare)
--acme-eab-key-id string External Account Binding key ID (required by some ACME providers)
--acme-eab-mac-key string External Account Binding MAC key (required by some ACME providers)
--acme-email string Email address for ACME account registration
--acme-enabled Enable ACME certificate issuer
--acme-issuer-name string Name for the ACME ClusterIssuer (default "acme-issuer")
--acme-server string ACME server URL (default "https://acme-v02.api.letsencrypt.org/directory")
--ansible-inventory string Path to Ansible inventory file to import host information from
--ceph-csi-kubelet-dir string Directory of kubelet for ceph csi. Required for some cloud providers
--ceph-nodes-subnet string CIDR subnet for ceph nodes
-c, --config string Output file path for config.yaml (default "config.yaml")
--dc-city string Datacenter city
--dc-country-code string Datacenter country code
--dc-id int Datacenter ID
--dc-name string Datacenter name
--domain string Main Codesphere domain
--generate-keys Generate SSH keys and certificates (default true)
-h, --help help for install-config
--interactive Enable interactive prompting (when true, other config flags are ignored) (default true)
--k8s-control-plane strings K8s control plane IPs (comma-separated)
--k8s-managed Use Codesphere-managed Kubernetes (default true)
--openbao-engine string Engine for OpenBao (default "cs-secrets-engine")
--openbao-password string Password for OpenBao authentication
--openbao-uri string URI for OpenBao (e.g., https://openbao.example.com)
--openbao-user string Username for OpenBao authentication (default "admin")
--postgres-mode string PostgreSQL setup mode (install/external)
--postgres-primary-ip string Primary PostgreSQL server IP
--postgres-server string PostgreSQL server hostname for install mode or address for external mode
--profile string Use a predefined configuration profile (dev, production, minimal)
--registry-server string Server for container registry
--secrets-dir string Secrets base directory (default "/root/secrets")
--validate Validate existing config files instead of creating new ones
--vault string Output file path for prod.vault.yaml (default "prod.vault.yaml")
--with-comments Add helpful comments to the generated YAML files
--acme-dns01-provider string DNS provider for DNS-01 solver (e.g., cloudflare)
--acme-eab-key-id string External Account Binding key ID (required by some ACME providers)
--acme-eab-mac-key string External Account Binding MAC key (required by some ACME providers)
--acme-email string Email address for ACME account registration
--acme-enabled Enable ACME certificate issuer
--acme-issuer-name string Name for the ACME ClusterIssuer (default "acme-issuer")
--acme-server string ACME server URL (default "https://acme-v02.api.letsencrypt.org/directory")
--ansible-inventory string Path to Ansible inventory file to import host information from
--ceph-csi-kubelet-dir string Directory of kubelet for ceph csi. Required for some cloud providers
--ceph-nodes-subnet string CIDR subnet for ceph nodes
-c, --config string Output file path for config.yaml (default "config.yaml")
--dc-city string Datacenter city
--dc-country-code string Datacenter country code
--dc-id int Datacenter ID
--dc-name string Datacenter name
--domain string Main Codesphere domain
--generate-keys Generate SSH keys and certificates (default true)
-h, --help help for install-config
--interactive Enable interactive prompting (when true, other config flags are ignored) (default true)
--k8s-control-plane strings K8s control plane IPs (comma-separated)
--k8s-managed Use Codesphere-managed Kubernetes (default true)
--openbao-engine string Engine for OpenBao (default "cs-secrets-engine")
--openbao-password string Password for OpenBao authentication
--openbao-uri string URI for OpenBao (e.g., https://openbao.example.com)
--openbao-user string Username for OpenBao authentication (default "admin")
--openfga-backups-access-key-id string S3 access key ID for OpenFGA backups
--openfga-backups-destination string Backup destination (S3 URL, e.g. s3://backup-openfga)
--openfga-backups-enabled Enable OpenFGA database backups
--openfga-backups-endpoint string S3-compatible endpoint URL (e.g. https://storage.googleapis.com)
--openfga-backups-retention string Retention policy (e.g. 7d, empty for chart default)
--openfga-backups-schedule string Backup schedule (6-field cron, empty for chart default)
--openfga-backups-secret-access-key string S3 secret access key for OpenFGA backups
--postgres-mode string PostgreSQL setup mode (install/external)
--postgres-primary-ip string Primary PostgreSQL server IP
--postgres-server string PostgreSQL server hostname for install mode or address for external mode
--profile string Use a predefined configuration profile (dev, production, minimal)
--registry-server string Server for container registry
--secrets-dir string Secrets base directory (default "/root/secrets")
--validate Validate existing config files instead of creating new ones
--vault string Output file path for prod.vault.yaml (default "prod.vault.yaml")
--with-comments Add helpful comments to the generated YAML files
```

### SEE ALSO
Expand Down
46 changes: 46 additions & 0 deletions internal/installer/config_generator_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,52 @@ func (g *InstallConfig) collectCodesphereConfig(prompter *Prompter) {
}

g.collectOpenBaoConfig(prompter)
g.collectOpenfgaBackupsConfig(prompter)
}

func (g *InstallConfig) collectOpenfgaBackupsConfig(prompter *Prompter) {
log.Println("\n=== OpenFGA Database Backups (Optional) ===")
hasBackups := prompter.Bool("Configure OpenFGA database backups", g.Config.Codesphere.OpenfgaBackups != nil && g.Config.Codesphere.OpenfgaBackups.Enabled)
if !hasBackups {
g.Config.Codesphere.OpenfgaBackups = nil
return
}

if g.Config.Codesphere.OpenfgaBackups == nil {
g.Config.Codesphere.OpenfgaBackups = &files.OpenfgaBackupsConfig{}
}
ob := g.Config.Codesphere.OpenfgaBackups
ob.Enabled = true

ob.DestinationPath = g.collectString(prompter, "Backup destination (S3 URL, e.g. s3://backup-openfga)", ob.DestinationPath)
ob.EndpointURL = g.collectString(prompter, "S3-compatible endpoint URL (e.g. https://storage.googleapis.com)", ob.EndpointURL)
ob.Schedule = g.collectString(prompter, "Backup schedule (6-field cron, empty for chart default)", ob.Schedule)
ob.RetentionPolicy = g.collectString(prompter, "Retention policy (e.g. 7d, empty for chart default)", ob.RetentionPolicy)

accessKeyID := ""
secretAccessKey := ""
if g.Vault != nil {
if s := g.Vault.GetSecret(files.SecretOpenfgaDbBackupAccessKeyId); s != nil && s.Fields != nil {
accessKeyID = s.Fields.Password
}
if s := g.Vault.GetSecret(files.SecretOpenfgaDbBackupSecretAccessKey); s != nil && s.Fields != nil {
secretAccessKey = s.Fields.Password
}
}
accessKeyID = g.collectString(prompter, "S3 access key ID", accessKeyID)
secretAccessKey = g.collectString(prompter, "S3 secret access key", secretAccessKey)

if accessKeyID != "" || secretAccessKey != "" {
if g.Vault == nil {
g.Vault = &files.InstallVault{}
}
if accessKeyID != "" {
g.Vault.SetSecret(files.SecretEntry{Name: files.SecretOpenfgaDbBackupAccessKeyId, Fields: &files.SecretFields{Password: accessKeyID}})
}
if secretAccessKey != "" {
g.Vault.SetSecret(files.SecretEntry{Name: files.SecretOpenfgaDbBackupSecretAccessKey, Fields: &files.SecretFields{Password: secretAccessKey}})
}
}
}

func (g *InstallConfig) collectOpenBaoConfig(prompter *Prompter) {
Expand Down
4 changes: 2 additions & 2 deletions internal/installer/files/config_yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,8 @@ type OpenBaoConfig struct {
}

// OpenfgaBackupsConfig is the friendly representation of the OpenFGA database
// backup settings. On marshal it is translated into the openfga subchart values
// under codesphere.override (see buildOpenfgaBackupOverride).
// backup settings. On marshal it is translated into the openfga application
// values under pcApps.applications.openfga (see buildOpenfgaBackupValues).
type OpenfgaBackupsConfig struct {
Enabled bool `yaml:"enabled"`
// Schedule is an optional 6-field cron expression. When empty the chart default applies.
Expand Down
Loading