Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
4d00f25
add priority to CNRs
dnorman3 Aug 11, 2025
18e4f6f
ensure backward compatibility
dnorman3 Aug 11, 2025
da244fa
rm priority
dnorman3 Aug 11, 2025
f01af16
moved filter logic to function
dnorman3 Aug 11, 2025
8c3e152
rm priority
dnorman3 Aug 11, 2025
797c689
added metrics
dnorman3 Aug 11, 2025
c491336
use max()
dnorman3 Aug 11, 2025
d58dce4
rename function
dnorman3 Aug 12, 2025
ed50518
use max()
dnorman3 Aug 12, 2025
9a1baea
add tests
dnorman3 Aug 12, 2025
ec68f28
rename to batchPriority for clarity
dnorman3 Aug 12, 2025
78f3273
make names clearer
dnorman3 Aug 12, 2025
f7ec290
update metrics name
dnorman3 Aug 12, 2025
70f3323
rm Priority
dnorman3 Aug 12, 2025
9ab7a5a
make names clearer
dnorman3 Aug 12, 2025
65cdbf9
added comments
dnorman3 Aug 12, 2025
2be3d22
fix tab
dnorman3 Aug 12, 2025
4a5d699
fix tab
dnorman3 Aug 12, 2025
63e856b
add complex test case
dnorman3 Aug 12, 2025
0839bfc
met metrics to counter and update names
dnorman3 Aug 12, 2025
4468e29
set metrics to counter
dnorman3 Aug 12, 2025
34abc20
allow negative values
dnorman3 Aug 12, 2025
b187dbd
add priority
dnorman3 Aug 12, 2025
a95dfb1
Merge branch 'master' into dnorman3/priority-system
dnorman3 Aug 12, 2025
25e602a
bumped Go version
dnorman3 Aug 12, 2025
de0b25f
Replace local getPriority func with direct field access
dnorman3 Aug 13, 2025
a34bcec
reference negavtive numbers
dnorman3 Aug 13, 2025
9104133
add value range
dnorman3 Aug 13, 2025
73ad501
rm "len(lowestPriorityBatch)" as selectLowestPriorityNodeGroups alway…
dnorman3 Aug 13, 2025
b25458a
bump k8s version
dnorman3 Aug 13, 2025
116933f
added logging
dnorman3 Aug 14, 2025
0a5ccc9
remove node groups with concurrency set to 0
dnorman3 Aug 18, 2025
a77c634
rm metrics
dnorman3 Aug 18, 2025
c8e791b
add tests
dnorman3 Aug 20, 2025
bb6e575
add NodeGroupInfo metric
dnorman3 Aug 20, 2025
091a7aa
add NodeGroupChangeStatus
dnorman3 Aug 20, 2025
1621d15
observeChanges now returns a map
dnorman3 Aug 20, 2025
10148d5
observeChages now returns map and added metric
dnorman3 Aug 20, 2025
c8730da
metrics before return statement
dnorman3 Aug 21, 2025
38bda70
use nodegroup_name
dnorman3 Aug 21, 2025
89fab8a
rm redundant statement
dnorman3 Aug 25, 2025
75e7651
version 1.10.2
dnorman3 Aug 25, 2025
457dcd1
rm redundant test
dnorman3 Aug 25, 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 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION = 1.10.1
VERSION = 1.10.2
# IMPORTANT! Update api version if a new release affects cnr
API_VERSION = 1.0.0
IMAGE = cyclops:$(VERSION)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ See [Docs](docs/README.md)

## Requirements

