diff --git a/go.mod b/go.mod index c0e370a..90be4c2 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.21 require ( github.com/go-logr/logr v1.4.1 + github.com/hashicorp/go-version v1.6.0 github.com/stretchr/testify v1.8.2 k8s.io/api v0.28.4 k8s.io/apimachinery v0.28.4 diff --git a/go.sum b/go.sum index fdd5f40..6a4c65b 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJY github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= diff --git a/pkg/checks/checks.go b/pkg/checks/checks.go index 9113ab7..ecf401b 100644 --- a/pkg/checks/checks.go +++ b/pkg/checks/checks.go @@ -5,20 +5,21 @@ import ( "fmt" "github.com/go-logr/logr" + versions "github.com/hashicorp/go-version" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) -func IsJobRunning(clientset kubernetes.Interface, logger logr.Logger, job *batchv1.Job, versionFn func() (int, int)) bool { - // - // status.ready is behind a feature gate 'JobReadyPods', - // which is present since 1.23, and enabled by default since Kubernetes 1.24. - // See: https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/ - // - major, minor := versionFn() - if major > 1 || major == 1 && minor >= 24 { +// status.ready is behind a feature gate 'JobReadyPods', +// which is present since 1.23, and enabled by default since Kubernetes 1.24. +// See: https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/ +var firstVersionWithJobReadyPods, _ = versions.NewVersion("v1.24") + +func IsJobRunning(clientset kubernetes.Interface, logger logr.Logger, job *batchv1.Job, versionFn func() *versions.Version) bool { + version := versionFn() + if version.GreaterThanOrEqual(firstVersionWithJobReadyPods) { return job.Status.Ready != nil && *job.Status.Ready > 0 } diff --git a/pkg/checks/checks_test.go b/pkg/checks/checks_test.go index 8d0d00b..5cb8cf4 100644 --- a/pkg/checks/checks_test.go +++ b/pkg/checks/checks_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + versions "github.com/hashicorp/go-version" "github.com/stretchr/testify/require" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" @@ -49,11 +50,15 @@ func Test__IsPodRunning(t *testing.T) { func Test__IsJobRunning(t *testing.T) { // test for versions with JobReadyPods feature gate - versions := [][2]int{{1, 24}, {1, 25}, {1, 26}, {1, 27}, {1, 28}, {1, 29}, {1, 30}, {2, 0}} + vs := []string{"v1.24", "v1.24.17", "v1.24.17-eks-c12679a", "v1.25", "v1.26", "v1.27", "v1.28", "v1.29", "v1.30", "v2.0"} - for _, v := range versions { - versionFn := func() (int, int) { return v[0], v[1] } - t.Run(fmt.Sprintf("v%d.%d, ready flag unset => false", v[0], v[1]), func(t *testing.T) { + for _, version := range vs { + versionFn := func() *versions.Version { + v, _ := versions.NewVersion(version) + return v + } + + t.Run(fmt.Sprintf("%s, ready flag unset => false", version), func(t *testing.T) { clientset := fake.NewSimpleClientset() ready := int32(0) j := &batchv1.Job{ @@ -65,7 +70,7 @@ func Test__IsJobRunning(t *testing.T) { require.False(t, IsJobRunning(clientset, klog.Background(), j, versionFn)) }) - t.Run(fmt.Sprintf("v%d.%d, ready flag set => true", v[0], v[1]), func(t *testing.T) { + t.Run(fmt.Sprintf("%s, ready flag set => true", version), func(t *testing.T) { clientset := fake.NewSimpleClientset() ready := int32(1) j := &batchv1.Job{ @@ -79,12 +84,15 @@ func Test__IsJobRunning(t *testing.T) { } // test for versions without JobReadyPods feature gate - versions = [][2]int{{1, 18}, {1, 19}, {1, 20}, {1, 21}, {1, 22}, {1, 23}} + vs = []string{"v1.18", "v1.19", "v1.20", "v1.21", "v1.22", "v1.23", "v1.23.17", "v1.23.17-eks-c12679a"} - for _, v := range versions { - versionFn := func() (int, int) { return v[0], v[1] } + for _, version := range vs { + versionFn := func() *versions.Version { + v, _ := versions.NewVersion(version) + return v + } - t.Run(fmt.Sprintf("v%d.%d, pod does not exist => false", v[0], v[1]), func(t *testing.T) { + t.Run(fmt.Sprintf("%s, pod does not exist => false", version), func(t *testing.T) { clientset := fake.NewSimpleClientset() ready := int32(1) j := &batchv1.Job{ @@ -96,7 +104,7 @@ func Test__IsJobRunning(t *testing.T) { require.False(t, IsJobRunning(clientset, klog.Background(), j, versionFn)) }) - t.Run(fmt.Sprintf("v%d.%d, pending pod exists => false", v[0], v[1]), func(t *testing.T) { + t.Run(fmt.Sprintf("%s, pending pod exists => false", version), func(t *testing.T) { jobName := "job1" clientset := fake.NewSimpleClientset([]runtime.Object{ &corev1.Pod{ @@ -118,7 +126,7 @@ func Test__IsJobRunning(t *testing.T) { require.False(t, IsJobRunning(clientset, klog.Background(), j, versionFn)) }) - t.Run(fmt.Sprintf("v%d.%d, running pod exists => true", v[0], v[1]), func(t *testing.T) { + t.Run(fmt.Sprintf("%s, running pod exists => true", version), func(t *testing.T) { jobName := "job1" clientset := fake.NewSimpleClientset([]runtime.Object{ &corev1.Pod{ diff --git a/pkg/controller/job_scheduler.go b/pkg/controller/job_scheduler.go index fd07714..acce1fa 100644 --- a/pkg/controller/job_scheduler.go +++ b/pkg/controller/job_scheduler.go @@ -4,12 +4,12 @@ import ( "context" "errors" "fmt" - "strconv" "strings" "sync" "time" "github.com/go-logr/logr" + versions "github.com/hashicorp/go-version" "github.com/renderedtext/agent-k8s-stack/pkg/agenttypes" checks "github.com/renderedtext/agent-k8s-stack/pkg/checks" "github.com/renderedtext/agent-k8s-stack/pkg/config" @@ -31,12 +31,11 @@ type JobState struct { } type JobScheduler struct { - clientset kubernetes.Interface - config *config.Config - current map[string]*JobState - mu sync.Mutex - kubernetesMajorVersion int - kubernetesMinorVersion int + clientset kubernetes.Interface + config *config.Config + current map[string]*JobState + mu sync.Mutex + kubernetesVersion *versions.Version } func NewJobScheduler(clientset kubernetes.Interface, config *config.Config) (*JobScheduler, error) { @@ -46,22 +45,17 @@ func NewJobScheduler(clientset kubernetes.Interface, config *config.Config) (*Jo } klog.InfoS("Kubernetes version", "version", version) - major, err := strconv.Atoi(version.Major) - if err != nil { - return nil, err - } - minor, err := strconv.Atoi(version.Minor) + v, err := versions.NewVersion(version.String()) if err != nil { return nil, err } return &JobScheduler{ - current: map[string]*JobState{}, - clientset: clientset, - config: config, - kubernetesMajorVersion: major, - kubernetesMinorVersion: minor, + current: map[string]*JobState{}, + clientset: clientset, + config: config, + kubernetesVersion: v, }, nil } @@ -306,11 +300,9 @@ func (s *JobScheduler) isJobRunning(logger logr.Logger, jobID string, job *batch return true } - running := checks.IsJobRunning(s.clientset, logger, job, func() (int, int) { - return s.kubernetesMajorVersion, s.kubernetesMinorVersion + return checks.IsJobRunning(s.clientset, logger, job, func() *versions.Version { + return s.kubernetesVersion }) - - return running } func (s *JobScheduler) handleInProgress(logger logr.Logger, jobID string, job *batchv1.Job) { diff --git a/pkg/controller/job_scheduler_test.go b/pkg/controller/job_scheduler_test.go index f0a81be..9e7db35 100644 --- a/pkg/controller/job_scheduler_test.go +++ b/pkg/controller/job_scheduler_test.go @@ -272,7 +272,7 @@ func newFakeClientset(t *testing.T, objects []runtime.Object) kubernetes.Interfa fakeClientset := fake.NewSimpleClientset(objects...) fakeDiscovery, ok := fakeClientset.Discovery().(*fakediscovery.FakeDiscovery) require.True(t, ok) - fakeDiscovery.FakedServerVersion = &version.Info{Major: "1", Minor: "27"} + fakeDiscovery.FakedServerVersion = &version.Info{GitVersion: "v1.27.1"} return fakeClientset }