mirror of
https://github.com/lukaszraczylo/jobs-manager-operator.git
synced 2026-06-10 22:59:18 +00:00
Multiple fixes (#29)
* Multiple fixes - add goreleaser to the build / release process - add kubectl plugin for job graphs visualization - add installation scripts - update dependencies * Update the release & CRD content. * Next set of improvements. Code Quality - Label constants: Added LabelWorkflowName, LabelGroupName, LabelJobName, LabelJobID in controllers/definitions.go - Removed commented debug code: Cleaned up dead code from multiple files - Removed unused dependencyTree field: Cleaned connPackage struct - Fixed snake_case variables: Changed to camelCase (runGroup, groupDep, runJob, jobDep, k8sJob) Kubernetes Best Practices - Finalizers: Implemented handleDeletion() and deleteChildJobs() for proper cleanup - Status enum validation: Added +kubebuilder:validation:Enum=pending;running;succeeded;failed;aborted - ImagePullPolicy default: Created getImagePullPolicy() helper that defaults to IfNotPresent - Resource limits support: Added Resources *corev1.ResourceRequirements to ManagedJobParameters Observability - Prometheus metrics: Created controllers/metrics.go with counters (jobs created/succeeded/failed), histogram (reconciliation duration), and gauge (active jobs) - Structured logging: Added logger field to connPackage, used context-based logging throughout Configuration - Leader election ID: Made configurable via --leader-election-id flag - Development mode: Made configurable via --dev-mode flag and LOG_LEVEL env var Performance - Dependency lookup optimization: Changed from O(n*m) to O(1) using lookup maps (jobDepMap, groupDepMap) - Reconciliation backoff: Added RequeueAfter: 30*time.Second when workflow is running Documentation & Testing - Godoc documentation: Added comprehensive comments to API types and controller - Unit tests: Added helpers_test.go with tests for all helper functions - Integration tests: Added managedjob_controller_test.go with Ginkgo/Gomega tests * Add the helm chart release. * Add reasonable test coverage.
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
package visualization
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type TreeTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func TestTreeSuite(t *testing.T) {
|
||||
suite.Run(t, new(TreeTestSuite))
|
||||
}
|
||||
|
||||
func (s *TreeTestSuite) TestNewStatusTree() {
|
||||
tree := NewStatusTree("root")
|
||||
|
||||
s.Equal("root", tree.Text())
|
||||
s.Equal("", tree.Status())
|
||||
s.Empty(tree.Items())
|
||||
}
|
||||
|
||||
func (s *TreeTestSuite) TestNewStatusTreeWithStatus() {
|
||||
tree := NewStatusTreeWithStatus("workflow", StatusRunning)
|
||||
|
||||
s.Equal("workflow", tree.Text())
|
||||
s.Equal(StatusRunning, tree.Status())
|
||||
s.Empty(tree.Items())
|
||||
}
|
||||
|
||||
func (s *TreeTestSuite) TestAdd() {
|
||||
root := NewStatusTree("root")
|
||||
child := root.Add("child")
|
||||
|
||||
s.Equal("child", child.Text())
|
||||
s.Len(root.Items(), 1)
|
||||
s.Equal(child, root.Items()[0])
|
||||
}
|
||||
|
||||
func (s *TreeTestSuite) TestAddWithStatus() {
|
||||
root := NewStatusTree("root")
|
||||
child := root.AddWithStatus("job", StatusSucceeded)
|
||||
|
||||
s.Equal("job", child.Text())
|
||||
s.Equal(StatusSucceeded, child.Status())
|
||||
s.Len(root.Items(), 1)
|
||||
}
|
||||
|
||||
func (s *TreeTestSuite) TestChaining() {
|
||||
root := NewStatusTree("workflow")
|
||||
group := root.Add("group1")
|
||||
job := group.AddWithStatus("job1", StatusRunning)
|
||||
job.Add("depends on: init-job")
|
||||
|
||||
s.Len(root.Items(), 1)
|
||||
s.Len(root.Items()[0].Items(), 1)
|
||||
s.Len(root.Items()[0].Items()[0].Items(), 1)
|
||||
}
|
||||
|
||||
func (s *TreeTestSuite) TestItems() {
|
||||
root := NewStatusTree("root")
|
||||
root.Add("child1")
|
||||
root.Add("child2")
|
||||
root.Add("child3")
|
||||
|
||||
items := root.Items()
|
||||
s.Len(items, 3)
|
||||
s.Equal("child1", items[0].Text())
|
||||
s.Equal("child2", items[1].Text())
|
||||
s.Equal("child3", items[2].Text())
|
||||
}
|
||||
|
||||
// ==================== MATRIX TEST: TREE BUILDING ====================
|
||||
|
||||
func TestStatusTree_StatusValues(t *testing.T) {
|
||||
statuses := []string{
|
||||
StatusPending,
|
||||
StatusRunning,
|
||||
StatusSucceeded,
|
||||
StatusFailed,
|
||||
StatusAborted,
|
||||
StatusUnknown,
|
||||
}
|
||||
|
||||
for _, status := range statuses {
|
||||
t.Run(status, func(t *testing.T) {
|
||||
tree := NewStatusTreeWithStatus("node", status)
|
||||
assert.Equal(t, status, tree.Status())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusTree_DeepNesting(t *testing.T) {
|
||||
depth := 10
|
||||
root := NewStatusTree("level-0")
|
||||
current := root
|
||||
|
||||
for i := 1; i < depth; i++ {
|
||||
current = current.Add("level-" + string(rune('0'+i)))
|
||||
}
|
||||
|
||||
// Traverse back to verify
|
||||
node := root
|
||||
for i := 0; i < depth-1; i++ {
|
||||
assert.Len(t, node.Items(), 1)
|
||||
node = node.Items()[0]
|
||||
}
|
||||
assert.Empty(t, node.Items())
|
||||
}
|
||||
|
||||
func TestStatusTree_MultipleChildren(t *testing.T) {
|
||||
root := NewStatusTree("workflow")
|
||||
|
||||
// Add multiple groups
|
||||
for i := 0; i < 5; i++ {
|
||||
group := root.AddWithStatus("group"+string(rune('0'+i)), StatusPending)
|
||||
// Add jobs to each group
|
||||
for j := 0; j < 3; j++ {
|
||||
group.AddWithStatus("job"+string(rune('0'+j)), StatusPending)
|
||||
}
|
||||
}
|
||||
|
||||
assert.Len(t, root.Items(), 5)
|
||||
for _, group := range root.Items() {
|
||||
assert.Len(t, group.Items(), 3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusTree_EmptyTree(t *testing.T) {
|
||||
tree := NewStatusTree("")
|
||||
assert.Equal(t, "", tree.Text())
|
||||
assert.Equal(t, "", tree.Status())
|
||||
assert.Empty(t, tree.Items())
|
||||
}
|
||||
|
||||
func TestStatusTree_ComplexWorkflow(t *testing.T) {
|
||||
// Simulate a real workflow structure
|
||||
workflow := NewStatusTreeWithStatus("my-workflow", StatusRunning)
|
||||
|
||||
// First group - succeeded
|
||||
group1 := workflow.AddWithStatus("init-group", StatusSucceeded)
|
||||
group1.AddWithStatus("setup-database", StatusSucceeded)
|
||||
group1.AddWithStatus("setup-cache", StatusSucceeded)
|
||||
|
||||
// Second group - running
|
||||
group2 := workflow.AddWithStatus("build-group", StatusRunning)
|
||||
group2.Add("depends on group: init-group")
|
||||
build := group2.AddWithStatus("build-app", StatusRunning)
|
||||
build.Add("depends on: setup-database")
|
||||
group2.AddWithStatus("run-tests", StatusPending).Add("depends on: build-app")
|
||||
|
||||
// Third group - pending
|
||||
group3 := workflow.AddWithStatus("deploy-group", StatusPending)
|
||||
group3.Add("depends on group: build-group")
|
||||
group3.AddWithStatus("deploy-staging", StatusPending)
|
||||
group3.AddWithStatus("deploy-production", StatusPending).Add("depends on: deploy-staging")
|
||||
|
||||
assert.Len(t, workflow.Items(), 3)
|
||||
assert.Equal(t, StatusSucceeded, workflow.Items()[0].Status())
|
||||
assert.Equal(t, StatusRunning, workflow.Items()[1].Status())
|
||||
assert.Equal(t, StatusPending, workflow.Items()[2].Status())
|
||||
}
|
||||
Reference in New Issue
Block a user