Skip to content

Commit

Permalink
chore: adds godoc to Feature builder (#1013)
Browse files Browse the repository at this point in the history
As the first step of improving Feature DSL this PR brings godoc explaining purpose of each method in the builder chain.
  • Loading branch information
bartoszmajsak committed May 16, 2024
1 parent e26100e commit 5ce6306
Showing 1 changed file with 64 additions and 42 deletions.
106 changes: 64 additions & 42 deletions pkg/feature/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type featureBuilder struct {
managed bool
}

// CreateFeature creates a new feature builder with the given name.
func CreateFeature(name string) *usingFeaturesHandler { //nolint:golint,revive //No need to export featureBuilder.
return &usingFeaturesHandler{
name: name,
Expand All @@ -38,6 +39,7 @@ type usingFeaturesHandler struct {
name string
}

// For sets the associated FeaturesHandler for the feature which will serve as entry point managing all the related features.
func (u *usingFeaturesHandler) For(featuresHandler *FeaturesHandler) *featureBuilder {
createSpec := func(f *Feature) error {
f.Spec = &Spec{
Expand All @@ -63,28 +65,6 @@ func (u *usingFeaturesHandler) For(featuresHandler *FeaturesHandler) *featureBui
return fb
}

func (fb *featureBuilder) UsingConfig(config *rest.Config) *featureBuilder {
fb.config = config
return fb
}

func createClient(config *rest.Config) partialBuilder {
return func(f *Feature) error {
var err error

f.Client, err = client.New(config, client.Options{})
if err != nil {
return errors.WithStack(err)
}

var multiErr *multierror.Error
s := f.Client.Scheme()
multiErr = multierror.Append(multiErr, featurev1.AddToScheme(s), apiextv1.AddToScheme(s), ofapiv1alpha1.AddToScheme(s))

return multiErr.ErrorOrNil()
}
}

// Used to enforce that Manifests() is called after ManifestSource() in the chain.
type featureBuilderWithManifestSource struct {
*featureBuilder
Expand All @@ -96,6 +76,7 @@ func (fb *featureBuilder) ManifestSource(fsys fs.FS) *featureBuilderWithManifest
return &featureBuilderWithManifestSource{featureBuilder: fb}
}

// Manifests loads manifests from the provided paths.
func (fb *featureBuilderWithManifestSource) Manifests(paths ...string) *featureBuilderWithManifestSource {
fb.builders = append(fb.builders, func(f *Feature) error {
var err error
Expand All @@ -116,6 +97,24 @@ func (fb *featureBuilderWithManifestSource) Manifests(paths ...string) *featureB
return fb
}

// TargetNamespace sets the namespace in which the feature should be applied.
// If not set, the feature will be applied in the application namespace (where this operator lives).
func (fb *featureBuilder) TargetNamespace(targetNs string) *featureBuilder {
fb.targetNS = targetNs

return fb
}

// Managed marks the feature as managed by the operator. This effectively marks all resources which are part of this feature
// as those that should be updated on operator reconcile.
func (fb *featureBuilder) Managed() *featureBuilder {
fb.managed = true

return fb
}

// WithData adds data loaders to the feature. This way you can define what data should be loaded before the feature is applied.
// This can be later used in templates and when creating resources programmatically.
func (fb *featureBuilder) WithData(loader ...Action) *featureBuilder {
fb.builders = append(fb.builders, func(f *Feature) error {
f.loaders = append(f.loaders, loader...)
Expand All @@ -126,46 +125,58 @@ func (fb *featureBuilder) WithData(loader ...Action) *featureBuilder {
return fb
}

func (fb *featureBuilder) PreConditions(preconditions ...Action) *featureBuilder {
// WithResources allows to define programmatically which resources should be created when applying defined Feature.
func (fb *featureBuilder) WithResources(resources ...Action) *featureBuilder {
fb.builders = append(fb.builders, func(f *Feature) error {
f.preconditions = append(f.preconditions, preconditions...)
f.resources = resources

return nil
})

return fb
}

func (fb *featureBuilder) PostConditions(postconditions ...Action) *featureBuilder {
// PreConditions adds preconditions to the feature. Preconditions are actions that are executed before the feature is applied.
// They can be used to check if the feature can be applied by inspecting the cluster state or by executing some arbitrary checks.
// If any of the precondition fails, the feature will not be applied.
func (fb *featureBuilder) PreConditions(preconditions ...Action) *featureBuilder {
fb.builders = append(fb.builders, func(f *Feature) error {
f.postconditions = append(f.postconditions, postconditions...)
f.preconditions = append(f.preconditions, preconditions...)

return nil
})

return fb
}

func (fb *featureBuilder) OnDelete(cleanups ...Action) *featureBuilder {
// PostConditions adds postconditions to the feature. Postconditions are actions that are executed after the feature is applied.
func (fb *featureBuilder) PostConditions(postconditions ...Action) *featureBuilder {
fb.builders = append(fb.builders, func(f *Feature) error {
f.addCleanup(cleanups...)
f.postconditions = append(f.postconditions, postconditions...)

return nil
})

return fb
}

func (fb *featureBuilder) WithResources(resources ...Action) *featureBuilder {
// OnDelete allow to add cleanup hooks that are executed when the feature is going to be deleted.
// By default, all resources created by the feature are deleted when the feature is deleted, so there is no need to
// explicitly add cleanup hooks for them.
//
// This is useful when you need to perform some additional cleanup actions such as removing effects of a patch operation.
func (fb *featureBuilder) OnDelete(cleanups ...Action) *featureBuilder {
fb.builders = append(fb.builders, func(f *Feature) error {
f.resources = resources
f.addCleanup(cleanups...)

return nil
})

return fb
}

// Load creates a new Feature instance and add it to corresponding FeaturesHandler.
// The actual feature creation in the cluster is not performed here.
func (fb *featureBuilder) Load() error {
feature := newFeature(fb.name)

Expand Down Expand Up @@ -196,6 +207,29 @@ func (fb *featureBuilder) Load() error {
return nil
}

// UsingConfig allows to pass a custom rest.Config to the feature. Useful for testing.
func (fb *featureBuilder) UsingConfig(config *rest.Config) *featureBuilder {
fb.config = config
return fb
}

func createClient(config *rest.Config) partialBuilder {
return func(f *Feature) error {
var err error

f.Client, err = client.New(config, client.Options{})
if err != nil {
return errors.WithStack(err)
}

var multiErr *multierror.Error
s := f.Client.Scheme()
multiErr = multierror.Append(multiErr, featurev1.AddToScheme(s), apiextv1.AddToScheme(s), ofapiv1alpha1.AddToScheme(s))

return multiErr.ErrorOrNil()
}
}

func (fb *featureBuilder) withDefaultClient() error {
restCfg, err := config.GetConfig()
if errors.Is(err, rest.ErrNotInCluster) {
Expand All @@ -216,15 +250,3 @@ func (fb *featureBuilder) withDefaultClient() error {
fb.config = restCfg
return nil
}

func (fb *featureBuilder) TargetNamespace(targetNs string) *featureBuilder {
fb.targetNS = targetNs

return fb
}

func (fb *featureBuilder) Managed() *featureBuilder {
fb.managed = true

return fb
}

0 comments on commit 5ce6306

Please sign in to comment.