event

package
v1.1.1-0...-18dfa52 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 10, 2025 License: Apache-2.0 Imports: 3 Imported by: 0

README

Event Generation Behavior

The event package is designed to detect changes and generate events based on the provided rules.

Event

An Event represents a detected change and its associated rule. It contains:

  • The rule that was matched.
  • The detected change.
  • Paths of changes that matched the rule (triggered an event).

Rule

A Rule defines the conditions that trigger events based on detected changes. It specifies:

  • The rule's type.
  • The rule's path.
  • The type of change it observes.

Event Generation

The core of the event generation process is the GenerateEvents function. It evaluates the changes from the comparison tree against the provided rules and returns a list of corresponding events. A single change can match at most one rule and thus produce at most one event.

The process involves:

  • Validating the provided rules.
  • Recursively traversing through each node in the comparison tree.
  • Matching each leaf change against the provided rules and determining the best match.
  • Creating and adding events based on best matched rule.

Matching Rules

The matchRule function evaluates each change to identify the best matching rule. A change matches a rule if both the path and change type align.

For each change, the matchRule function determines the most appropriate rule (best match). A change matches a rule if both the path and change type align. If no rules match with a given change, the change is ignored. If multiple rules match, they are prioritized based on:

  1. Path Length: Longer paths are prioritized.
  2. Wildcard Count: Paths with fewer wildcards are considered more specific and are prioritized.
  3. Rule Priority: Higher priority rules are prioritized.

RulePath

To improve rule matching flexibility, the rule's path uses specific operators. While these resemble regex, they are much simpler and less powerful.

Operators

  • .: Separates path into multiple segments.
  • {}: Option block defines multiple options divided with comma for matching path segments.
  • *: Acts as a wildcard, matching any path segment.
  • @: Denotes an anchor for a path segment.
  • !: Indicates path termination, ensuring an exact match.

Simplifications

  • {a} can be simplified to a when the option block contains only one option.
  • @{a} can be simplified to @a when using an anchor with a single segment option.
  • @* can be simplified to @ when using an anchor as a wildcard.
  • Spaces within rules are ignored and can be omitted.

Reserved Characters

The characters *, @, {, }, ., ! and space ( ) are considered reserved. Rule paths containing these characters outside their intended context will be considered invalid. Therefore, change paths containing these characters will never be matched.

Matching Scenarios

Consider changes on the following paths:

  • a
  • a.b
  • a.b.c
  • a.B.c
  • a.C.c
  • A.B.c
  • a.b.c.d
  1. Normal Path:

    • Description: Normally, rule path matches change paths that begin with the same segments.
    • Example: Rule path a.b.c matches both a.b.c and a.b.c.d.
  2. Exact Match:

    • Description: For a precise match without considering extended paths, end the rule path with !.
    • Example: Rule path a.b.c! strictly matches a.b.c.
  3. Path Options:

    • Description: Option blocks in rule paths allow matching multiple potential segments.
    • Example: Rule path a.{b, B}.c can match either a.b.c or a.B.c.
  4. Wildcard Path:

    • Description: Wildcards allow matching any segment in the change path.
    • Example: Rule path a.*.c can match any of a.b.c, a.B.c, a.C.c, or a.b.c.d.
  5. Anchor Path:

    • Description: Anchors ensure that the change in an event is extracted from the anchored path segment, rather than the segment where the change was actually detected.
    • Examples:
      • Rule path a.@*.c (or its shorthand a.@.c) matches paths like a.b.c, a.B.c, a.C.c, and a.b.c.d.
      • Rule path a.@{b}.c (or its shorthand a.@b.c) specifically matches a.b.c and its extension a.b.c.d.

Documentation

Index

Constants

View Source
const (
	// PathSeparator divides a rule's path into distinct segments.
	PathSeparator = "."

	// PathTerminator is used to prevent rule path from being matched with
	// longer change paths.
	PathTerminator = "!"

	// OptionPrefix marks the beginning of an option block.
	OptionPrefix = "{"

	// OptionSuffix marks the end of an option block.
	OptionSuffix = "}"

	// OptionSeparator divides individual options within an option block.
	OptionSeparator = ","

	// Wildcard represents a flexible match, allowing any segment in the
	// change path to be matched.
	Wildcard = "*"

	// Anchor signifies that the resulting change should be derived from
	// this specific path segment, rather than the segment where the change
	// was actually detected.
	Anchor = "@"
)

RulePath operators are reserved characters with distinct meanings in rule paths. Currently, these characters are not allowed outside their intended context.

Note: Spaces are automatically stripped from rule paths upon creation.