- [Kubernetes](https://kubernetes.io/) version 1.13+. Cyclops has been tested and deployed on 1.13+ and newer. Older versions of Kubernetes may have bugs or issues that will prevent it from functioning properly.
- [Go](https://golang.org/) version 1.13+. Newer versions of Go are highly recommended.
- [Kubernetes](https://kubernetes.io/) version 1.32+. Cyclops has been tested and deployed on 1.32+ and newer. Older versions of Kubernetes may have bugs or issues that will prevent it from functioning properly.
- [Go](https://golang.org/) version 1.23+. Newer versions of Go are highly recommended.
- Dependencies and their locked versions can be found in `go.mod` and `go.sum`.

## Building
Expand Down
6 changes: 6 additions & 0 deletions deploy/crds/atlassian.com_nodegroups_crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,12 @@ spec:
- triggerEndpoint
type: object
type: array
priority:
description: Priority controls the ordering of CNR creation for this
NodeGroup. Lower values are higher priority. -10 runs before 0;
then 10, 20, ... Value range is from -2,147,483,648 to 2,147,483,647.
format: int32
type: integer
skipInitialHealthChecks:
description: SkipInitialHealthChecks is an optional flag to skip the
initial set of node health checks before cycling begins This does
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/atlassian/v1/nodegroup_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ type NodeGroupSpec struct {

// SkipPreTerminationChecks is an optional flag to skip pre-termination checks during cycling
SkipPreTerminationChecks bool `json:"skipPreTerminationChecks,omitempty"`

// Priority controls the ordering of CNR creation for this NodeGroup.
// Lower values are higher priority. Examples: -10 runs before 0; then 10, 20, ...
Priority int32 `json:"priority,omitempty"`
}

// NodeGroupStatus defines the observed state of NodeGroup
Expand Down
38 changes: 38 additions & 0 deletions pkg/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package metrics
import (
"context"
"fmt"
"strings"
"time"

v1 "github.com/atlassian-labs/cyclops/pkg/apis/atlassian/v1"
Expand Down Expand Up @@ -43,6 +44,24 @@ var (
[]string{"phase"},
nil,
)
// NodeGroupInfo provides static information about nodegroups
NodeGroupInfo = prometheus.NewDesc(
fmt.Sprintf("%v_node_group_info", namespace),
"Static information about nodegroups in the cluster",
[]string{
"nodegroup_name",
"nodegroups_list",
"node_selector",
"concurrency",
"method",
"max_failed_cnrs",
"skip_missing_named_nodes",
"skip_initial_health_checks",
"skip_pre_termination_checks",
"priority",
},
nil,
)
)

// Register registers the custom metrics with prometheus
Expand Down Expand Up @@ -105,6 +124,7 @@ func (c cyclopsCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- CycleNodeRequestsByPhase
ch <- CycleNodeStatuses
ch <- CycleNodeStatusesByPhase
ch <- NodeGroupInfo
}

func (c cyclopsCollector) Collect(ch chan<- prometheus.Metric) {
Expand Down Expand Up @@ -172,4 +192,22 @@ func (c cyclopsCollector) Collect(ch chan<- prometheus.Metric) {
prometheus.GaugeValue,
float64(len(c.cycleNodeStatusList.Items)),
)

for _, nodegroup := range c.nodeGroupList.Items {
ch <- prometheus.MustNewConstMetric(
NodeGroupInfo,
prometheus.GaugeValue,
1.0,
nodegroup.Spec.NodeGroupName,
strings.Join(nodegroup.Spec.NodeGroupsList, ","),
nodegroup.Spec.NodeSelector.String(),
fmt.Sprintf("%d", nodegroup.Spec.CycleSettings.Concurrency),
string(nodegroup.Spec.CycleSettings.Method),
fmt.Sprintf("%d", nodegroup.Spec.MaxFailedCycleNodeRequests),
fmt.Sprintf("%t", nodegroup.Spec.ValidationOptions.SkipMissingNamedNodes),
fmt.Sprintf("%t", nodegroup.Spec.SkipInitialHealthChecks),
fmt.Sprintf("%t", nodegroup.Spec.SkipPreTerminationChecks),
fmt.Sprintf("%d", nodegroup.Spec.Priority),
)
}
}
128 changes: 128 additions & 0 deletions pkg/metrics/metrics_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package metrics

import (
"testing"

v1 "github.com/atlassian-labs/cyclops/pkg/apis/atlassian/v1"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestNodeGroupInfoMetric(t *testing.T) {
// Create a test NodeGroup
testNodeGroup := &v1.NodeGroup{
ObjectMeta: metav1.ObjectMeta{
Name: "test-ng",
},
Spec: v1.NodeGroupSpec{
NodeGroupName: "test-nodegroup",
NodeGroupsList: []string{"ng1", "ng2"},
NodeSelector: metav1.LabelSelector{
MatchLabels: map[string]string{
"role": "worker",
},
},
CycleSettings: v1.CycleSettings{
Method: "Drain",
Concurrency: 2,
},
MaxFailedCycleNodeRequests: 3,
ValidationOptions: v1.ValidationOptions{
SkipMissingNamedNodes: true,
},
SkipInitialHealthChecks: false,
SkipPreTerminationChecks: true,
Priority: 10,
},
}

// Create a test NodeGroupList
testNodeGroupList := &v1.NodeGroupList{
Items: []v1.NodeGroup{*testNodeGroup},
}

// Create a mock collector with all required fields initialized
collector := &cyclopsCollector{
nodeGroupList: testNodeGroupList,
cycleNodeRequestList: &v1.CycleNodeRequestList{Items: []v1.CycleNodeRequest{}},
cycleNodeStatusList: &v1.CycleNodeStatusList{Items: []v1.CycleNodeStatus{}},
}

// Test that the metric can be described
t.Run("Describe", func(t *testing.T) {
ch := make(chan *prometheus.Desc, 10) // Buffer size to hold all metrics
go func() {
collector.Describe(ch)
close(ch)
}()

// Read all descriptions
descriptions := make([]*prometheus.Desc, 0)
for desc := range ch {
descriptions = append(descriptions, desc)
}

// Verify we got descriptions and one contains our metric
assert.Greater(t, len(descriptions), 0)
found := false
for _, desc := range descriptions {
if desc.String() == NodeGroupInfo.String() {
found = true
break
}
}
assert.True(t, found, "NodeGroupInfo metric should be described")
})

// Test that the metric can be collected
t.Run("Collect", func(t *testing.T) {
ch := make(chan prometheus.Metric, 10) // Buffer size to hold all metrics
go func() {
collector.Collect(ch)
close(ch)
}()

// Read all metrics
metrics := make([]prometheus.Metric, 0)
for metric := range ch {
metrics = append(metrics, metric)
}

// Verify we got metrics and one is our NodeGroupInfo
assert.Greater(t, len(metrics), 0)
found := false
for _, metric := range metrics {
desc := metric.Desc()
if desc.String() == NodeGroupInfo.String() {
found = true
break
}
}
assert.True(t, found, "NodeGroupInfo metric should be collected")
})
}

func TestNodeGroupInfoLabels(t *testing.T) {
// Test that the metric definition has the correct number of labels
expectedLabels := []string{
"nodegroup_name",
"nodegroups_list",
"node_selector",
"concurrency",
"method",
"max_failed_cnrs",
"skip_missing_named_nodes",
"skip_initial_health_checks",
"skip_pre_termination_checks",
"priority",
}

// Verify we have the expected number of labels
assert.Equal(t, 10, len(expectedLabels))

assert.Contains(t, NodeGroupInfo.String(), "cyclops_node_group_info")

// Test that the metric is properly registered
assert.NotNil(t, NodeGroupInfo)
}
Loading
Loading