Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
c471665
add definitions
fuskovic Oct 29, 2025
1bdd0db
remove usage of custom operator
fuskovic Oct 31, 2025
52d950e
add enums for validation
fuskovic Oct 31, 2025
83ca77f
wip
fuskovic Oct 31, 2025
6170d05
Apply suggestions from code review
fuskovic Oct 31, 2025
4d952a3
tests for parsing values as list
fuskovic Nov 4, 2025
e5b21ed
add tests for evaluteConditionSelector
fuskovic Nov 4, 2025
e1c26e0
refactor to support single value for index selector requirement
fuskovic Nov 4, 2025
25d10bd
refactor + move condition evaluation logic into its own file
fuskovic Nov 5, 2025
8f265b6
implement handleRefreshAction
fuskovic Nov 5, 2025
a4d6ba6
add comment
fuskovic Nov 5, 2025
f617813
add and propogate datastructurees for collecting results + sub-results
fuskovic Nov 5, 2025
e9dd30b
comments + logs
fuskovic Nov 5, 2025
de15b5f
move list option related code to its own file and write tests
fuskovic Nov 6, 2025
e986f90
fix lint issues
fuskovic Nov 6, 2025
a50c453
add tests for normalize
fuskovic Nov 6, 2025
725d105
make codegen
fuskovic Nov 6, 2025
cb30848
add tests for handleRefreshAction
fuskovic Nov 6, 2025
b312dea
move condition evaluation outside of select block
fuskovic Nov 6, 2025
b11e9f8
add generic handler tests covering condition eval/satisfied edgecases
fuskovic Nov 7, 2025
23d6fd2
add more tests
fuskovic Nov 7, 2025
10f0316
use zero-val for satisfied when appending condition result
fuskovic Nov 7, 2025
d3056cb
remove redundant test
fuskovic Nov 7, 2025
ef4b6e9
polish
fuskovic Nov 7, 2025
b063856
handleRefreshAction -> refreshTargets
fuskovic Nov 7, 2025
baf470e
logging improvements
fuskovic Nov 7, 2025
41a2263
add docs
fuskovic Nov 10, 2025
dd71f18
parse label selector matchExpression values for expressions
fuskovic Nov 10, 2025
df59b1f
add name field to GenericWebhookTarget
fuskovic Nov 10, 2025
c8e8542
handle supplied name in refreshTargets
fuskovic Nov 10, 2025
a101b59
add hook to validate generic webhook target configuration
fuskovic Nov 10, 2025
63d3f3f
remove debug log
fuskovic Nov 10, 2025
ca90cbe
rename loop variable
fuskovic Nov 10, 2025
097f65b
gen api docs
fuskovic Nov 10, 2025
2308bf1
Merge branch 'main' into fuskovic/gwhr
fuskovic Nov 12, 2025
9ca00be
use new registry
fuskovic Nov 12, 2025
c6f1ca1
move normalize to expression/functions
fuskovic Nov 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,322 changes: 1,882 additions & 440 deletions api/v1alpha1/generated.pb.go

Large diffs are not rendered by default.

115 changes: 115 additions & 0 deletions api/v1alpha1/generated.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

138 changes: 138 additions & 0 deletions api/v1alpha1/project_config_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ type WebhookReceiverConfig struct {
// Gitea contains the configuration for a webhook receiver that is compatible
// with Gitea payloads.
Gitea *GiteaWebhookReceiverConfig `json:"gitea,omitempty" protobuf:"bytes,7,opt,name=gitea"`
// Generic contains the configuration for a generic webhook receiver.
Generic *GenericWebhookReceiverConfig `json:"generic,omitempty" protobuf:"bytes,11,opt,name=generic"`
}

// GiteaWebhookReceiverConfig describes a webhook receiver that is compatible
Expand Down Expand Up @@ -342,6 +344,142 @@ type AzureWebhookReceiverConfig struct {
SecretRef corev1.LocalObjectReference `json:"secretRef" protobuf:"bytes,1,opt,name=secretRef"`
}

// GenericWebhookReceiverConfig describes a generic webhook receiver that can be
// configured to respond to any arbitrary POST by applying user-defined actions
// user-defined sets of resources selected by labels and/or pre-built indices.
Copy link
Member

@krancour krancour Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// user-defined sets of resources selected by labels and/or pre-built indices.
// on user-defined sets of resources selected by name, labels, and/or values in pre-built indices.

// Both types of selectors support using values extracted from the request by
// means of expressions. Currently refreshing resources is the only supported
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// means of expressions. Currently refreshing resources is the only supported
// means of expressions. Currently, refreshing resources is the only supported

// action and Warehouse is the only supported kind. "Refreshing" means
// immediately enqueuing the target resource for immediate reconciliation by its
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can guarantee immediate enqueue... we can't guarantee there isn't a bunch of stuff ahead of it, so the actual reconciliation may not be immediate.

Suggested change
// immediately enqueuing the target resource for immediate reconciliation by its
// immediately enqueuing the target resource for reconciliation by its