Variables

View Source
var ModifyRules = []Rule{
	{

		Type:            Warn,
		MatchChangeType: cmp.Modify,
		MatchPath:       NewRulePath("hosts.*.mainResourcePoolPath"),
		Message:         "Changing main resource pool location will trigger recreation of all resources bound to that resource pool, such as virtual machines and data disks.",
	},
	{

		Type:            Warn,
		MatchChangeType: cmp.Delete,
		MatchPath:       NewRulePath("hosts.*.dataResourcePools.*"),
		Message:         "Removing data resource pool will destroy all the data on that location.",
	},
	{

		Type:            Warn,
		MatchChangeType: cmp.Modify,
		MatchPath:       NewRulePath("hosts.*.dataResourcePools.*.path"),
		Message:         "Changing data resource pool location will trigger recreation of all resources bound to that resource pool, such as virtual machines and data disks",
	},
	{

		Type:            Allow,
		MatchChangeType: cmp.Any,
		MatchPath:       NewRulePath("hosts.*.dataResourcePools.*"),
	},
	{

		Type:            Error,
		MatchChangeType: cmp.Any,
		MatchPath:       NewRulePath("cluster.network"),
		Message:         "Once the cluster is created, further changes to the network properties are not allowed. Such action may render the cluster unusable.",
	},
	{

		Type:            Error,
		MatchChangeType: cmp.Any,
		MatchPath:       NewRulePath("cluster.nodeTemplate"),
		Message:         "Once the cluster is created, further changes to the nodeTemplate properties are not allowed. Such action may render the cluster unusable.",
	},
	{

		Type:            Error,
		MatchChangeType: cmp.Delete,
		MatchPath:       NewRulePath("cluster.nodes.{master, worker, loadBalancer}.instances.@"),
		Message:         "To remove existing nodes run apply command with '--action scale' flag.",
	},
	{

		Type:            Error,
		MatchChangeType: cmp.Create,
		MatchPath:       NewRulePath("cluster.nodes.{master, worker, loadBalancer}.instances.@"),
		Message:         "To add new nodes run apply command with '--action scale' flag.",
	},
	{

		Type:            Error,
		MatchChangeType: cmp.Any,
		MatchPath:       NewRulePath("cluster.nodes.{master, worker, loadBalancer}.default.{cpu, ram, mainDiskSize}"),
		Message:         "Changing any default physical properties of nodes (cpu, ram, mainDiskSize) is not allowed. Such action may render the cluster unusable.",
	},
	{

		Type:            Error,
		MatchChangeType: cmp.Modify,
		MatchPath:       NewRulePath("cluster.nodes.{master, worker, loadBalancer}.instances.@.{cpu, ram, mainDiskSize}"),
		Message:         "Changing any physical properties of nodes (cpu, ram, mainDiskSize) is not allowed. Such action will recreate the node.",
	},
	{

		Type:            Error,
		MatchChangeType: cmp.Modify,
		MatchPath:       NewRulePath("cluster.nodes.{master, worker, loadBalancer}.instances.@.{ip, mac}"),
		Message:         "Changing IP or MAC address of the node is not allowed. Such action may render the cluster unusable.",
	},
	{

		Type:            Warn,
		MatchChangeType: cmp.Modify,
		MatchPath:       NewRulePath("cluster.nodes.{master, worker}.instances.*.dataDisks.*"),
		Message:         "Changing data disk properties, will recreate the disk (removing all of its content in the process).",
	},
	{

		Type:            Warn,
		MatchChangeType: cmp.Delete,
		MatchPath:       NewRulePath("cluster.nodes.{master, worker}.instances.*.dataDisks.*"),
		Message:         "One or more data disks will be removed.",
	},
	{

		Type:            Allow,
		MatchChangeType: cmp.Any,
		MatchPath:       NewRulePath("cluster.nodes.loadBalancer.forwardPorts.*"),
	},
	{

		Type:            Error,
		MatchChangeType: cmp.Any,
		MatchPath:       NewRulePath("cluster.nodes.loadBalancer.vip"),
		Message:         "Once the cluster is created, changing virtual IP (VIP) is not allowed. Such action may render the cluster unusable.",
	},
	{

		Type:            Allow,
		MatchChangeType: cmp.Any,
		MatchPath:       NewRulePath("cluster.nodes.{master, worker, loadBalancer}.instances.*"),
	},
	{

		Type:            Error,
		MatchChangeType: cmp.Any,
		MatchPath:       NewRulePath("kubernetes.version"),
		Message:         "Changing Kubernetes is allowed only when upgrading the cluster.\nTo upgrade the cluster run apply command with '--action upgrade' flag.",
	},
	{

		Type:            Allow,
		MatchChangeType: cmp.Any,
		MatchPath:       NewRulePath("addons"),
	},
	{

		Type:            Error,
		MatchChangeType: cmp.Any,
		MatchPath:       NewRulePath("@"),
		Message:         "Change is not allowed.",
	},
}
View Source
var ScaleRules = []Rule{
	{
		Type:            Allow,
		MatchChangeType: cmp.Delete,
		MatchPath:       NewRulePath("cluster.nodes.worker.instances.@"),
		ActionType:      Action_ScaleDown,
	},
	{
		Type:            Allow,
		MatchChangeType: cmp.Create,
		MatchPath:       NewRulePath("cluster.nodes.worker.instances.@"),
		ActionType:      Action_ScaleUp,
	},
	{
		Type:            Allow,
		MatchChangeType: cmp.Delete,
		MatchPath:       NewRulePath("cluster.nodes.loadBalancer.instances.@"),
		ActionType:      Action_ScaleDown,
	},
	{
		Type:            Allow,
		MatchChangeType: cmp.Create,
		MatchPath:       NewRulePath("cluster.nodes.loadBalancer.instances.@"),
		ActionType:      Action_ScaleUp,
	},
	{
		Type:            Error,
		MatchChangeType: cmp.Create,
		MatchPath:       NewRulePath("cluster.nodes.master.instances.@"),
		Message:         "Currently, control plane cannot be scaled.",
	},
	{
		Type:            Allow,
		MatchChangeType: cmp.Delete,
		MatchPath:       NewRulePath("cluster.nodes.master.instances.@"),
		Message:         "Currently, control plane cannot be scaled.",
	},

	{
		Type:            Allow,
		MatchChangeType: cmp.Create,
		MatchPath:       NewRulePath("hosts.@"),
	},
	{
		Type:            Allow,
		MatchChangeType: cmp.Delete,
		MatchPath:       NewRulePath("hosts.@"),
	},

	{
		Type:            Error,
		MatchChangeType: cmp.Any,
		MatchPath:       NewRulePath("*"),
		Message:         "Change is not allowed. Scale action allows only addition and removal of worker and load balancer nodes.",
	},
}
View Source
var UpgradeRules = []Rule{
	{
		Type:            Allow,
		MatchChangeType: cmp.Modify,
		MatchPath:       NewRulePath("kubernetes.version"),
	},

	{
		Type:            Error,
		MatchChangeType: cmp.Any,
		MatchPath:       NewRulePath("@"),
		Message:         "Change is not allowed. Upgrade action allows changing only 'kubernetes.version'.",
	},
}

