Skip to content

Commit

Permalink
Add support for preflight check configuration
Browse files Browse the repository at this point in the history
This adds configuration file support (`preflightRules`) for preflight
checks. Each preflight check must add a `SetConfig()` method to
accept configuration from the Preflight registry.

Signed-off-by: Todd Short <[email protected]>
  • Loading branch information
tmshort committed Mar 12, 2024
1 parent 939c724 commit 018ea04
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 22 deletions.
4 changes: 4 additions & 0 deletions pkg/kapp/cmd/app/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ func (o *DeployOptions) Run() error {
}

if o.PreflightChecks != nil {
err = o.PreflightChecks.SetConfig(conf)
if err != nil {
return fmt.Errorf("preflight configuration settings failed: %w", err)
}
err = o.PreflightChecks.Run(context.Background(), clusterChangesGraph)
if err != nil {
return fmt.Errorf("preflight checks failed: %w", err)
Expand Down
8 changes: 8 additions & 0 deletions pkg/kapp/config/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,14 @@ func (c Conf) TemplateRules() []TemplateRule {
return result
}

func (c Conf) PreflightRules() []PreflightRule {
var result []PreflightRule
for _, config := range c.configs {
result = append(result, config.PreflightRules...)
}
return result
}

func (c Conf) DiffMaskRules() []DiffMaskRule {
var result []DiffMaskRule
for _, config := range c.configs {
Expand Down
6 changes: 6 additions & 0 deletions pkg/kapp/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Config struct {
LabelScopingRules []LabelScopingRule
TemplateRules []TemplateRule
DiffMaskRules []DiffMaskRule
PreflightRules []PreflightRule

AdditionalLabels map[string]string
DiffAgainstLastAppliedFieldExclusionRules []DiffAgainstLastAppliedFieldExclusionRule
Expand Down Expand Up @@ -145,6 +146,11 @@ type ChangeRuleBinding struct {
ResourceMatchers []ResourceMatcher
}

type PreflightRule struct {
Name string
Config map[string]any
}

func NewConfigFromResource(res ctlres.Resource) (Config, error) {
if res.APIVersion() != configAPIVersion {
return Config{}, fmt.Errorf(
Expand Down
4 changes: 4 additions & 0 deletions pkg/kapp/permissions/preflight.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ func (p *Preflight) SetEnabled(enabled bool) {
p.enabled = enabled
}

func (p *Preflight) SetConfig(_ preflight.CheckConfig) error {
return nil
}

func (p *Preflight) Run(ctx context.Context, changeGraph *ctldgraph.ChangeGraph) error {
client, err := p.depsFactory.CoreClient()
if err != nil {
Expand Down
26 changes: 21 additions & 5 deletions pkg/kapp/preflight/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,34 @@ package preflight

import (
"context"

ctldgraph "github.com/vmware-tanzu/carvel-kapp/pkg/kapp/diffgraph"
)

type CheckFunc func(context.Context, *ctldgraph.ChangeGraph) error
type CheckFunc func(context.Context, *ctldgraph.ChangeGraph, CheckConfig) error
type CheckConfig map[string]any
type ConfigFunc func(CheckConfig) error

type Check interface {
Enabled() bool
SetEnabled(bool)
SetConfig(CheckConfig) error
Run(context.Context, *ctldgraph.ChangeGraph) error
}

type checkImpl struct {
enabled bool
checkFunc CheckFunc

config CheckConfig
configFunc ConfigFunc
}

func NewCheck(cf CheckFunc, enabled bool) Check {
func NewCheck(cf CheckFunc, sf ConfigFunc, enabled bool) Check {
return &checkImpl{
enabled: enabled,
checkFunc: cf,
enabled: enabled,
checkFunc: cf,
configFunc: sf,
}
}

Expand All @@ -36,6 +44,14 @@ func (cf *checkImpl) SetEnabled(enabled bool) {
cf.enabled = enabled
}

func (cf *checkImpl) SetConfig(config CheckConfig) error {
cf.config = config
if cf.configFunc != nil {
return cf.configFunc(config)
}
return nil
}

func (cf *checkImpl) Run(ctx context.Context, changeGraph *ctldgraph.ChangeGraph) error {
return cf.checkFunc(ctx, changeGraph)
return cf.checkFunc(ctx, changeGraph, cf.config)
}
66 changes: 57 additions & 9 deletions pkg/kapp/preflight/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"github.com/spf13/pflag"
"github.com/vmware-tanzu/carvel-kapp/pkg/kapp/config"
ctldgraph "github.com/vmware-tanzu/carvel-kapp/pkg/kapp/diffgraph"
)

Expand All @@ -17,6 +18,8 @@ const preflightFlag = "preflight"
// Registry is a collection of preflight checks
type Registry struct {
known map[string]Check
// Stores the enabled values from the command line
enabledFlag map[string]bool
}

// NewRegistry will return a new *Registry with the
Expand Down Expand Up @@ -59,25 +62,23 @@ func (c *Registry) Type() string {
// Returns an error if there is a problem
// parsing the preflight checks
func (c *Registry) Set(s string) error {
if c.known == nil {
if c.known == nil || c.enabledFlag == nil {
return nil
}

enabled := map[string]struct{}{}
// enable those specified
// Using enabledFlag allows multiple --preflight check flags to be specified
mappings := strings.Split(s, ",")
for _, key := range mappings {
if _, ok := c.known[key]; !ok {
return fmt.Errorf("unknown preflight check %q specified", key)
}
c.known[key].SetEnabled(true)
enabled[key] = struct{}{}
c.enabledFlag[key] = true
}
// disable unspecified validators

// enable/disabled based on validators specified
for key := range c.known {
if _, ok := enabled[key]; !ok {
c.known[key].SetEnabled(false)
}
enabled, ok := c.enabledFlag[key]
c.known[key].SetEnabled(ok && enabled)
}
return nil
}
Expand All @@ -101,9 +102,56 @@ func (c *Registry) AddCheck(name string, check Check) {
if c.known == nil {
c.known = make(map[string]Check)
}
if c.enabledFlag == nil {
c.enabledFlag = make(map[string]bool)
}
c.known[name] = check
}

// Validate the configuration provided; the rules are:
// 1. Unknown validator = error
// 2. Duplicate validator = error
func (c *Registry) validateConfig(conf config.Conf) error {
haveConfig := map[string]bool{}
for _, rule := range conf.PreflightRules() {
if _, ok := c.known[rule.Name]; !ok {
return fmt.Errorf("unknown preflight check in configuration: %q", rule.Name)
}
if _, ok := haveConfig[rule.Name]; ok {
return fmt.Errorf("duplicate preflight check in configuration: %q", rule.Name)
}
haveConfig[rule.Name] = true
}
return nil
}

func (c *Registry) SetConfig(conf config.Conf) error {
// We get the --preflight cmdline flag _before_ the configuration from the file.
// So, we need to evaluate the config that we've gotten in light of the enabledFlag
if err := c.validateConfig(conf); err != nil {
return err
}
// map the configuration by name
config := map[string]map[string]any{}
for _, rule := range conf.PreflightRules() {
config[rule.Name] = rule.Config
}
if len(c.enabledFlag) == 0 {
// no --preflight flag, so enable validators according to their presence in the config
for name, check := range c.known {
_, ok := config[name]
check.SetEnabled(ok)
}
}
for name, check := range c.known {
err := check.SetConfig(config[name])
if err != nil {
return fmt.Errorf("setting preflight config %q: %w", name, err)
}
}
return nil
}

// Run will execute any enabled preflight checks. The provided
// Context and ChangeGraph will be passed to the preflight checks
// that are being executed.
Expand Down
Loading

0 comments on commit 018ea04

Please sign in to comment.