Documentation
¶
Overview ¶
Example (BuilderContractDefaults) ¶
package main
import (
"fmt"
"github.com/go-kure/kure/pkg/stack/fluxcd"
)
func main() {
// declared in pkg/stack/fluxcd/defaults.go
fmt.Println(fluxcd.DefaultInterval) // 60 * time.Minute
fmt.Println(fluxcd.DefaultNamespace) // "flux-system"
fmt.Println(fluxcd.DefaultSourceKind) // "OCIRepository"
}
Output: 1h0m0s flux-system OCIRepository
Example (FluxWorkflowBootstrapNamespace) ¶
package main
import (
"fmt"
"github.com/go-kure/kure/pkg/stack"
"github.com/go-kure/kure/pkg/stack/fluxcd"
)
func main() {
engine := fluxcd.Engine()
rootNode := &stack.Node{Name: "production"}
bootstrapConfig := &stack.BootstrapConfig{Enabled: true}
engine.GetBootstrapGenerator().DefaultNamespace = "custom-flux" // default: "flux-system"
objects, err := engine.GenerateBootstrap(bootstrapConfig, rootNode)
if err != nil {
panic(err)
}
for _, obj := range objects {
switch obj.GetObjectKind().GroupVersionKind().Kind {
case "FluxInstance":
fmt.Println("FluxInstance in", obj.GetNamespace())
case "Namespace":
fmt.Println("Namespace", obj.GetName())
}
}
}
Output: Namespace flux-system FluxInstance in custom-flux
Example (FluxWorkflowDefine) ¶
package main
import (
"fmt"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/go-kure/kure/pkg/kubernetes"
"github.com/go-kure/kure/pkg/stack"
)
// workloadApp is the stack.ApplicationConfig the guide's applications use: a
// Deployment and a Service named after the application.
type workloadApp struct{}
func (workloadApp) Generate(app *stack.Application) ([]*client.Object, error) {
var dep client.Object = kubernetes.CreateDeployment(app.Name, app.Namespace)
var svc client.Object = kubernetes.CreateService(app.Name, app.Namespace)
return []*client.Object{&dep, &svc}, nil
}
var (
certManagerConfig stack.ApplicationConfig = workloadApp{}
frontendConfig stack.ApplicationConfig = workloadApp{}
apiConfig stack.ApplicationConfig = workloadApp{}
)
func main() {
// The Flux source every bundle's Kustomization reads from.
source := &stack.SourceRef{Kind: "GitRepository", Name: "flux-system"}
certManager, err := stack.NewBundle("cert-manager", []*stack.Application{
stack.NewApplication("cert-manager", "cert-manager", certManagerConfig),
}, nil)
if err != nil {
panic(err)
}
certManager.SourceRef = source
webTier, err := stack.NewBundle("web-tier", []*stack.Application{
stack.NewApplication("frontend", "web", frontendConfig),
stack.NewApplication("api-gateway", "web", apiConfig),
}, nil)
if err != nil {
panic(err)
}
webTier.SourceRef = source
cluster := stack.NewCluster("production", &stack.Node{
Name: "production",
Children: []*stack.Node{
{Name: "infrastructure", Bundle: certManager},
{Name: "applications", Bundle: webTier},
},
})
for _, node := range cluster.Node.Children {
b := node.Bundle
fmt.Println(node.Name, b.Name, len(b.Applications), b.SourceRef.Kind, b.SourceRef.Name)
}
}
Output: infrastructure cert-manager 1 GitRepository flux-system applications web-tier 2 GitRepository flux-system
Example (FluxWorkflowDependsOn) ¶
package main
import (
"fmt"
"github.com/go-kure/kure/pkg/stack/layout"
)
func main() {
preInstall := &layout.ManifestLayout{
Name: "nginx-00-pre-install",
// ...
}
hooks := &layout.ManifestLayout{
Name: "nginx-01-hooks",
DependsOn: []string{"nginx-00-pre-install"},
// ...
}
fmt.Println(hooks.Name, "after", hooks.DependsOn[0] == preInstall.Name)
}
Output: nginx-01-hooks after true
Example (FluxWorkflowEngine) ¶
package main
import (
"fmt"
"github.com/go-kure/kure/pkg/stack/fluxcd"
)
func main() {
engine := fluxcd.Engine()
fmt.Println(engine.GetName())
}
Output: FluxCD Workflow Engine
Example (FluxWorkflowLayout) ¶
package main
import (
"fmt"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/go-kure/kure/pkg/kubernetes"
"github.com/go-kure/kure/pkg/stack"
"github.com/go-kure/kure/pkg/stack/fluxcd"
"github.com/go-kure/kure/pkg/stack/layout"
)
// workloadApp is the stack.ApplicationConfig the guide's applications use: a
// Deployment and a Service named after the application.
type workloadApp struct{}
func (workloadApp) Generate(app *stack.Application) ([]*client.Object, error) {
var dep client.Object = kubernetes.CreateDeployment(app.Name, app.Namespace)
var svc client.Object = kubernetes.CreateService(app.Name, app.Namespace)
return []*client.Object{&dep, &svc}, nil
}
var (
certManagerConfig stack.ApplicationConfig = workloadApp{}
frontendConfig stack.ApplicationConfig = workloadApp{}
apiConfig stack.ApplicationConfig = workloadApp{}
)
// productionCluster is the cluster Example_fluxWorkflowDefine builds, for the
// steps that start from it. Keep the two in step: the Example spells the
// cluster out because its body is the guide's Step 1, so it cannot call this.
func productionCluster() *stack.Cluster {
source := &stack.SourceRef{Kind: "GitRepository", Name: "flux-system"}
certManager, err := stack.NewBundle("cert-manager", []*stack.Application{
stack.NewApplication("cert-manager", "cert-manager", certManagerConfig),
}, nil)
if err != nil {
panic(err)
}
certManager.SourceRef = source
webTier, err := stack.NewBundle("web-tier", []*stack.Application{
stack.NewApplication("frontend", "web", frontendConfig),
stack.NewApplication("api-gateway", "web", apiConfig),
}, nil)
if err != nil {
panic(err)
}
webTier.SourceRef = source
return stack.NewCluster("production", &stack.Node{
Name: "production",
Children: []*stack.Node{
{Name: "infrastructure", Bundle: certManager},
{Name: "applications", Bundle: webTier},
},
})
}
func main() {
cluster := productionCluster()
engine := fluxcd.Engine()
// Define layout rules
rules := layout.LayoutRules{
NodeGrouping: layout.GroupByName,
BundleGrouping: layout.GroupByName,
ApplicationGrouping: layout.GroupByName,
FilePer: layout.FilePerResource,
FluxPlacement: layout.FluxSeparate, // Flux resources in separate tree
}
// Generate layout with Flux resources integrated
ml, err := engine.CreateLayoutWithResources(cluster, rules)
if err != nil {
panic(err)
}
for _, child := range ml.(*layout.ManifestLayout).Children {
fmt.Println(child.FullRepoPath())
}
}
Output: production/infrastructure production/applications production/flux-system
Example (FluxWorkflowUmbrella) ¶
package main
import (
"fmt"
kustv1 "github.com/fluxcd/kustomize-controller/api/v1"
"github.com/go-kure/kure/pkg/stack"
"github.com/go-kure/kure/pkg/stack/fluxcd"
)
func main() {
umbrella := &stack.Bundle{
Name: "platform",
Children: []*stack.Bundle{
{Name: "platform-infra"},
{Name: "platform-services"},
{Name: "platform-apps"},
},
}
objects, err := fluxcd.Engine().ResourceGen.GenerateForBundle(umbrella, "production/apps/platform")
if err != nil {
panic(err)
}
ks := objects[0].(*kustv1.Kustomization)
for _, hc := range ks.Spec.HealthChecks {
fmt.Println(hc.Kind, hc.Namespace, hc.Name)
}
}
Output: Kustomization flux-system platform-infra Kustomization flux-system platform-services Kustomization flux-system platform-apps
Example (FluxWorkflowWrite) ¶
ml := productionLayout()
out, err := os.MkdirTemp("", "kure-flux-workflow")
if err != nil {
panic(err)
}
defer func() { _ = os.RemoveAll(out) }()
err = layout.WriteManifest(out, layout.DefaultLayoutConfig(), ml.(*layout.ManifestLayout))
if err != nil {
panic(err)
}
printFiles(out)
Output: clusters/production/applications/kustomization.yaml clusters/production/applications/web-tier/api-gateway/kustomization.yaml clusters/production/applications/web-tier/api-gateway/web-deployment-api-gateway.yaml clusters/production/applications/web-tier/api-gateway/web-service-api-gateway.yaml clusters/production/applications/web-tier/frontend/kustomization.yaml clusters/production/applications/web-tier/frontend/web-deployment-frontend.yaml clusters/production/applications/web-tier/frontend/web-service-frontend.yaml clusters/production/applications/web-tier/kustomization.yaml clusters/production/flux-system/flux-system-kustomization-cert-manager.yaml clusters/production/flux-system/flux-system-kustomization-web-tier.yaml clusters/production/flux-system/kustomization.yaml clusters/production/infrastructure/cert-manager/cert-manager/cert-manager-deployment-cert-manager.yaml clusters/production/infrastructure/cert-manager/cert-manager/cert-manager-service-cert-manager.yaml clusters/production/infrastructure/cert-manager/cert-manager/kustomization.yaml clusters/production/infrastructure/cert-manager/kustomization.yaml clusters/production/infrastructure/kustomization.yaml clusters/production/kustomization.yaml
Index ¶
- Constants
- func FluxOperatorInstallObjects() ([]client.Object, error)
- type BootstrapGenerator
- func (bg *BootstrapGenerator) GenerateBootstrap(config *stack.BootstrapConfig, rootNode *stack.Node) ([]client.Object, error)
- func (bg *BootstrapGenerator) GenerateFluxInstance(config *stack.BootstrapConfig, rootNode *stack.Node) (*fluxv1.FluxInstance, error)
- func (bg *BootstrapGenerator) SupportedBootstrapModes() []string
- type LayoutIntegrator
- type ResourceGenerator
- func (g *ResourceGenerator) GenerateForBundle(b *stack.Bundle, path string) ([]client.Object, error)
- func (g *ResourceGenerator) GenerateFromCluster(c *stack.Cluster) ([]client.Object, error)
- func (g *ResourceGenerator) GenerateFromLayout(root *layout.ManifestLayout, c *stack.Cluster) ([]client.Object, error)
- func (g *ResourceGenerator) GetName() string
- func (g *ResourceGenerator) GetVersion() string
- type WorkflowEngine
- func (we *WorkflowEngine) CreateLayoutWithResources(c *stack.Cluster, rules stack.LayoutRulesProvider) (stack.ManifestLayoutResult, error)
- func (we *WorkflowEngine) GenerateBootstrap(config *stack.BootstrapConfig, rootNode *stack.Node) ([]client.Object, error)
- func (we *WorkflowEngine) GenerateFromCluster(c *stack.Cluster) ([]client.Object, error)
- func (we *WorkflowEngine) GetBootstrapGenerator() *BootstrapGenerator
- func (we *WorkflowEngine) GetLayoutIntegrator() *LayoutIntegrator
- func (we *WorkflowEngine) GetName() string
- func (we *WorkflowEngine) GetResourceGenerator() *ResourceGenerator
- func (we *WorkflowEngine) GetVersion() string
- func (we *WorkflowEngine) IntegrateWithLayout(ml *layout.ManifestLayout, c *stack.Cluster, rules layout.LayoutRules) error
- func (we *WorkflowEngine) SupportedBootstrapModes() []string
Examples ¶
- Package (BuilderContractDefaults)
- Package (FluxWorkflowBootstrapNamespace)
- Package (FluxWorkflowDefine)
- Package (FluxWorkflowDependsOn)
- Package (FluxWorkflowEngine)
- Package (FluxWorkflowLayout)
- Package (FluxWorkflowUmbrella)
- Package (FluxWorkflowWrite)
- BootstrapGenerator.GenerateFluxInstance
- Engine
- NewWorkflowEngine
- ResourceGenerator.GenerateFromLayout
- WorkflowEngine.CreateLayoutWithResources
- WorkflowEngine.GenerateBootstrap
Constants ¶
const ( // DefaultNamespace is the namespace generated Flux resources are placed in // when the caller names none. DefaultNamespace = "flux-system" // DefaultSourceName is the name given to a generated GitRepository or // OCIRepository when the root node has no name of its own. DefaultSourceName = "flux-system" // DefaultBootstrapName is the name given to the bootstrap Kustomization. It // is not derived from the root node. Override it by assigning // [BootstrapGenerator.BootstrapName]. It does not reach the FluxInstance, // whose name is fixed — see [FluxInstanceName]. DefaultBootstrapName = "flux-system" // FluxInstanceName is the metadata.name of the FluxInstance emitted in // flux-operator mode. It is not a default and has no override: the // flux-operator CRD requires metadata.name to be "flux" and rejects any // other name at admission (x-kubernetes-validations rule // `self.metadata.name == 'flux'` in the vendored flux_operator_install.yaml). // The name used to follow [BootstrapGenerator.BootstrapName], so a bundle // generated with the default ([DefaultBootstrapName]) or any override other // than "flux" was refused with "the only accepted name for a FluxInstance // is 'flux'". A test compares this constant against the vendored CRD's rule, so // an operator bump that changes the rule fails there rather than at apply. FluxInstanceName = "flux" // DefaultFluxDirName is the directory a separate Flux layout is placed in, // under FluxSeparate placement. It is a path segment, not a namespace: it // happens to share [DefaultNamespace]'s value and must not be derived from // it, because a caller who renames the namespace does not thereby rename // the directory. It has no override. DefaultFluxDirName = "flux-system" // DefaultBootstrapPathRoot is the first segment of the bootstrap // Kustomization's spec.path; the root node's name is joined onto it. It is a // fixed segment with no override — rename the root node to change the path. DefaultBootstrapPathRoot = "manifests" // DefaultFluxMode is the bootstrap mode used when BootstrapConfig.FluxMode // is empty. It is also the mode GenerateBootstrap dispatches on and the // first entry [BootstrapGenerator.SupportedBootstrapModes] reports, so // changing it here changes the default and the accepted spelling together // — the alternative left an empty FluxMode resolving to a mode the switch // no longer recognised. DefaultFluxMode = "flux-operator" // ModeGotk is the legacy bootstrap mode. It is not a default — nothing // falls back to it — but it is named here so the mode set has one // authority alongside [DefaultFluxMode] rather than a literal repeated in // the dispatch switch and the supported-mode list. ModeGotk = "gotk" // DefaultSourceKind is the kind of source object bootstrap emits, and // references, when BootstrapConfig.SourceKind does not name "GitRepository". // That includes the empty string: an unnamed kind yields an OCIRepository // for backward compatibility, which is generateSource's own behaviour. // // There is deliberately one identifier rather than one per emission site. // The bootstrap Kustomization's sourceRef, the source object itself and the // FluxInstance sync block previously each decided the kind for themselves, // and the first of the three used the opposite polarity to the other two — // so an empty SourceKind emitted an OCIRepository under a sourceRef naming a // GitRepository that was never created. [resolvedSourceKind] is now the only // place that decision is made. DefaultSourceKind = "OCIRepository" // DefaultSourceRef is the OCI tag used when BootstrapConfig.SourceRef is // empty, by both the gotk OCIRepository and the FluxInstance sync // ([resolvedSyncRef]). It has no GitRepository equivalent: an empty // SourceRef leaves the Git reference unset rather than guessing a branch. DefaultSourceRef = "latest" // DefaultSyncPath is the path a FluxInstance sync block uses when the root // node has no name, and the prefix its name is appended to when it has one. // It has no override: it is the prefix a sync path is built from, not a // value a caller replaces. DefaultSyncPath = "./" )
The values a generator falls back to when the caller supplies nothing.
pkg/stack is a workflow layer above pkg/kubernetes and may hold opinions — but as declared inputs with names a consumer can read, compare against and override, never as literals buried in a constructor. Every fallback the generators apply is one of the identifiers below; grep for the identifier to find every place the value can reach emitted YAML.
These are defaults, not policy, and they are overridden in one of two ways. DefaultNamespace, DefaultInterval and DefaultBootstrapName are copied into an exported field of ResourceGenerator or BootstrapGenerator by its constructor, and a caller that assigns the field afterwards is never overridden. The rest are applied where they are used, and are overridden by naming the corresponding input on stack.BootstrapConfig, stack.Bundle or the root stack.Node — the per-identifier comments below say which input each one yields to.
Three have no override at all: DefaultSyncPath, DefaultBootstrapPathRoot and DefaultFluxDirName. Each is a fixed structural segment of a path the package builds, not a value a caller replaces, and their comments say so. They are named here anyway so the value is greppable and reviewable rather than a literal inside the function that emits it. FluxInstanceName is likewise fixed but is not a default of any kind: the flux-operator CRD accepts no other value, so it is a constraint the package satisfies, not an opinion it holds.
Interval and namespace are the two that always reach output. KustomizationSpec.Interval is +required upstream with no omitempty (kustomize-controller api/v1/kustomization_types.go:68), so a Kustomization cannot be emitted without one; DefaultInterval is what a caller who names no interval gets.
const DefaultInterval = 60 * time.Minute
DefaultInterval is the reconciliation interval used when the caller names none, on both generators. It applies only to an empty Bundle.Interval: a non-empty value that does not parse is a validation error, never a reason to fall back to this default.
const DefaultMode = layout.KustomizationExplicit
DefaultMode names the kustomization.yaml listing mode the layout writers use for a layout with no Mode of its own (they treat KustomizationUnset as KustomizationExplicit). It is informational: no generator field is seeded from it, because a Kustomization's spec.path is not a mode of the generator but the directory of the layout that renders the bundle (layout.OriginIndex.KustomizationPath).
const FluxOperatorVersion = "v0.58.1"
FluxOperatorVersion is the upstream flux-operator release whose install manifest is vendored as fluxOperatorInstallYAML. It is pinned to match the github.com/controlplaneio-fluxcd/flux-operator Go module version in kure's go.mod so that the generated FluxInstance type and the install bundle stay in lockstep.
scripts/sync-flux-operator-pin.sh performs steps 2-4 below from go.mod's version, and Renovate runs it after a module bump. To refresh by hand instead:
- Bump github.com/controlplaneio-fluxcd/flux-operator in go.mod.
- Download the matching install.yaml from the flux-operator GitHub release page: https://github.com/controlplaneio-fluxcd/flux-operator/releases/download/{version}/install.yaml
- Replace pkg/stack/fluxcd/flux_operator_install.yaml with it.
- Update this constant, and the version named in pkg/stack/fluxcd/README.md ("currently **vX.Y.Z**"); no test checks it.
- Run the tests in this package and confirm the resource inventory in TestFluxOperatorInstallObjects still matches.
const GotkVersion = gotk.Version
GotkVersion is the upstream flux2 release whose install manifests base (the release's manifests.tar.gz asset) is vendored in internal/gotk, so gotk-mode bootstrap generation emits the controllers whose API types kure compiles against, without any network access. The pin, and the procedure for refreshing it after a flux2 bump, live next to the bundle; TestVendoredPinsMatchGoMod fails when the pin and go.mod disagree.
Variables ¶
This section is empty.
Functions ¶
func FluxOperatorInstallObjects ¶
FluxOperatorInstallObjects returns the parsed Flux Operator install manifest: Namespace, CRDs, RBAC, ServiceAccount, Service, and controller Deployment. The bytes are embedded at build time from flux_operator_install.yaml (version FluxOperatorVersion).
The returned slice is cached on first parse; callers must treat it as read-only. To mutate any object, deep-copy first.
The order of objects matches the order in the upstream install.yaml (Namespace → CRDs → RBAC → ServiceAccount → Deployment → Service), which is also a valid apply order.
Types ¶
type BootstrapGenerator ¶
type BootstrapGenerator struct {
// DefaultNamespace is the namespace where bootstrap resources are created
DefaultNamespace string
// DefaultInterval is the default reconciliation interval
DefaultInterval time.Duration
// BootstrapName is the name given to the bootstrap Kustomization. It is not
// derived from the root node, and BootstrapConfig carries no equivalent
// input, so this field is the only way to override [DefaultBootstrapName].
// Leaving it empty means the default, not a nameless object — see
// [BootstrapGenerator.bootstrapName].
//
// It does not name the FluxInstance. That object's metadata.name is fixed
// at [FluxInstanceName] by the flux-operator CRD, which admits no other
// value; it used to take this field, so any bundle whose BootstrapName was
// not "flux" — the default included — was rejected at apply
// (go-kure/kure#847).
BootstrapName string
}
BootstrapGenerator implements the workflow.BootstrapGenerator interface for Flux. It handles the generation of bootstrap resources for setting up Flux.
func NewBootstrapGenerator ¶
func NewBootstrapGenerator() *BootstrapGenerator
NewBootstrapGenerator creates a FluxCD bootstrap generator seeded with the exported defaults (DefaultNamespace, DefaultInterval, DefaultBootstrapName). Assign the fields afterwards to override any of them.
func (*BootstrapGenerator) GenerateBootstrap ¶
func (bg *BootstrapGenerator) GenerateBootstrap(config *stack.BootstrapConfig, rootNode *stack.Node) ([]client.Object, error)
GenerateBootstrap creates bootstrap resources for setting up Flux. When FluxMode is empty, flux-operator is used as the default.
func (*BootstrapGenerator) GenerateFluxInstance ¶
func (bg *BootstrapGenerator) GenerateFluxInstance(config *stack.BootstrapConfig, rootNode *stack.Node) (*fluxv1.FluxInstance, error)
GenerateFluxInstance returns only the FluxInstance CR configured for the given bootstrap settings, without the full Flux Operator install bundle. Returns (nil, nil) when config is nil. Unlike GenerateBootstrap, this method does not check config.Enabled — the caller is responsible for that gate.
Example ¶
package main
import (
"fmt"
"github.com/go-kure/kure/pkg/stack"
"github.com/go-kure/kure/pkg/stack/fluxcd"
)
func main() {
engine := fluxcd.Engine()
rootNode := &stack.Node{Name: "prod"}
bootstrapConfig := &stack.BootstrapConfig{
Enabled: true,
SourceURL: "oci://registry.example.com/fleet",
SourceRef: "latest",
SyncName: "fleet",
}
fi, err := engine.GetBootstrapGenerator().GenerateFluxInstance(bootstrapConfig, rootNode)
if err != nil {
panic(err)
}
fmt.Println(fi.Name, fi.Spec.Sync.Name, fi.Spec.Sync.Ref)
}
Output: flux fleet latest
func (*BootstrapGenerator) SupportedBootstrapModes ¶
func (bg *BootstrapGenerator) SupportedBootstrapModes() []string
SupportedBootstrapModes returns the bootstrap modes supported by this generator. DefaultFluxMode is the primary (recommended) mode; ModeGotk is the legacy one. This is the single list: GenerateBootstrap's validation error reports it rather than restating the modes.
type LayoutIntegrator ¶
type LayoutIntegrator struct {
// ResourceGenerator generates the Flux resources
Generator *ResourceGenerator
}
LayoutIntegrator implements the workflow.LayoutIntegrator interface for Flux. It handles integration of Flux resources with manifest layouts.
Placement (FluxIntegratedPerLayout vs FluxSeparate) is configured via layout.LayoutRules.FluxPlacement on each call. CreateLayoutWithResources normalizes FluxUnset to FluxSeparate before invoking the SourceRef validation gate, WalkCluster, and IntegrateWithLayout, so all three observers agree on the effective placement.
func NewLayoutIntegrator ¶
func NewLayoutIntegrator(generator *ResourceGenerator) *LayoutIntegrator
NewLayoutIntegrator creates a FluxCD layout integrator.
func (*LayoutIntegrator) CreateLayoutWithResources ¶
func (li *LayoutIntegrator) CreateLayoutWithResources(c *stack.Cluster, rules layout.LayoutRules) (*layout.ManifestLayout, error)
CreateLayoutWithResources creates a new layout that includes Flux resources.
rules.FluxPlacement is normalized once at the top of this method (FluxUnset -> FluxSeparate) and the normalized rules are passed to the SourceRef validation gate, WalkCluster, and IntegrateWithLayout. This guarantees a single placement authority per call.
func (*LayoutIntegrator) IntegrateWithLayout ¶
func (li *LayoutIntegrator) IntegrateWithLayout(ml *layout.ManifestLayout, c *stack.Cluster, rules layout.LayoutRules) error
IntegrateWithLayout adds Flux resources to a manifest layout that layout.WalkCluster built from c.
Placement is driven by rules.FluxPlacement. FluxUnset is treated as FluxSeparate to match DefaultLayoutRules and the walker's normalization.
Every placement first indexes the layout's origins (layout.IndexOrigins): a hand-built, partial or other-cluster tree is refused rather than matched by name, and every Kustomization's spec.path is the directory of the layout that renders its bundle. Integrating the same layout again adds nothing: a CR already present with the same name and spec.path in the layout that would host it is kept; one with the same name elsewhere or with another path is an error.
type ResourceGenerator ¶
type ResourceGenerator struct {
// DefaultInterval is the default reconciliation interval for generated resources
DefaultInterval time.Duration
// DefaultNamespace is the default namespace for generated Flux resources
DefaultNamespace string
// Prune is the garbage-collection input for Kustomizations generated from a
// layout.ManifestLayout (FluxIntegratedPerLayout mode), which carries no
// prune setting of its own. Kustomizations generated from a stack.Bundle
// use that bundle's own Prune and ignore this field. nil emits
// prune: false — see pruneValue.
Prune *bool
}
ResourceGenerator implements the workflow.ResourceGenerator interface for Flux. It focuses purely on generating Flux CRDs from stack components.
func NewResourceGenerator ¶
func NewResourceGenerator() *ResourceGenerator
NewResourceGenerator creates a FluxCD resource generator seeded with the exported defaults (DefaultInterval, DefaultNamespace). Assign the fields afterwards to override any of them; nothing else is injected into generated resources.
func (*ResourceGenerator) GenerateForBundle ¶
func (g *ResourceGenerator) GenerateForBundle(b *stack.Bundle, path string) ([]client.Object, error)
GenerateForBundle creates the Flux resources for b itself: a Kustomization whose spec.path is path, verbatim, and a Source when b.SourceRef has a URL. Umbrella Children are not recursed. The generator computes no path: take it from the layout that renders b (layout.OriginIndex.KustomizationPath).
func (*ResourceGenerator) GenerateFromCluster ¶
GenerateFromCluster creates Flux Kustomizations and Sources from a cluster definition. It runs stack.ValidateCluster first to fail fast on structural errors (umbrella cycles, disjointness violations, etc.), then walks the cluster with layout.DefaultLayoutRules and generates from that layout (see GenerateFromLayout).
The spec.path values are therefore the directories WalkCluster writes under the default rules: the root node at <root>, its children at <root>/<child>. Callers that write the layout with other rules must generate from the layout they write instead — CreateLayoutWithResources, or GenerateFromLayout on their own WalkCluster result. The walk renders every application and runs every LayoutAugmenter, so their errors surface here.
func (*ResourceGenerator) GenerateFromLayout ¶
func (g *ResourceGenerator) GenerateFromLayout(root *layout.ManifestLayout, c *stack.Cluster) ([]client.Object, error)
GenerateFromLayout creates one Kustomization (and, when its SourceRef has a URL, a Source) for every reconciliation unit of the layout tree root — every directory that renders bundles — in layout pre-order. root must have been walked from c (layout.WalkCluster): layout.IndexOrigins refuses anything else. Each spec.path is that directory, relative to the writer's output root; the bundles a grouping axis merged into it share the Kustomization (see generateForUnit).
Example ¶
package main
import (
"fmt"
kustv1 "github.com/fluxcd/kustomize-controller/api/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/go-kure/kure/pkg/kubernetes"
"github.com/go-kure/kure/pkg/stack"
"github.com/go-kure/kure/pkg/stack/fluxcd"
"github.com/go-kure/kure/pkg/stack/layout"
)
// configMapApp is a minimal stack.ApplicationConfig: one ConfigMap named after
// the application.
type configMapApp struct{}
func (configMapApp) Generate(app *stack.Application) ([]*client.Object, error) {
var obj client.Object = kubernetes.CreateConfigMap(app.Name, app.Namespace)
return []*client.Object{&obj}, nil
}
// exampleCluster is the cluster the examples generate from: a root node
// "apps" whose bundle "web" holds one application.
func exampleCluster() *stack.Cluster {
cluster, err := stack.NewClusterBuilder("prod").
WithNode("apps").
WithBundle("web").
WithApplication("web", configMapApp{}).
End().
End().
Build()
if err != nil {
panic(err)
}
return cluster
}
func main() {
cluster := exampleCluster()
engine := fluxcd.Engine()
rules := layout.DefaultLayoutRules()
bundle := cluster.Node.Bundle
// From an entire cluster: walks it with layout.DefaultLayoutRules()
objects, err := engine.GenerateFromCluster(cluster)
if err != nil {
panic(err)
}
fmt.Println(objects[0].GetName(), objects[0].(*kustv1.Kustomization).Spec.Path)
// From a layout you walked (and will write) yourself
ml, err := layout.WalkCluster(cluster, rules)
if err != nil {
panic(err)
}
objects, err = engine.ResourceGen.GenerateFromLayout(ml, cluster)
if err != nil {
panic(err)
}
fmt.Println(objects[0].GetName(), objects[0].(*kustv1.Kustomization).Spec.Path)
// For one bundle, at a path you supply
objects, err = engine.ResourceGen.GenerateForBundle(bundle, "clusters/prod/apps")
if err != nil {
panic(err)
}
fmt.Println(objects[0].GetName(), objects[0].(*kustv1.Kustomization).Spec.Path)
}
Output: web apps web apps web clusters/prod/apps
func (*ResourceGenerator) GetName ¶
func (g *ResourceGenerator) GetName() string
GetName returns the name of this resource generator.
func (*ResourceGenerator) GetVersion ¶
func (g *ResourceGenerator) GetVersion() string
GetVersion returns the version of this resource generator.
type WorkflowEngine ¶
type WorkflowEngine struct {
// ResourceGen handles core resource generation
ResourceGen *ResourceGenerator
// LayoutInteg handles layout integration
LayoutInteg *LayoutIntegrator
// BootstrapGen handles bootstrap resource generation
BootstrapGen *BootstrapGenerator
}
WorkflowEngine implements the stack.Workflow interface by composing the specialized generator components. This provides a complete FluxCD workflow implementation with clear separation of concerns.
func Engine ¶
func Engine() *WorkflowEngine
Engine returns a WorkflowEngine initialized with defaults. This is the primary entry point for FluxCD workflow functionality.
Example ¶
package main
import (
"fmt"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/go-kure/kure/pkg/kubernetes"
"github.com/go-kure/kure/pkg/stack"
"github.com/go-kure/kure/pkg/stack/fluxcd"
)
// configMapApp is a minimal stack.ApplicationConfig: one ConfigMap named after
// the application.
type configMapApp struct{}
func (configMapApp) Generate(app *stack.Application) ([]*client.Object, error) {
var obj client.Object = kubernetes.CreateConfigMap(app.Name, app.Namespace)
return []*client.Object{&obj}, nil
}
// exampleCluster is the cluster the examples generate from: a root node
// "apps" whose bundle "web" holds one application.
func exampleCluster() *stack.Cluster {
cluster, err := stack.NewClusterBuilder("prod").
WithNode("apps").
WithBundle("web").
WithApplication("web", configMapApp{}).
End().
End().
Build()
if err != nil {
panic(err)
}
return cluster
}
func main() {
cluster := exampleCluster()
// Create engine with defaults. Placement is set on the LayoutRules passed
// to the layout call, not on the engine — see Layout Integration below.
engine := fluxcd.Engine()
// Generate all Flux resources for a cluster (paths of a default-rules walk)
objects, err := engine.GenerateFromCluster(cluster)
if err != nil {
panic(err)
}
for _, obj := range objects {
fmt.Println(obj.GetObjectKind().GroupVersionKind().Kind, obj.GetName())
}
}
Output: Kustomization web
func NewWorkflowEngine ¶
func NewWorkflowEngine() *WorkflowEngine
NewWorkflowEngine creates a FluxCD workflow engine with default components.
Example ¶
package main
import (
"fmt"
"github.com/go-kure/kure/pkg/stack/fluxcd"
)
func main() {
// Default engine
engine := fluxcd.Engine()
// The same, built from its components
built := fluxcd.NewWorkflowEngine()
fmt.Println(engine.GetName() == built.GetName(), built.SupportedBootstrapModes())
}
Output: true [flux-operator gotk]
func (*WorkflowEngine) CreateLayoutWithResources ¶
func (we *WorkflowEngine) CreateLayoutWithResources(c *stack.Cluster, rules stack.LayoutRulesProvider) (stack.ManifestLayoutResult, error)
CreateLayoutWithResources creates a new layout that includes Flux resources.
Example ¶
package main
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/go-kure/kure/pkg/kubernetes"
"github.com/go-kure/kure/pkg/stack"
"github.com/go-kure/kure/pkg/stack/fluxcd"
"github.com/go-kure/kure/pkg/stack/layout"
)
// configMapApp is a minimal stack.ApplicationConfig: one ConfigMap named after
// the application.
type configMapApp struct{}
func (configMapApp) Generate(app *stack.Application) ([]*client.Object, error) {
var obj client.Object = kubernetes.CreateConfigMap(app.Name, app.Namespace)
return []*client.Object{&obj}, nil
}
// exampleCluster is the cluster the examples generate from: a root node
// "apps" whose bundle "web" holds one application.
func exampleCluster() *stack.Cluster {
cluster, err := stack.NewClusterBuilder("prod").
WithNode("apps").
WithBundle("web").
WithApplication("web", configMapApp{}).
End().
End().
Build()
if err != nil {
panic(err)
}
return cluster
}
// printFiles prints every file below dir, relative to it.
func printFiles(dir string) {
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if err == nil && !d.IsDir() {
rel, _ := filepath.Rel(dir, path)
fmt.Println(rel)
}
return err
})
if err != nil {
panic(err)
}
}
func main() {
cluster := exampleCluster()
engine := fluxcd.Engine()
rules := layout.DefaultLayoutRules()
out, err := os.MkdirTemp("", "kure-fluxcd-example")
if err != nil {
panic(err)
}
defer func() { _ = os.RemoveAll(out) }()
// Create layout with Flux resources integrated
ml, err := engine.CreateLayoutWithResources(cluster, rules)
if err != nil {
panic(err)
}
// Write to disk: every spec.path is relative to <out>/clusters
err = layout.WriteManifest(out, layout.DefaultLayoutConfig(), ml.(*layout.ManifestLayout))
if err != nil {
panic(err)
}
printFiles(out)
}
Output: clusters/apps/cluster-configmap-web.yaml clusters/apps/flux-system/flux-system-kustomization-web.yaml clusters/apps/flux-system/kustomization.yaml clusters/apps/kustomization.yaml
func (*WorkflowEngine) GenerateBootstrap ¶
func (we *WorkflowEngine) GenerateBootstrap(config *stack.BootstrapConfig, rootNode *stack.Node) ([]client.Object, error)
GenerateBootstrap creates bootstrap resources for setting up Flux.
Example ¶
package main
import (
"fmt"
"github.com/go-kure/kure/pkg/stack"
"github.com/go-kure/kure/pkg/stack/fluxcd"
)
func main() {
engine := fluxcd.Engine()
rootNode := &stack.Node{Name: "prod"}
bootstrapConfig := &stack.BootstrapConfig{
Enabled: true,
FluxMode: "flux-operator", // or "gotk"; empty defaults to "flux-operator"
FluxVersion: "v2.8.2",
SourceURL: "oci://registry.example.com/fleet",
SourceRef: "latest",
}
objects, err := engine.GenerateBootstrap(bootstrapConfig, rootNode)
if err != nil {
panic(err)
}
for _, obj := range objects {
if obj.GetObjectKind().GroupVersionKind().Kind == "FluxInstance" {
fmt.Println(obj.GetNamespace(), obj.GetName())
}
}
}
Output: flux-system flux
func (*WorkflowEngine) GenerateFromCluster ¶
GenerateFromCluster creates Flux resources from a cluster definition, with the spec.path values of a default-rules walk (see ResourceGenerator.GenerateFromCluster).
func (*WorkflowEngine) GetBootstrapGenerator ¶
func (we *WorkflowEngine) GetBootstrapGenerator() *BootstrapGenerator
GetBootstrapGenerator returns the underlying bootstrap generator for advanced configuration.
func (*WorkflowEngine) GetLayoutIntegrator ¶
func (we *WorkflowEngine) GetLayoutIntegrator() *LayoutIntegrator
GetLayoutIntegrator returns the underlying layout integrator for advanced configuration.
func (*WorkflowEngine) GetName ¶
func (we *WorkflowEngine) GetName() string
GetName returns a human-readable name for this workflow engine.
func (*WorkflowEngine) GetResourceGenerator ¶
func (we *WorkflowEngine) GetResourceGenerator() *ResourceGenerator
GetResourceGenerator returns the underlying resource generator for advanced configuration.
func (*WorkflowEngine) GetVersion ¶
func (we *WorkflowEngine) GetVersion() string
GetVersion returns the version of this workflow engine.
func (*WorkflowEngine) IntegrateWithLayout ¶
func (we *WorkflowEngine) IntegrateWithLayout(ml *layout.ManifestLayout, c *stack.Cluster, rules layout.LayoutRules) error
IntegrateWithLayout adds Flux resources to an existing manifest layout.
func (*WorkflowEngine) SupportedBootstrapModes ¶
func (we *WorkflowEngine) SupportedBootstrapModes() []string
SupportedBootstrapModes returns the bootstrap modes supported by this engine.