generated from ironcore-dev/repository-template
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* classification types Signed-off-by: Artem Bortnikov <[email protected]> * aggregate controller Signed-off-by: Artem Bortnikov <[email protected]> * size controller Signed-off-by: Artem Bortnikov <[email protected]> * size controller Signed-off-by: Artem Bortnikov <[email protected]> * flags to enable controllers Signed-off-by: Artem Bortnikov <[email protected]> * update size labels on machine Signed-off-by: Artem Bortnikov <[email protected]> * chore updates for types and controllers Signed-off-by: Artem Bortnikov <[email protected]> * fix lint Signed-off-by: Artem Bortnikov <[email protected]> --------- Signed-off-by: Artem Bortnikov <[email protected]>
- Loading branch information
Showing
32 changed files
with
3,186 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package v1alpha1 | ||
|
||
import ( | ||
"github.com/pkg/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
type AggregateType string | ||
|
||
const ( | ||
AverageAggregateType AggregateType = "avg" | ||
SumAggregateType AggregateType = "sum" | ||
MinAggregateType AggregateType = "min" | ||
MaxAggregateType AggregateType = "max" | ||
CountAggregateType AggregateType = "count" | ||
) | ||
|
||
type AggregateItem struct { | ||
// SourcePath is a path in Inventory spec aggregate will be applied to | ||
// +kubebuilder:validation:Required | ||
SourcePath JSONPath `json:"sourcePath"` | ||
// TargetPath is a path in Inventory status `computed` field | ||
// +kubebuilder:validation:Required | ||
TargetPath JSONPath `json:"targetPath"` | ||
// Aggregate defines whether collection values should be aggregated | ||
// for constraint checks, in case if path defines selector for collection | ||
// +kubebuilder:validation:Optional | ||
// +kubebuilder:validation:Enum=avg;sum;min;max;count | ||
Aggregate AggregateType `json:"aggregate,omitempty"` | ||
} | ||
|
||
// AggregateSpec defines the desired state of Aggregate. | ||
type AggregateSpec struct { | ||
// Aggregates is a list of aggregates required to be computed | ||
// +kubebuilder:validation:Required | ||
// +kubebuilder:validation:MinItems=1 | ||
Aggregates []AggregateItem `json:"aggregates"` | ||
} | ||
|
||
// AggregateStatus defines the observed state of Aggregate. | ||
type AggregateStatus struct{} | ||
|
||
// +kubebuilder:object:root=true | ||
// +kubebuilder:subresource:status | ||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||
// +genclient | ||
|
||
// Aggregate is the Schema for the aggregates API. | ||
type Aggregate struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
||
Spec AggregateSpec `json:"spec,omitempty"` | ||
Status AggregateStatus `json:"status,omitempty"` | ||
} | ||
|
||
func init() { | ||
SchemeBuilder.Register(&Aggregate{}, &AggregateList{}) | ||
} | ||
|
||
// +kubebuilder:object:root=true | ||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||
|
||
// AggregateList contains a list of Aggregate. | ||
type AggregateList struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ListMeta `json:"metadata,omitempty"` | ||
Items []Aggregate `json:"items"` | ||
} | ||
|
||
func (in *Aggregate) Compute(inventory *Inventory) (interface{}, error) { | ||
resultMap := make(map[string]interface{}) | ||
|
||
for _, ai := range in.Spec.Aggregates { | ||
jp, err := ai.SourcePath.ToK8sJSONPath() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
jp.AllowMissingKeys(true) | ||
data, err := jp.FindResults(inventory) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var aggregatedValue interface{} = nil | ||
tokenizedPath := ai.TargetPath.Tokenize() | ||
|
||
dataLen := len(data) | ||
if dataLen == 0 { | ||
if err := setValueToPath(resultMap, tokenizedPath, aggregatedValue); err != nil { | ||
return nil, err | ||
} | ||
continue | ||
} | ||
if dataLen > 1 { | ||
return nil, errors.New("expected only one value collection to be returned for aggregation") | ||
} | ||
|
||
values := data[0] | ||
valuesLen := len(values) | ||
|
||
if valuesLen == 0 { | ||
if err := setValueToPath(resultMap, tokenizedPath, aggregatedValue); err != nil { | ||
return nil, err | ||
} | ||
continue | ||
} | ||
|
||
if ai.Aggregate == "" { | ||
interfacedValues := make([]interface{}, valuesLen) | ||
for i, value := range values { | ||
interfacedValues[i] = value.Interface() | ||
} | ||
aggregatedValue = interfacedValues | ||
} else { | ||
aggregatedValue, err = makeAggregate(ai.Aggregate, values) | ||
if err != nil { | ||
// If we are failing to calculate one aggregate, we can skip it | ||
// and continue to the next one | ||
continue | ||
} | ||
} | ||
|
||
if err := setValueToPath(resultMap, tokenizedPath, aggregatedValue); err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
return resultMap, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package v1alpha1 | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/util/json" | ||
) | ||
|
||
// +kubebuilder:validation:Type=object | ||
type AggregationResults struct { | ||
Object map[string]interface{} `json:"-"` | ||
} | ||
|
||
func (in *AggregationResults) MarshalJSON() ([]byte, error) { | ||
if in.Object == nil { | ||
return json.Marshal(map[string]interface{}{}) | ||
} | ||
return json.Marshal(in.Object) | ||
} | ||
|
||
func (in *AggregationResults) UnmarshalJSON(b []byte) error { | ||
stringVal := string(b) | ||
if stringVal == null { | ||
in.Object = nil | ||
return nil | ||
} | ||
if err := json.Unmarshal(b, &in.Object); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (in *AggregationResults) DeepCopyInto(out *AggregationResults) { | ||
if in == nil { | ||
out = nil | ||
} else if in.Object == nil { | ||
out.Object = nil | ||
} else { | ||
out.Object = runtime.DeepCopyJSON(in.Object) | ||
} | ||
} | ||
|
||
func (in *AggregationResults) DeepCopy() *AggregationResults { | ||
if in == nil { | ||
return nil | ||
} | ||
out := new(AggregationResults) | ||
in.DeepCopyInto(out) | ||
return out | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package v1alpha1 | ||
|
||
import ( | ||
"reflect" | ||
|
||
"github.com/pkg/errors" | ||
"k8s.io/apimachinery/pkg/api/resource" | ||
) | ||
|
||
// ConstraintSpec contains conditions of constraint that should be applied on resource. | ||
type ConstraintSpec struct { | ||
// Path is a path to the struct field constraint will be applied to | ||
// +kubebuilder:validation:Optional | ||
Path JSONPath `json:"path,omitempty"` | ||
// Aggregate defines whether collection values should be aggregated | ||
// for constraint checks, in case if path defines selector for collection | ||
// +kubebuilder:validation:Optional | ||
// +kubebuilder:validation:Enum=avg;sum;min;max;count | ||
Aggregate AggregateType `json:"agg,omitempty"` | ||
// Equal contains an exact expected value | ||
// +kubebuilder:validation:Optional | ||
Equal *ConstraintValSpec `json:"eq,omitempty"` | ||
// NotEqual contains an exact not expected value | ||
// +kubebuilder:validation:Optional | ||
NotEqual *ConstraintValSpec `json:"neq,omitempty"` | ||
// LessThan contains an highest expected value, exclusive | ||
// +kubebuilder:validation:Optional | ||
LessThan *resource.Quantity `json:"lt,omitempty"` | ||
// LessThan contains an highest expected value, inclusive | ||
// +kubebuilder:validation:Optional | ||
LessThanOrEqual *resource.Quantity `json:"lte,omitempty"` | ||
// LessThan contains an lowest expected value, exclusive | ||
// +kubebuilder:validation:Optional | ||
GreaterThan *resource.Quantity `json:"gt,omitempty"` | ||
// GreaterThanOrEqual contains an lowest expected value, inclusive | ||
// +kubebuilder:validation:Optional | ||
GreaterThanOrEqual *resource.Quantity `json:"gte,omitempty"` | ||
} | ||
|
||
func (in *ConstraintSpec) MatchSingleValue(value *reflect.Value) (bool, error) { | ||
// We do not have special constraints for nil and/or empty values | ||
// so returning false for now if the value is nil | ||
if value.Kind() == reflect.Ptr && value.IsNil() { | ||
return false, nil | ||
} | ||
|
||
if in.Equal != nil { | ||
r, err := in.Equal.Compare(value) | ||
if err != nil { | ||
return false, err | ||
} | ||
return r == 0, nil | ||
} | ||
if in.NotEqual != nil { | ||
r, err := in.NotEqual.Compare(value) | ||
if err != nil { | ||
return false, err | ||
} | ||
return r != 0, nil | ||
} | ||
|
||
matches := true | ||
q, err := valueToQuantity(value) | ||
if err != nil { | ||
return false, err | ||
} | ||
if in.GreaterThanOrEqual != nil { | ||
r := q.Cmp(*in.GreaterThanOrEqual) | ||
matches = r >= 0 | ||
} | ||
if in.GreaterThan != nil { | ||
r := q.Cmp(*in.GreaterThan) | ||
matches = matches && (r > 0) | ||
} | ||
if in.LessThanOrEqual != nil { | ||
r := q.Cmp(*in.LessThanOrEqual) | ||
matches = matches && (r <= 0) | ||
} | ||
if in.LessThan != nil { | ||
r := q.Cmp(*in.LessThan) | ||
matches = matches && (r < 0) | ||
} | ||
|
||
return matches, nil | ||
} | ||
|
||
func (in *ConstraintSpec) MatchMultipleValues(aggregateType AggregateType, values []reflect.Value) (bool, error) { | ||
if aggregateType == "" { | ||
for _, value := range values { | ||
matches, err := in.MatchSingleValue(&value) | ||
if err != nil { | ||
return false, err | ||
} | ||
if !matches { | ||
return false, nil | ||
} | ||
} | ||
return true, nil | ||
} | ||
|
||
agg, err := makeAggregate(aggregateType, values) | ||
if err != nil { | ||
return false, errors.Wrapf(err, "unable to compute aggregate %s", aggregateType) | ||
} | ||
|
||
if in.Equal != nil { | ||
return agg.Cmp(*in.Equal.Numeric) == 0, nil | ||
} | ||
if in.NotEqual != nil { | ||
return agg.Cmp(*in.NotEqual.Numeric) != 0, nil | ||
} | ||
|
||
matches := true | ||
if in.GreaterThanOrEqual != nil { | ||
r := agg.Cmp(*in.GreaterThanOrEqual) | ||
matches = r >= 0 | ||
} | ||
if in.GreaterThan != nil { | ||
r := agg.Cmp(*in.GreaterThan) | ||
matches = matches && (r > 0) | ||
} | ||
if in.LessThanOrEqual != nil { | ||
r := agg.Cmp(*in.LessThanOrEqual) | ||
matches = matches && (r <= 0) | ||
} | ||
if in.LessThan != nil { | ||
r := agg.Cmp(*in.LessThan) | ||
matches = matches && (r < 0) | ||
} | ||
|
||
return matches, nil | ||
} |
Oops, something went wrong.