+105
-32
@@ -13,27 +13,32 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
generateEnvs []string
|
||||
generateOutput string
|
||||
generateDry bool
|
||||
generateEnvs []string
|
||||
generateOutput string
|
||||
generateDry bool
|
||||
generatePRNumber string
|
||||
)
|
||||
|
||||
var generateCmd = &cobra.Command{
|
||||
Use: "generate",
|
||||
Short: "Generate Kubernetes manifests from kforge.yml",
|
||||
Long: `Reads kforge.yml (or the file specified with --config), resolves
|
||||
each requested environment, and writes flat Kubernetes manifest
|
||||
files to the output directory.
|
||||
Long: `Reads kforge.yml, resolves each requested environment, and writes
|
||||
flat Kubernetes manifest files to the output directory.
|
||||
|
||||
If no --env flags are given, manifests are generated for all
|
||||
environments defined in kforge.yml.
|
||||
|
||||
Use --pr-number to generate manifests for a PR preview environment.
|
||||
A Namespace manifest is prepended so kubectl apply creates the
|
||||
namespace automatically.
|
||||
|
||||
Examples:
|
||||
kforge generate
|
||||
kforge generate --env staging
|
||||
kforge generate --env staging --env production
|
||||
kforge generate --env production --output .kube/
|
||||
kforge generate --dry-run`,
|
||||
kforge generate --dry-run
|
||||
kforge generate --pr-number 42`,
|
||||
RunE: runGenerate,
|
||||
}
|
||||
|
||||
@@ -44,6 +49,8 @@ func init() {
|
||||
"Directory to write generated manifests into")
|
||||
generateCmd.Flags().BoolVar(&generateDry, "dry-run", false,
|
||||
"Print manifests to stdout instead of writing files")
|
||||
generateCmd.Flags().StringVar(&generatePRNumber, "pr-number", "",
|
||||
"PR number — synthesizes and generates a preview environment")
|
||||
rootCmd.AddCommand(generateCmd)
|
||||
}
|
||||
|
||||
@@ -53,6 +60,15 @@ func runGenerate(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Preview mode: synthesize the preview environment.
|
||||
if generatePRNumber != "" {
|
||||
env, err := config.SynthesizePreviewEnvironment(cfg, generatePRNumber)
|
||||
if err != nil {
|
||||
return fmt.Errorf("synthesizing preview environment: %w", err)
|
||||
}
|
||||
return generateForPreview(cfg, &env, generatePRNumber)
|
||||
}
|
||||
|
||||
envKeys := generateEnvs
|
||||
if len(envKeys) == 0 {
|
||||
envKeys = config.EnvironmentKeys(cfg)
|
||||
@@ -78,30 +94,9 @@ func generateForEnv(cfg *config.KforgeConfig, envKey string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Core manifests (Service, Deployment, Ingress, Certs).
|
||||
coreYAML, err := generator.GenerateAll(&env, cfg)
|
||||
coreYAML, infraManifests, err := buildManifests(cfg, &env)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating core manifests: %w", err)
|
||||
}
|
||||
|
||||
// Infrastructure manifests.
|
||||
infraManifests, err := generator.GenerateInfrastructure(&env)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating infrastructure manifests: %w", err)
|
||||
}
|
||||
|
||||
// Collect all infrastructure env vars and append to deployment.
|
||||
// We re-generate core manifests after injecting infra env vars.
|
||||
var infraEnvVars []config.EnvVarConfig
|
||||
for _, m := range infraManifests {
|
||||
infraEnvVars = append(infraEnvVars, m.EnvVars...)
|
||||
}
|
||||
if len(infraEnvVars) > 0 {
|
||||
env.EnvVars = config.MergeEnvVars(env.EnvVars, infraEnvVars)
|
||||
coreYAML, err = generator.GenerateAll(&env, cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("re-generating core manifests with infra vars: %w", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if generateDry {
|
||||
@@ -112,14 +107,16 @@ func generateForEnv(cfg *config.KforgeConfig, envKey string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write core manifest.
|
||||
if err := os.MkdirAll(generateOutput, 0o755); err != nil {
|
||||
return fmt.Errorf("creating output directory: %w", err)
|
||||
}
|
||||
|
||||
coreFile := filepath.Join(generateOutput, envKey+"-core.yaml")
|
||||
if err := os.WriteFile(coreFile, []byte(coreYAML), 0o644); err != nil {
|
||||
return fmt.Errorf("writing core manifest: %w", err)
|
||||
}
|
||||
fmt.Printf(" ✓ %s\n", coreFile)
|
||||
|
||||
// Write infra manifests.
|
||||
for _, m := range infraManifests {
|
||||
infraFile := filepath.Join(generateOutput, envKey+"-infra-"+m.Name+".yaml")
|
||||
if err := os.WriteFile(infraFile, []byte(m.Content), 0o644); err != nil {
|
||||
@@ -131,6 +128,82 @@ func generateForEnv(cfg *config.KforgeConfig, envKey string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateForPreview generates manifests for a synthesized preview
|
||||
// environment, prepending a Namespace manifest so kubectl apply
|
||||
// creates the namespace in a single pass.
|
||||
func generateForPreview(cfg *config.KforgeConfig, env *config.ResolvedEnvironment, prNumber string) error {
|
||||
coreYAML, infraManifests, err := buildManifests(cfg, env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepend a Namespace manifest so kubectl apply is self-contained.
|
||||
nsYAML := generator.Namespace(env.Namespace, map[string]string{
|
||||
"managed-by": "kforge",
|
||||
"kforge/preview": "true",
|
||||
"kforge/pr": prNumber,
|
||||
})
|
||||
coreYAML = nsYAML + generator.Separator + coreYAML
|
||||
|
||||
envKey := "preview-" + prNumber
|
||||
|
||||
if generateDry {
|
||||
printDryRun(envKey, "core", coreYAML)
|
||||
for _, m := range infraManifests {
|
||||
printDryRun(envKey, m.Name, m.Content)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(generateOutput, 0o755); err != nil {
|
||||
return fmt.Errorf("creating output directory: %w", err)
|
||||
}
|
||||
|
||||
coreFile := filepath.Join(generateOutput, envKey+"-core.yaml")
|
||||
if err := os.WriteFile(coreFile, []byte(coreYAML), 0o644); err != nil {
|
||||
return fmt.Errorf("writing core manifest: %w", err)
|
||||
}
|
||||
fmt.Printf(" ✓ %s\n", coreFile)
|
||||
|
||||
for _, m := range infraManifests {
|
||||
infraFile := filepath.Join(generateOutput, envKey+"-infra-"+m.Name+".yaml")
|
||||
if err := os.WriteFile(infraFile, []byte(m.Content), 0o644); err != nil {
|
||||
return fmt.Errorf("writing %s manifest: %w", m.Name, err)
|
||||
}
|
||||
fmt.Printf(" ✓ %s\n", infraFile)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildManifests generates core + infra manifests for a resolved environment,
|
||||
// injecting infra env vars into the deployment.
|
||||
func buildManifests(cfg *config.KforgeConfig, env *config.ResolvedEnvironment) (string, []generator.InfraManifest, error) {
|
||||
coreYAML, err := generator.GenerateAll(env, cfg)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("generating core manifests: %w", err)
|
||||
}
|
||||
|
||||
infraManifests, err := generator.GenerateInfrastructure(env)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("generating infrastructure manifests: %w", err)
|
||||
}
|
||||
|
||||
var infraEnvVars []config.EnvVarConfig
|
||||
for _, m := range infraManifests {
|
||||
infraEnvVars = append(infraEnvVars, m.EnvVars...)
|
||||
}
|
||||
if len(infraEnvVars) > 0 {
|
||||
env.EnvVars = config.MergeEnvVars(env.EnvVars, infraEnvVars)
|
||||
coreYAML, err = generator.GenerateAll(env, cfg)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("re-generating core manifests with infra vars: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return coreYAML, infraManifests, nil
|
||||
}
|
||||
|
||||
func printDryRun(envKey, name, content string) {
|
||||
fmt.Printf("\n%s\n# --- %s / %s ---\n%s\n",
|
||||
generator.Separator,
|
||||
|
||||
Reference in New Issue
Block a user