// controller. The practical effect of refreshing a Warehouses is triggering its
// artifact discovery process.
type GenericWebhookReceiverConfig struct {
// SecretRef contains a reference to a Secret. For Project-scoped webhook
// receivers, the referenced Secret must be in the same namespace as the
// ProjectConfig.
//
// For cluster-scoped webhook receivers, the referenced Secret must be in the
// designated "cluster Secrets" namespace.
//
// The Secret's data map is expected to contain a `secret` key whose value
// does NOT need to be shared directly with the sender. It is used only by
// Kargo to create a complex, hard-to-guess URL, which implicitly serves as a
// shared secret.
//
// +kubebuilder:validation:Required
SecretRef corev1.LocalObjectReference `json:"secretRef" protobuf:"bytes,1,opt,name=secretRef"`

// Actions is a list of actions to be performed when a webhook event is received.
//
// +kubebuilder:validation:MinItems=1
Actions []GenericWebhookAction `json:"actions,omitempty" protobuf:"bytes,2,rep,name=actions"`
}

// GenericWebhookAction describes an action to be performed on a resource
// and the conditions under which it should be performed.
type GenericWebhookAction struct {
// Name is the name of the action to be performed.
//
// +kubebuilder:validation:Enum=Refresh;
Name GenericWebhookActionName `json:"action" protobuf:"bytes,1,opt,name=action"`

// MatchExpression is the validation criteria that must be met for the action to
// be performed.
Comment on lines +386 to +387
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// MatchExpression is the validation criteria that must be met for the action to
// be performed.
// MatchExpression defines criteria that a request must meet to trigger this
// action.

Also, since this is marked as optional, I think you need to also describe what the behavior is when it is unspecified. Match everything?

//
// +optional
MatchExpression string `json:"matchExpression,omitempty" protobuf:"bytes,2,opt,name=matchExpression"`

// Parameters contains additional parameters for the action.
//
// +optional
Parameters map[string]string `json:"parameters,omitempty" protobuf:"bytes,3,rep,name=parameters" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`

// Targets is a list of selection criteria for the resources on which the
// action should be performed.
//
// +kubebuilder:validation:MinItems=1
Targets []GenericWebhookTarget `json:"targets,omitempty" protobuf:"bytes,4,rep,name=targets"`
}

// GenericWebhookActionName represents the name of an action to be performed on a resource.
type GenericWebhookActionName string

const (
// GenericWebhookActionNameRefresh indicates a request to refresh the resource.
GenericWebhookActionNameRefresh GenericWebhookActionName = "Refresh"
)

// GenericWebhookTarget describes selection criteria for resources to which some
// action is to be applied.
type GenericWebhookTarget struct {
// Kind is the kind of the target resource.
//
// +kubebuilder:validation:Enum=Warehouse;
Kind GenericWebhookTargetKind `json:"kind" protobuf:"bytes,1,opt,name=kind"`

// Name is the name of the target resource.
//
// +optional
Name string `json:"name,omitempty" protobuf:"bytes,2,opt,name=name"`

// LabelSelector is a label selector to identify the target resources.
// If used with IndexSelector, the results are the combined (logical AND) of the two criteria.
//
// +optional
LabelSelector metav1.LabelSelector `json:"labelSelector,omitempty" protobuf:"bytes,3,opt,name=labelSelector"`

// IndexSelector is a selector used to identify cached target resources by cache key.
// If used with LabelSelector, the results are the combined (logical AND) of the two criteria.
//
// +optional
IndexSelector IndexSelector `json:"indexSelector,omitempty" protobuf:"bytes,4,opt,name=indexSelector"`
Comment on lines +420 to +435
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For LabelSelector and IndexSelector, we've documented what the behavior is if they are used in combination with one another, but we haven't documented the behavior if they are used in combination with name.

}

// GenericWebhookTargetKind represents the kind of a target resource.
type GenericWebhookTargetKind string

const (
GenericWebhookTargetKindWarehouse GenericWebhookTargetKind = "Warehouse"
)

// IndexSelector encapsulates a selector used to derive index keys
// based on expressions.
type IndexSelector struct {
// MatchExpressions is a list of index selector requirements.
//
// +kubebuilder:validation:MinItems=1
MatchExpressions []IndexSelectorRequirement `json:"matchExpressions,omitempty" protobuf:"bytes,1,rep,name=matchExpressions"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this what we had in the spec? The field name feels quite wrong, as the selector may actually have nothing at all to do with an expression and simply use static values for all of key, operator, and value.

}

// IndexSelectorRequirement encapsulates a requirement used to select indexes
// based on specific criteria.
type IndexSelectorRequirement struct {
// Key is the key of the index.
//
// +kubebuilder:validation:Enum=subscribedURLs;receiverPaths
Key string `json:"key" protobuf:"bytes,1,opt,name=key"`

// Operator indicates the operation that should be used to evaluate
// whether the selection requirement is satisfied.
//
// kubebuilder:validation:Enum=Equal;NotEqual;
Operator IndexSelectorRequirementOperator `json:"operator" protobuf:"bytes,2,opt,name=operator"`

// Values is a list of values or a single value returned from an expression.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has to be an expression?

//
// kubebuilder:validation:Required
Value string `json:"value" protobuf:"bytes,3,opt,name=value"`
}

// IndexSelectorRequirementOperator represents a set of operators that can be
// used in an index selector requirement.
type IndexSelectorRequirementOperator string

const (
IndexSelectorRequirementOperatorEqual IndexSelectorRequirementOperator = "Equal"
IndexSelectorRequirementOperatorNotEqual IndexSelectorRequirementOperator = "NotEqual"
)

// WebhookReceiverDetails encapsulates the details of a webhook receiver.
type WebhookReceiverDetails struct {
// Name is the name of the webhook receiver.
Expand Down
Loading