Functions

func SliceContains

func SliceContains[T ~string](slice []T, key T) bool

SliceContains checks whether a slice of strings contains a given key.

Types

type ActionType

type ActionType string
const (
	Action_ScaleUp   ActionType = "scale_up"
	Action_ScaleDown ActionType = "scale_down"
)

type Event

type Event struct {
	Rule   Rule
	Change cmp.Change

	// Paths of changes that matched the rule.
	MatchedChangePaths []string
}

Event represents a detected change and its associated rule.

func GenerateEvents

func GenerateEvents(node *cmp.DiffNode, rules []Rule) ([]Event, error)

GenerateEvents evaluates the changes from the comparison tree against the provided rules and returns a list of corresponding events. Each event encapsulates a matched change and its associated rule. A single change can match at most one rule and thus produce at most one event. Note that provided rules are validated prior the event generation.

func (Event) String

func (e Event) String() string

type Events

type Events []Event

Events abstracts Event list in order to provide more advance operations on the list.

func (Events) Filter

func (events Events) Filter(filter func(event Event) bool) Events

Filter iterates over the events and returns a new slice of events containing only those events that satisfy the provided filter function (return true).

func (Events) FilterByAction

func (events Events) FilterByAction(t ActionType) Events

FilterByActionType returns a new slice containing only those events that match the provided ActionType.

func (Events) FilterByRuleType

func (events Events) FilterByRuleType(t RuleType) Events

FilterByRuleType returns a new slice containing only those events that match the provided RuleType.

type Rule

type Rule struct {
	Type RuleType

	MatchPath       RulePath
	MatchChangeType cmp.ChangeType

	// Optional fields.
	ActionType ActionType
	Message    string
}

Rule defines the conditions that trigger events based on the detected changes. It specifies the rule's type, the type and path of a change it observes. Other fields are optional and can be used for further event processing.

func (Rule) IsOfType

func (r Rule) IsOfType(t RuleType) bool

IsOfType checks if the rule's type matches the given RuleTypes after both types are normalized. This is useful for comparing RuleTypes that might not be one of the predefined constants but should be treated as if they were.

func (Rule) Validate

func (r Rule) Validate() error

Validate ensures the rule's match change type and path are valid. An error is returned if validation is not successful.

type RulePath

type RulePath struct {
	// contains filtered or unexported fields
}

RulePath represents a rule's path, broken down into individual segments. It provides utilities for matching and processing change paths.

func NewRulePath

func NewRulePath(path string) RulePath

NewRulePath constructs a new rule path from a given string path. It parses the individual segments from the path and sets path's metadata.

func (RulePath) FindAnchorPath

func (rp RulePath) FindAnchorPath(changePath string) string

FindAnchorPath extracts the portion of the change path that corresponds to the anchor in the rule path. If no anchor exists in the rule path, it returns the entire change path.

func (RulePath) IsAnchorPath

func (rp RulePath) IsAnchorPath() bool

IsAnchorPath indicates whether the rule path contains an anchor segment.

func (RulePath) IsExactPath

func (rp RulePath) IsExactPath() bool

IsExactPath indicates if the rule path is terminated, ensuring an exact match.

func (RulePath) Len

func (rp RulePath) Len() int

Len returns the number of segments in the rule path.

func (RulePath) Matches

func (rp RulePath) Matches(changePath string) bool

Matches determines if the rule path matches with a given change path by comparing each segment of the rule path with the corresponding segment of the change path.

func (RulePath) Path

func (rp RulePath) Path() string

Path returns the rule's path (without any spaces).

func (RulePath) Validate

func (rp RulePath) Validate() error

Validate ensures the rule path follows the expected format and constraints.

func (RulePath) WildcardCount

func (rp RulePath) WildcardCount() int

WildcardCount returns the number of wildcard segments in the rule path.

type RulePathSegment

type RulePathSegment struct {
	// contains filtered or unexported fields
}

RulePathSegment represents a segment within a rule path. It can be a simple string, a wildcard, an anchor, or contain multiple options.

func NewRulePathSegment

func NewRulePathSegment(path string) RulePathSegment

NewRulePathSegment constructs a new rule path segment from a given path. It parses segment options if they exist, and evaluates whether the segment is a wildcard or/and an anchor.

func (RulePathSegment) ContainsOption

func (rps RulePathSegment) ContainsOption(o string) bool

ContainsOption checks if the rule path segment contains a given option.

func (RulePathSegment) IsAnchor

func (rps RulePathSegment) IsAnchor() bool

IsAnchor checks if the rule path segment is an anchor.

func (RulePathSegment) IsWildcard

func (rps RulePathSegment) IsWildcard() bool

IsWildcard checks if the rule path segment is a wildcard.

func (RulePathSegment) Matches

func (rps RulePathSegment) Matches(changePathSeg string) bool

Matches checks if the rule path segment matches a given change path segment. A match occurs if the segments are identical, the rule path segment is a wildcard, or the change path segment matches any options in the rule segment.

func (RulePathSegment) Validate

func (rps RulePathSegment) Validate() error

Validate checks the validity of a rule path segment and returns an error if invalid.

type RuleType

type RuleType uint8

RuleType represents the priority or significance of a rule. A rule type with a higher value indicates greater importance. However, the 'Ignore' rule type is a special case and is always treated with the highest priority.

const (
	// Allow is a normal rule type used for safe operations that do not
	// require any user confirmation.
	Allow RuleType = 0

	// Warn is a rule type used for potentially dangerous operations that
	// should request user permission in order to proceed.
	Warn RuleType = 100

	// Error is a rule type that should prevent any further actions.
	Error RuleType = 200

	// Ignore is a rule type used for ignoring specific changes.
	Ignore RuleType = 255
)

func (RuleType) Normalize

func (t RuleType) Normalize() RuleType

Normalize normalizes a RuleType by mapping it to one of the known types (Allow, Warn, Error, Ignore). It returns the closest RuleType with smaller priority.

func (RuleType) String

func (t RuleType) String() string

type ValidationError

type ValidationError struct {
	// contains filtered or unexported fields
}

ValidationError represents an error that occurs during the validation of the rule related object. It contains a descriptive message about the nature of the validation error and the object that caused the error.

func NewValidationError

func NewValidationError(o any, errFmt string, errArgs ...any) ValidationError

func (ValidationError) Error

func (e ValidationError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL