From 36b110f2f473e3eab10afbdbcb1d48d55baabe69 Mon Sep 17 00:00:00 2001 From: Sergey Smolnikov Date: Mon, 11 Mar 2024 15:23:19 +0100 Subject: [PATCH 01/11] Extended scheduled job status messages --- api/environments/job_handler.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/api/environments/job_handler.go b/api/environments/job_handler.go index 36909e45..6305b732 100644 --- a/api/environments/job_handler.go +++ b/api/environments/job_handler.go @@ -481,6 +481,17 @@ func (eh EnvironmentHandler) getScheduledJobSummary(batch *radixv1.RadixBatch, j summary.Started = radixutils.FormatTime(status.StartTime) summary.Ended = radixutils.FormatTime(status.EndTime) summary.Message = status.Message + if strings.EqualFold(summary.Status, jobSchedulerModels.Failed.String()) { + subMessage := status.Reason + if status.Reason == "OOMKilled" { + subMessage = "Out of memory." + } + if len(summary.Message) == 0 { + summary.Message = subMessage + } else { + summary.Message = fmt.Sprintf("%s\n%s", subMessage, summary.Message) + } + } summary.FailedCount = status.Failed summary.Restart = status.Restart } From 1bd563d38cd02cc419e39d562bd65cd9917bf523 Mon Sep 17 00:00:00 2001 From: Sergey Smolnikov Date: Thu, 14 Mar 2024 17:26:11 +0100 Subject: [PATCH 02/11] Extended scheduled job status messages --- api/alerting/handler.go | 4 +- .../models/component_deployment.go | 21 ++++ api/environments/job_handler.go | 119 ++++++++++-------- go.mod | 11 +- go.sum | 25 +++- 5 files changed, 115 insertions(+), 65 deletions(-) diff --git a/api/alerting/handler.go b/api/alerting/handler.go index 68984c58..3a29b53f 100644 --- a/api/alerting/handler.go +++ b/api/alerting/handler.go @@ -9,11 +9,11 @@ import ( alertModels "github.com/equinor/radix-api/api/alerting/models" "github.com/equinor/radix-api/api/utils/labelselector" "github.com/equinor/radix-api/models" + "github.com/equinor/radix-common/utils" operatoralert "github.com/equinor/radix-operator/pkg/apis/alert" "github.com/equinor/radix-operator/pkg/apis/kube" radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" crdutils "github.com/equinor/radix-operator/pkg/apis/utils" - "github.com/equinor/radix-operator/pkg/apis/utils/slice" corev1 "k8s.io/api/core/v1" kubeErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -195,7 +195,7 @@ func (h *handler) validateUpdateAlertingConfig(config *alertModels.UpdateAlertin return InvalidAlertReceiverError(alert.Alert, alert.Receiver) } // Verify alert name is valid - if !slice.ContainsString(h.validAlertNames, alert.Alert) { + if !utils.ContainsString(h.validAlertNames, alert.Alert) { return InvalidAlertError(alert.Alert) } } diff --git a/api/deployments/models/component_deployment.go b/api/deployments/models/component_deployment.go index 0f875f41..41c9109c 100644 --- a/api/deployments/models/component_deployment.go +++ b/api/deployments/models/component_deployment.go @@ -282,6 +282,18 @@ type ReplicaSummary struct { // example: 2006-01-02T15:04:05Z Created string `json:"created"` + // The time at which the batch job's pod startedAt + // + // required: false + // example: 2006-01-02T15:04:05Z + StartTime string `json:"startTime,omitempty"` + + // The time at which the batch job's pod finishedAt. + // + // required: false + // example: 2006-01-02T15:04:05Z + EndTime string `json:"endTime,omitempty"` + // Container started timestamp // // required: false @@ -315,6 +327,15 @@ type ReplicaSummary struct { // example: radixdev.azurecr.io/app-server@sha256:d40cda01916ef63da3607c03785efabc56eb2fc2e0dab0726b1a843e9ded093f ImageId string `json:"imageId"` + // The index of the pod in the re-starts + PodIndex int `json:"podIndex"` + + // Exit status from the last termination of the container + ExitCode int32 `json:"exitCode"` + + // A brief CamelCase message indicating details about why the job is in this phase + Reason string `json:"reason,omitempty"` + // Resources Resource requirements for the pod // // required: false diff --git a/api/environments/job_handler.go b/api/environments/job_handler.go index 6305b732..da75f717 100644 --- a/api/environments/job_handler.go +++ b/api/environments/job_handler.go @@ -48,7 +48,7 @@ func (eh EnvironmentHandler) getJobs(ctx context.Context, appName, envName, jobC return nil, err } - return eh.getScheduledJobSummaryList(radixBatches, nil), nil + return eh.getScheduledJobSummaryList(radixBatches), nil } // GetJob Gets job by name @@ -136,11 +136,6 @@ func (eh EnvironmentHandler) getJob(ctx context.Context, appName, envName, jobCo return nil, jobNotFoundError(jobName) } - pods, err := eh.getPodsForBatchJob(ctx, appName, envName, batchName, batchJobName) - if err != nil { - return nil, err - } - var jobComponent *radixv1.RadixDeployJobComponent namespace := operatorUtils.GetEnvironmentNamespace(appName, envName) if rd, err := eh.accounts.UserAccount.RadixClient.RadixV1().RadixDeployments(namespace).Get(ctx, batch.Spec.RadixDeploymentJobRef.Name, metav1.GetOptions{}); err == nil { @@ -149,7 +144,7 @@ func (eh EnvironmentHandler) getJob(ctx context.Context, appName, envName, jobCo } } - jobSummary := eh.getScheduledJobSummary(batch, jobs[0], pods, jobComponent) + jobSummary := eh.getScheduledJobSummary(batch, jobs[0], jobComponent) return &jobSummary, nil } @@ -248,11 +243,7 @@ func (eh EnvironmentHandler) getBatch(ctx context.Context, appName, envName, job } batchSummary := eh.getScheduledBatchSummary(batch) - pods, err := eh.getPodsForBatch(ctx, appName, envName, batchName) - if err != nil { - return nil, err - } - batchSummary.JobList = eh.getScheduledJobSummaries(batch, pods) + batchSummary.JobList = eh.getScheduledJobSummaries(batch) return &batchSummary, nil } @@ -413,38 +404,34 @@ func (eh EnvironmentHandler) getScheduledJobStatus(jobStatus *jobSchedulerV1Mode } } -func (eh EnvironmentHandler) getScheduledJobSummaryList(batches []radixv1.RadixBatch, pods []corev1.Pod) (summaries []deploymentModels.ScheduledJobSummary) { +func (eh EnvironmentHandler) getScheduledJobSummaryList(batches []radixv1.RadixBatch) (summaries []deploymentModels.ScheduledJobSummary) { for _, batch := range batches { - summaries = append(summaries, eh.getScheduledJobSummaries(&batch, pods)...) + summaries = append(summaries, eh.getScheduledJobSummaries(&batch)...) } return } -func (eh EnvironmentHandler) getScheduledJobSummaries(batch *radixv1.RadixBatch, pods []corev1.Pod) (summaries []deploymentModels.ScheduledJobSummary) { +func (eh EnvironmentHandler) getScheduledJobSummaries(batch *radixv1.RadixBatch) (summaries []deploymentModels.ScheduledJobSummary) { for _, job := range batch.Spec.Jobs { - summaries = append(summaries, eh.getScheduledJobSummary(batch, job, pods, nil)) + summaries = append(summaries, eh.getScheduledJobSummary(batch, job, nil)) } - return } -func (eh EnvironmentHandler) getScheduledJobSummary(batch *radixv1.RadixBatch, job radixv1.RadixBatchJob, pods []corev1.Pod, jobComponent *radixv1.RadixDeployJobComponent) deploymentModels.ScheduledJobSummary { +func (eh EnvironmentHandler) getScheduledJobSummary(batch *radixv1.RadixBatch, job radixv1.RadixBatchJob, jobComponent *radixv1.RadixDeployJobComponent) deploymentModels.ScheduledJobSummary { var batchName string if batch.GetLabels()[kube.RadixBatchTypeLabel] == string(kube.RadixBatchTypeBatch) { batchName = batch.GetName() } - jobPods := slice.FindAll(pods, func(pod corev1.Pod) bool { - return isPodForBatchJob(&pod, batch.Spec.RadixDeploymentJobRef.Job, batch.GetName(), job.Name) - }) summary := deploymentModels.ScheduledJobSummary{ Name: fmt.Sprintf("%s-%s", batch.GetName(), job.Name), DeploymentName: batch.Spec.RadixDeploymentJobRef.Name, BatchName: batchName, JobId: job.JobId, - ReplicaList: getReplicaSummariesForPods(jobPods), - Status: getScheduledJobStatus(job, "").String(), + ReplicaList: getReplicaSummariesForJob(batch, job), + Status: getScheduledJobStatus(job, radixv1.BatchJobPhaseWaiting).String(), } if jobComponent != nil { @@ -474,24 +461,12 @@ func (eh EnvironmentHandler) getScheduledJobSummary(batch *radixv1.RadixBatch, j } } - if statuses := slice.FindAll(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { return jobStatus.Name == job.Name }); len(statuses) == 1 { - status := statuses[0] + if status, ok := slice.FindFirst(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { return jobStatus.Name == job.Name }); ok { summary.Status = getScheduledJobStatus(job, status.Phase).String() summary.Created = radixutils.FormatTime(status.CreationTime) summary.Started = radixutils.FormatTime(status.StartTime) summary.Ended = radixutils.FormatTime(status.EndTime) summary.Message = status.Message - if strings.EqualFold(summary.Status, jobSchedulerModels.Failed.String()) { - subMessage := status.Reason - if status.Reason == "OOMKilled" { - subMessage = "Out of memory." - } - if len(summary.Message) == 0 { - summary.Message = subMessage - } else { - summary.Message = fmt.Sprintf("%s\n%s", subMessage, summary.Message) - } - } summary.FailedCount = status.Failed summary.Restart = status.Restart } @@ -499,17 +474,6 @@ func (eh EnvironmentHandler) getScheduledJobSummary(batch *radixv1.RadixBatch, j return summary } -func isPodForBatchJob(pod *corev1.Pod, jobComponentName, batchName, batchJobName string) bool { - return labels. - SelectorFromSet( - radixLabels.Merge( - radixLabels.ForComponentName(jobComponentName), - radixLabels.ForBatchName(batchName), - radixLabels.ForBatchJobName(batchJobName), - )). - Matches(labels.Set(pod.GetLabels())) -} - func getScheduledBatchStatus(batch *radixv1.RadixBatch) (status jobSchedulerModels.ProgressStatus) { status = jobSchedulerModels.Waiting switch { @@ -531,6 +495,8 @@ func getScheduledJobStatus(job radixv1.RadixBatchJob, phase radixv1.RadixBatchJo switch phase { case radixv1.BatchJobPhaseActive: + status = jobSchedulerModels.Active + case radixv1.BatchJobPhaseRunning: status = jobSchedulerModels.Running case radixv1.BatchJobPhaseSucceeded: status = jobSchedulerModels.Succeeded @@ -538,26 +504,69 @@ func getScheduledJobStatus(job radixv1.RadixBatchJob, phase radixv1.RadixBatchJo status = jobSchedulerModels.Failed case radixv1.BatchJobPhaseStopped: status = jobSchedulerModels.Stopped + case radixv1.BatchJobPhaseWaiting: + status = jobSchedulerModels.Waiting } var stop bool if job.Stop != nil { stop = *job.Stop } + if stop && (status == jobSchedulerModels.Waiting || status == jobSchedulerModels.Active || status == jobSchedulerModels.Running) { + return jobSchedulerModels.Stopping + } + return status +} - if stop && (status == jobSchedulerModels.Waiting || status == jobSchedulerModels.Running) { - status = jobSchedulerModels.Stopping +func getReplicaSummariesForJob(radixBatch *radixv1.RadixBatch, job radixv1.RadixBatchJob) []deploymentModels.ReplicaSummary { + if jobStatus, ok := slice.FindFirst(radixBatch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { + return jobStatus.Name == job.Name + }); ok { + return slice.Reduce(jobStatus.RadixBatchJobPodStatuses, make([]deploymentModels.ReplicaSummary, 0), + func(acc []deploymentModels.ReplicaSummary, status radixv1.RadixBatchJobPodStatus) []deploymentModels.ReplicaSummary { + return append(acc, getReplicaSummaryByJobPodStatus(status, job)) + }) } + return nil +} - return +func getReplicaSummaryByJobPodStatus(status radixv1.RadixBatchJobPodStatus, job radixv1.RadixBatchJob) deploymentModels.ReplicaSummary { + summary := deploymentModels.ReplicaSummary{ + Name: status.Name, + Created: radixutils.FormatTimestamp(status.CreationTime.Time), + RestartCount: status.RestartCount, + Image: status.Image, + ImageId: status.ImageID, + PodIndex: status.PodIndex, + Reason: status.Reason, + ExitCode: status.ExitCode, + Status: getReplicaStatusByJobPodStatusPhase(status.Phase), + } + if status.StartTime != nil { + summary.StartTime = radixutils.FormatTimestamp(status.StartTime.Time) + } + if status.EndTime != nil { + summary.EndTime = radixutils.FormatTimestamp(status.EndTime.Time) + } + if job.Resources != nil { + summary.Resources = deploymentModels.ConvertRadixResourceRequirements(*job.Resources) + } + return summary } -func getReplicaSummariesForPods(jobPods []corev1.Pod) []deploymentModels.ReplicaSummary { - var replicaSummaries []deploymentModels.ReplicaSummary - for _, pod := range jobPods { - replicaSummaries = append(replicaSummaries, deploymentModels.GetReplicaSummary(pod)) +func getReplicaStatusByJobPodStatusPhase(statusPhase radixv1.RadixBatchJobPodPhase) deploymentModels.ReplicaStatus { + replicaStatus := deploymentModels.ReplicaStatus{} + switch statusPhase { + case radixv1.PodFailed: + replicaStatus.Status = "Failing" + case radixv1.PodRunning: + replicaStatus.Status = "Running" + case radixv1.PodSucceeded: + replicaStatus.Status = "Terminated" + default: + replicaStatus.Status = "Pending" } - return replicaSummaries + return replicaStatus } // check if batch can be stopped diff --git a/go.mod b/go.mod index f8c0c9f6..8c77fb39 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ go 1.21 require ( github.com/cert-manager/cert-manager v1.14.2 - github.com/equinor/radix-common v1.8.0 - github.com/equinor/radix-job-scheduler v1.8.5 - github.com/equinor/radix-operator v1.50.2 + github.com/equinor/radix-common v1.9.2 + github.com/equinor/radix-job-scheduler v1.9.1-0.20240314135648-a2487d847d4c + github.com/equinor/radix-operator v1.50.3-0.20240313152237-1dc5d2699a2e github.com/evanphx/json-patch/v5 v5.7.0 github.com/go-swagger/go-swagger v0.30.5 github.com/golang-jwt/jwt/v4 v4.5.0 @@ -21,7 +21,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 github.com/tektoncd/pipeline v0.55.0 github.com/urfave/negroni/v3 v3.0.0 golang.org/x/sync v0.5.0 @@ -71,6 +71,8 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -84,6 +86,7 @@ require ( github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/prometheus/statsd_exporter v0.22.7 // indirect + github.com/rs/zerolog v1.32.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect diff --git a/go.sum b/go.sum index 9d0743dc..57e809aa 100644 --- a/go.sum +++ b/go.sum @@ -70,6 +70,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudevents/sdk-go/v2 v2.14.0 h1:Nrob4FwVgi5L4tV9lhjzZcjYqFVyJzsA56CwPaPfv6s= github.com/cloudevents/sdk-go/v2 v2.14.0/go.mod h1:xDmKfzNjM8gBvjaF8ijFjM1VYOVUEeUfapHMUX1T5To= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -82,12 +83,14 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/equinor/radix-common v1.8.0 h1:4mMu36mvJi2QSPEiKOUUJG/Z5EUrPkf0/lRO1qzkt5c= -github.com/equinor/radix-common v1.8.0/go.mod h1:8wGBEAa6auVB3yQ5pImahQzrL3w1ZYTu6N7EXLpJKUk= +github.com/equinor/radix-common v1.9.2 h1:pOYN/mSAoPe6KO/Nvudfd5DUETbLv4nLTLzFPr62ADw= +github.com/equinor/radix-common v1.9.2/go.mod h1:ekn86U68NT4ccSdt3GT+ukpiclzfuhr96a7zBJKv/jw= github.com/equinor/radix-job-scheduler v1.8.5 h1:ahw6FkFpPV167B1/w7/aQKpVMmU5Vc7UBO8411ZtYck= github.com/equinor/radix-job-scheduler v1.8.5/go.mod h1:rNIQU1eCInLV8Yl+5SRITOUU52QwYioYrIGQcsDc3kg= -github.com/equinor/radix-operator v1.50.2 h1:xa5kPUN77QT6QJq9+DJzF/ic2c7AJcl4KKztky38sdc= -github.com/equinor/radix-operator v1.50.2/go.mod h1:rl8Tbor0wvKfol67nd/p72MRh0iDTClGeQ2HcMRG/LQ= +github.com/equinor/radix-job-scheduler v1.9.1-0.20240314135648-a2487d847d4c h1:TKPscZOxBsN41EBpGtBHOaBsrYWHLT96PfhG/wyvHbk= +github.com/equinor/radix-job-scheduler v1.9.1-0.20240314135648-a2487d847d4c/go.mod h1:8220ViUF4YLOvl2z+VJQnMOaZ6jhPMfHYWdIOuUGPdo= +github.com/equinor/radix-operator v1.50.3-0.20240313152237-1dc5d2699a2e h1:lWm6hwdHJe/sZtmT2B5QijihTK2NDTKImDAytPF0vWU= +github.com/equinor/radix-operator v1.50.3-0.20240313152237-1dc5d2699a2e/go.mod h1:rl8Tbor0wvKfol67nd/p72MRh0iDTClGeQ2HcMRG/LQ= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc= @@ -127,6 +130,7 @@ github.com/go-swagger/go-swagger v0.30.5 h1:SQ2+xSonWjjoEMOV5tcOnZJVlfyUfCBhGQGA github.com/go-swagger/go-swagger v0.30.5/go.mod h1:cWUhSyCNqV7J1wkkxfr5QmbcnCewetCdvEXqgPvbc/Q= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -260,6 +264,11 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marstr/guid v1.1.0 h1:/M4H/1G4avsieL6BbUwCOBzulmoeKVP5ux/3mQNnbyI= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= @@ -335,6 +344,9 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= @@ -370,6 +382,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= @@ -553,6 +567,9 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From 169d6667e085a2869a8247cdce84a76888460fe4 Mon Sep 17 00:00:00 2001 From: Sergey Smolnikov Date: Fri, 15 Mar 2024 16:01:30 +0100 Subject: [PATCH 03/11] Extended scheduled job status messages --- .../models/component_deployment.go | 19 ++++++----- api/environments/job_handler.go | 33 +++++++++++++------ swaggerui/html/swagger.json | 29 ++++++++++++++++ 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/api/deployments/models/component_deployment.go b/api/deployments/models/component_deployment.go index 41c9109c..9e80e495 100644 --- a/api/deployments/models/component_deployment.go +++ b/api/deployments/models/component_deployment.go @@ -6,6 +6,7 @@ import ( "strings" radixutils "github.com/equinor/radix-common/utils" + "github.com/equinor/radix-common/utils/pointers" corev1 "k8s.io/api/core/v1" ) @@ -280,7 +281,7 @@ type ReplicaSummary struct { // // required: false // example: 2006-01-02T15:04:05Z - Created string `json:"created"` + Created string `json:"created,omitempty"` // The time at which the batch job's pod startedAt // @@ -303,32 +304,32 @@ type ReplicaSummary struct { // Status describes the component container status // // required: false - Status ReplicaStatus `json:"replicaStatus"` + Status ReplicaStatus `json:"replicaStatus,omitempty"` // StatusMessage provides message describing the status of a component container inside a pod // // required: false - StatusMessage string `json:"statusMessage"` + StatusMessage string `json:"statusMessage,omitempty"` // RestartCount count of restarts of a component container inside a pod // // required: false - RestartCount int32 `json:"restartCount"` + RestartCount int32 `json:"restartCount,omitempty"` // The image the container is running. // // required: false // example: radixdev.azurecr.io/app-server:cdgkg - Image string `json:"image"` + Image string `json:"image,omitempty"` // ImageID of the container's image. // // required: false // example: radixdev.azurecr.io/app-server@sha256:d40cda01916ef63da3607c03785efabc56eb2fc2e0dab0726b1a843e9ded093f - ImageId string `json:"imageId"` + ImageId string `json:"imageId,omitempty"` // The index of the pod in the re-starts - PodIndex int `json:"podIndex"` + PodIndex int `json:"podIndex,omitempty"` // Exit status from the last termination of the container ExitCode int32 `json:"exitCode"` @@ -339,7 +340,7 @@ type ReplicaSummary struct { // Resources Resource requirements for the pod // // required: false - Resources ResourceRequirements `json:"resources,omitempty"` + Resources *ResourceRequirements `json:"resources,omitempty"` } // ReplicaStatus describes the status of a component container inside a pod @@ -471,7 +472,7 @@ func GetReplicaSummary(pod corev1.Pod) ReplicaSummary { replicaSummary.Image = containerStatus.Image replicaSummary.ImageId = containerStatus.ImageID if len(pod.Spec.Containers) > 0 { - replicaSummary.Resources = ConvertResourceRequirements(pod.Spec.Containers[0].Resources) + replicaSummary.Resources = pointers.Ptr(ConvertResourceRequirements(pod.Spec.Containers[0].Resources)) } return replicaSummary } diff --git a/api/environments/job_handler.go b/api/environments/job_handler.go index da75f717..6b130fc3 100644 --- a/api/environments/job_handler.go +++ b/api/environments/job_handler.go @@ -14,6 +14,7 @@ import ( "github.com/equinor/radix-api/api/utils" radixhttp "github.com/equinor/radix-common/net/http" radixutils "github.com/equinor/radix-common/utils" + "github.com/equinor/radix-common/utils/pointers" "github.com/equinor/radix-common/utils/slice" jobsSchedulerModels "github.com/equinor/radix-job-scheduler/models" jobSchedulerModels "github.com/equinor/radix-job-scheduler/models/common" @@ -474,6 +475,17 @@ func (eh EnvironmentHandler) getScheduledJobSummary(batch *radixv1.RadixBatch, j return summary } +func isPodForBatchJob(pod *corev1.Pod, jobComponentName, batchName, batchJobName string) bool { + return labels. + SelectorFromSet( + radixLabels.Merge( + radixLabels.ForComponentName(jobComponentName), + radixLabels.ForBatchName(batchName), + radixLabels.ForBatchJobName(batchJobName), + )). + Matches(labels.Set(pod.GetLabels())) +} + func getScheduledBatchStatus(batch *radixv1.RadixBatch) (status jobSchedulerModels.ProgressStatus) { status = jobSchedulerModels.Waiting switch { @@ -532,15 +544,16 @@ func getReplicaSummariesForJob(radixBatch *radixv1.RadixBatch, job radixv1.Radix func getReplicaSummaryByJobPodStatus(status radixv1.RadixBatchJobPodStatus, job radixv1.RadixBatchJob) deploymentModels.ReplicaSummary { summary := deploymentModels.ReplicaSummary{ - Name: status.Name, - Created: radixutils.FormatTimestamp(status.CreationTime.Time), - RestartCount: status.RestartCount, - Image: status.Image, - ImageId: status.ImageID, - PodIndex: status.PodIndex, - Reason: status.Reason, - ExitCode: status.ExitCode, - Status: getReplicaStatusByJobPodStatusPhase(status.Phase), + Name: status.Name, + Created: radixutils.FormatTimestamp(status.CreationTime.Time), + RestartCount: status.RestartCount, + Image: status.Image, + ImageId: status.ImageID, + PodIndex: status.PodIndex, + Reason: status.Reason, + StatusMessage: status.Message, + ExitCode: status.ExitCode, + Status: getReplicaStatusByJobPodStatusPhase(status.Phase), } if status.StartTime != nil { summary.StartTime = radixutils.FormatTimestamp(status.StartTime.Time) @@ -549,7 +562,7 @@ func getReplicaSummaryByJobPodStatus(status radixv1.RadixBatchJobPodStatus, job summary.EndTime = radixutils.FormatTimestamp(status.EndTime.Time) } if job.Resources != nil { - summary.Resources = deploymentModels.ConvertRadixResourceRequirements(*job.Resources) + summary.Resources = pointers.Ptr(deploymentModels.ConvertRadixResourceRequirements(*job.Resources)) } return summary } diff --git a/swaggerui/html/swagger.json b/swaggerui/html/swagger.json index 821ff0d0..a1eb6c02 100644 --- a/swaggerui/html/swagger.json +++ b/swaggerui/html/swagger.json @@ -7148,6 +7148,18 @@ "x-go-name": "Created", "example": "2006-01-02T15:04:05Z" }, + "endTime": { + "description": "The time at which the batch job's pod finishedAt.", + "type": "string", + "x-go-name": "EndTime", + "example": "2006-01-02T15:04:05Z" + }, + "exitCode": { + "description": "Exit status from the last termination of the container", + "type": "integer", + "format": "int32", + "x-go-name": "ExitCode" + }, "image": { "description": "The image the container is running.", "type": "string", @@ -7166,6 +7178,17 @@ "x-go-name": "Name", "example": "server-78fc8857c4-hm76l" }, + "podIndex": { + "description": "The index of the pod in the re-starts", + "type": "integer", + "format": "int64", + "x-go-name": "PodIndex" + }, + "reason": { + "description": "A brief CamelCase message indicating details about why the job is in this phase", + "type": "string", + "x-go-name": "Reason" + }, "replicaStatus": { "$ref": "#/definitions/ReplicaStatus" }, @@ -7178,6 +7201,12 @@ "format": "int32", "x-go-name": "RestartCount" }, + "startTime": { + "description": "The time at which the batch job's pod startedAt", + "type": "string", + "x-go-name": "StartTime", + "example": "2006-01-02T15:04:05Z" + }, "statusMessage": { "description": "StatusMessage provides message describing the status of a component container inside a pod", "type": "string", From 30c8f49b1c67f403faf0c1f74bb547ca9d43b2cc Mon Sep 17 00:00:00 2001 From: Sergey Smolnikov Date: Fri, 22 Mar 2024 16:52:10 +0100 Subject: [PATCH 04/11] Extended scheduled job status messages --- api/deployments/models/scheduled_batch.go | 2 +- api/environments/environment_controller.go | 8 ++++- api/environments/environment_handler.go | 4 +-- api/environments/job_handler.go | 38 ++++++++-------------- api/pods/pod_handler.go | 32 ++++++++++++++---- go.mod | 4 +-- go.sum | 36 +++----------------- swaggerui/html/swagger.json | 7 ++++ 8 files changed, 63 insertions(+), 68 deletions(-) diff --git a/api/deployments/models/scheduled_batch.go b/api/deployments/models/scheduled_batch.go index be7ad79b..1fd05585 100644 --- a/api/deployments/models/scheduled_batch.go +++ b/api/deployments/models/scheduled_batch.go @@ -30,7 +30,7 @@ type ScheduledJobSummary struct { // Status of the job // // required: true - // enum: Running,Succeeded,Failed,Waiting,Stopping,Stopped + // enum: Running,Active,Succeeded,Failed,Waiting,Stopping,Stopped // example: Waiting Status string `json:"status"` diff --git a/api/environments/environment_controller.go b/api/environments/environment_controller.go index 4f00bf08..a20626a0 100644 --- a/api/environments/environment_controller.go +++ b/api/environments/environment_controller.go @@ -1135,6 +1135,11 @@ func (c *environmentController) GetScheduledJobLog(accounts models.Accounts, w h // description: Name of scheduled job // type: string // required: true + // - name: replicaName + // in: query + // description: Name of the job replica + // type: string + // required: false // - name: sinceTime // in: query // description: Get log only from sinceTime (example 2020-03-18T07:20:41+00:00) @@ -1173,6 +1178,7 @@ func (c *environmentController) GetScheduledJobLog(accounts models.Accounts, w h appName := mux.Vars(r)["appName"] envName := mux.Vars(r)["envName"] scheduledJobName := mux.Vars(r)["scheduledJobName"] + replicaName := r.FormValue("replicaName") since, asFile, logLines, err, _ := logs.GetLogParams(r) if err != nil { @@ -1181,7 +1187,7 @@ func (c *environmentController) GetScheduledJobLog(accounts models.Accounts, w h } eh := c.environmentHandlerFactory(accounts) - logs, err := eh.GetScheduledJobLogs(r.Context(), appName, envName, scheduledJobName, &since, logLines) + logs, err := eh.GetScheduledJobLogs(r.Context(), appName, envName, scheduledJobName, replicaName, &since, logLines) if err != nil { c.ErrorResponse(w, r, err) return diff --git a/api/environments/environment_handler.go b/api/environments/environment_handler.go index 14427ed2..9b9d674c 100644 --- a/api/environments/environment_handler.go +++ b/api/environments/environment_handler.go @@ -303,9 +303,9 @@ func (eh EnvironmentHandler) GetLogs(ctx context.Context, appName, envName, podN } // GetScheduledJobLogs handler for GetScheduledJobLogs -func (eh EnvironmentHandler) GetScheduledJobLogs(ctx context.Context, appName, envName, scheduledJobName string, sinceTime *time.Time, logLines *int64) (io.ReadCloser, error) { +func (eh EnvironmentHandler) GetScheduledJobLogs(ctx context.Context, appName, envName, scheduledJobName, replicaName string, sinceTime *time.Time, logLines *int64) (io.ReadCloser, error) { handler := pods.Init(eh.client) - return handler.HandleGetEnvironmentScheduledJobLog(ctx, appName, envName, scheduledJobName, "", sinceTime, logLines) + return handler.HandleGetEnvironmentScheduledJobLog(ctx, appName, envName, scheduledJobName, replicaName, "", sinceTime, logLines) } // GetAuxiliaryResourcePodLog handler for GetAuxiliaryResourcePodLog diff --git a/api/environments/job_handler.go b/api/environments/job_handler.go index 6b130fc3..98d3ec9a 100644 --- a/api/environments/job_handler.go +++ b/api/environments/job_handler.go @@ -432,7 +432,7 @@ func (eh EnvironmentHandler) getScheduledJobSummary(batch *radixv1.RadixBatch, j BatchName: batchName, JobId: job.JobId, ReplicaList: getReplicaSummariesForJob(batch, job), - Status: getScheduledJobStatus(job, radixv1.BatchJobPhaseWaiting).String(), + Status: jobSchedulerModels.Waiting.String(), } if jobComponent != nil { @@ -461,9 +461,8 @@ func (eh EnvironmentHandler) getScheduledJobSummary(batch *radixv1.RadixBatch, j summary.Resources = deploymentModels.ConvertRadixResourceRequirements(jobComponent.Resources) } } - if status, ok := slice.FindFirst(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { return jobStatus.Name == job.Name }); ok { - summary.Status = getScheduledJobStatus(job, status.Phase).String() + summary.Status = getScheduledJobStatus(job, status).String() summary.Created = radixutils.FormatTime(status.CreationTime) summary.Started = radixutils.FormatTime(status.StartTime) summary.Ended = radixutils.FormatTime(status.EndTime) @@ -475,37 +474,24 @@ func (eh EnvironmentHandler) getScheduledJobSummary(batch *radixv1.RadixBatch, j return summary } -func isPodForBatchJob(pod *corev1.Pod, jobComponentName, batchName, batchJobName string) bool { - return labels. - SelectorFromSet( - radixLabels.Merge( - radixLabels.ForComponentName(jobComponentName), - radixLabels.ForBatchName(batchName), - radixLabels.ForBatchJobName(batchJobName), - )). - Matches(labels.Set(pod.GetLabels())) -} - func getScheduledBatchStatus(batch *radixv1.RadixBatch) (status jobSchedulerModels.ProgressStatus) { - status = jobSchedulerModels.Waiting switch { case batch.Status.Condition.Type == radixv1.BatchConditionTypeActive: - status = jobSchedulerModels.Running + return jobSchedulerModels.Running case batch.Status.Condition.Type == radixv1.BatchConditionTypeCompleted: - status = jobSchedulerModels.Succeeded - if slice.Any(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { + if slice.All(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { return jobStatus.Phase == radixv1.BatchJobPhaseFailed }) { - status = jobSchedulerModels.Failed + return jobSchedulerModels.Failed } + return jobSchedulerModels.Succeeded } - return + return jobSchedulerModels.Waiting } -func getScheduledJobStatus(job radixv1.RadixBatchJob, phase radixv1.RadixBatchJobPhase) (status jobSchedulerModels.ProgressStatus) { +func getScheduledJobStatus(job radixv1.RadixBatchJob, jobStatus radixv1.RadixBatchJobStatus) (status jobSchedulerModels.ProgressStatus) { status = jobSchedulerModels.Waiting - - switch phase { + switch jobStatus.Phase { case radixv1.BatchJobPhaseActive: status = jobSchedulerModels.Active case radixv1.BatchJobPhaseRunning: @@ -519,7 +505,6 @@ func getScheduledJobStatus(job radixv1.RadixBatchJob, phase radixv1.RadixBatchJo case radixv1.BatchJobPhaseWaiting: status = jobSchedulerModels.Waiting } - var stop bool if job.Stop != nil { stop = *job.Stop @@ -527,6 +512,11 @@ func getScheduledJobStatus(job radixv1.RadixBatchJob, phase radixv1.RadixBatchJo if stop && (status == jobSchedulerModels.Waiting || status == jobSchedulerModels.Active || status == jobSchedulerModels.Running) { return jobSchedulerModels.Stopping } + if slice.All(jobStatus.RadixBatchJobPodStatuses, func(jobPodStatus radixv1.RadixBatchJobPodStatus) bool { + return jobPodStatus.Phase == radixv1.PodFailed + }) { + return jobSchedulerModels.Failed + } return status } diff --git a/api/pods/pod_handler.go b/api/pods/pod_handler.go index d0aab711..ca393a25 100644 --- a/api/pods/pod_handler.go +++ b/api/pods/pod_handler.go @@ -4,10 +4,12 @@ import ( "context" "fmt" "io" + "strings" "time" "github.com/equinor/radix-api/api/utils/labelselector" sortUtils "github.com/equinor/radix-api/api/utils/sort" + "github.com/equinor/radix-common/utils/slice" crdUtils "github.com/equinor/radix-operator/pkg/apis/utils" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -38,9 +40,9 @@ func (ph PodHandler) HandleGetEnvironmentPodLog(ctx context.Context, appName, en } // HandleGetEnvironmentScheduledJobLog Get logs from scheduled job in environment -func (ph PodHandler) HandleGetEnvironmentScheduledJobLog(ctx context.Context, appName, envName, scheduledJobName, containerName string, sinceTime *time.Time, logLines *int64) (io.ReadCloser, error) { +func (ph PodHandler) HandleGetEnvironmentScheduledJobLog(ctx context.Context, appName, envName, scheduledJobName, replicaName, containerName string, sinceTime *time.Time, logLines *int64) (io.ReadCloser, error) { envNs := crdUtils.GetEnvironmentNamespace(appName, envName) - return ph.getScheduledJobLog(ctx, envNs, scheduledJobName, containerName, sinceTime, logLines) + return ph.getScheduledJobLog(ctx, envNs, scheduledJobName, replicaName, containerName, sinceTime, logLines) } // HandleGetEnvironmentAuxiliaryResourcePodLog Get logs from auxiliary resource pod in environment @@ -67,7 +69,7 @@ func (ph PodHandler) getPodLog(ctx context.Context, namespace, podName, containe return ph.getPodLogFor(ctx, pod, containerName, sinceTime, logLines, previousLog) } -func (ph PodHandler) getScheduledJobLog(ctx context.Context, namespace, scheduledJobName, containerName string, sinceTime *time.Time, logLines *int64) (io.ReadCloser, error) { +func (ph PodHandler) getScheduledJobLog(ctx context.Context, namespace, scheduledJobName, replicaName, containerName string, sinceTime *time.Time, logLines *int64) (io.ReadCloser, error) { pods, err := ph.client.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("job-name=%s", scheduledJobName), }) @@ -77,10 +79,28 @@ func (ph PodHandler) getScheduledJobLog(ctx context.Context, namespace, schedule if len(pods.Items) == 0 { return nil, PodNotFoundError(scheduledJobName) } + if pod, ok := getPod(pods, replicaName); ok { + return ph.getPodLogFor(ctx, pod, containerName, sinceTime, logLines, false) + } + podNameForError := scheduledJobName + if len(replicaName) > 0 { + podNameForError = fmt.Sprintf("%s/%s", podNameForError, replicaName) + } + return nil, PodNotFoundError(podNameForError) +} - sortUtils.Pods(pods.Items, sortUtils.ByPodCreationTimestamp, sortUtils.Descending) - pod := &pods.Items[0] - return ph.getPodLogFor(ctx, pod, containerName, sinceTime, logLines, false) +func getPod(pods *corev1.PodList, replicaName string) (*corev1.Pod, bool) { + if len(pods.Items) == 0 { + return nil, false + } + if len(replicaName) == 0 { + sortUtils.Pods(pods.Items, sortUtils.ByPodCreationTimestamp, sortUtils.Descending) + return &pods.Items[0], true + } + pod, ok := slice.FindFirst(pods.Items, func(pod corev1.Pod) bool { + return strings.EqualFold(pod.GetName(), replicaName) + }) + return &pod, ok } func (ph PodHandler) getPodLogFor(ctx context.Context, pod *corev1.Pod, containerName string, sinceTime *time.Time, logLines *int64, previousLog bool) (io.ReadCloser, error) { diff --git a/go.mod b/go.mod index 174428db..ca8b1f85 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.21 require ( github.com/cert-manager/cert-manager v1.14.2 github.com/equinor/radix-common v1.9.2 - github.com/equinor/radix-job-scheduler v1.9.0 - github.com/equinor/radix-operator v1.50.2 + github.com/equinor/radix-job-scheduler v1.9.1-0.20240314135648-a2487d847d4c + github.com/equinor/radix-operator v1.50.3-0.20240313152237-1dc5d2699a2e github.com/evanphx/json-patch/v5 v5.7.0 github.com/felixge/httpsnoop v1.0.4 github.com/go-swagger/go-swagger v0.30.5 diff --git a/go.sum b/go.sum index 52e4f88b..094b2e20 100644 --- a/go.sum +++ b/go.sum @@ -61,8 +61,6 @@ github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMr github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cert-manager/cert-manager v1.14.2 h1:C/uci6yxiCRO04PWomBbSX+T4JT58FIIpDj5SZ6Ks6I= github.com/cert-manager/cert-manager v1.14.2/go.mod h1:pik7K6jXfgh++lfVJ/i1HzEnDluSUtTVLXSHikj8Lho= -github.com/cert-manager/cert-manager v1.14.2 h1:C/uci6yxiCRO04PWomBbSX+T4JT58FIIpDj5SZ6Ks6I= -github.com/cert-manager/cert-manager v1.14.2/go.mod h1:pik7K6jXfgh++lfVJ/i1HzEnDluSUtTVLXSHikj8Lho= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -91,10 +89,10 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/equinor/radix-common v1.9.2 h1:pOYN/mSAoPe6KO/Nvudfd5DUETbLv4nLTLzFPr62ADw= github.com/equinor/radix-common v1.9.2/go.mod h1:ekn86U68NT4ccSdt3GT+ukpiclzfuhr96a7zBJKv/jw= -github.com/equinor/radix-job-scheduler v1.9.0 h1:ceq46IZPf0VfPjYr6XSRy8WPid8fn5JR0bMovD/dAJ8= -github.com/equinor/radix-job-scheduler v1.9.0/go.mod h1:8220ViUF4YLOvl2z+VJQnMOaZ6jhPMfHYWdIOuUGPdo= -github.com/equinor/radix-operator v1.50.2 h1:xa5kPUN77QT6QJq9+DJzF/ic2c7AJcl4KKztky38sdc= -github.com/equinor/radix-operator v1.50.2/go.mod h1:rl8Tbor0wvKfol67nd/p72MRh0iDTClGeQ2HcMRG/LQ= +github.com/equinor/radix-job-scheduler v1.9.1-0.20240314135648-a2487d847d4c h1:TKPscZOxBsN41EBpGtBHOaBsrYWHLT96PfhG/wyvHbk= +github.com/equinor/radix-job-scheduler v1.9.1-0.20240314135648-a2487d847d4c/go.mod h1:8220ViUF4YLOvl2z+VJQnMOaZ6jhPMfHYWdIOuUGPdo= +github.com/equinor/radix-operator v1.50.3-0.20240313152237-1dc5d2699a2e h1:lWm6hwdHJe/sZtmT2B5QijihTK2NDTKImDAytPF0vWU= +github.com/equinor/radix-operator v1.50.3-0.20240313152237-1dc5d2699a2e/go.mod h1:rl8Tbor0wvKfol67nd/p72MRh0iDTClGeQ2HcMRG/LQ= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc= @@ -234,8 +232,6 @@ github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWS github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 h1:6UKoz5ujsI55KNpsJH3UwCq3T8kKbZwNZBNPuTTje8U= github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 h1:6UKoz5ujsI55KNpsJH3UwCq3T8kKbZwNZBNPuTTje8U= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -247,13 +243,9 @@ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iP github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= -github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -368,8 +360,6 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= @@ -397,8 +387,6 @@ github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= -github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= -github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -471,8 +459,6 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= -golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= -golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -745,12 +731,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1: google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= -google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM= -google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -782,8 +762,6 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -828,10 +806,6 @@ k8s.io/kube-openapi v0.0.0-20240103051144-eec4567ac022 h1:avRdiaB03v88Mfvum2S3BB k8s.io/kube-openapi v0.0.0-20240103051144-eec4567ac022/go.mod h1:sIV51WBTkZrlGOJMCDZDA1IaPBUDTulPpD4y7oe038k= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -k8s.io/kube-openapi v0.0.0-20240103051144-eec4567ac022 h1:avRdiaB03v88Mfvum2S3BBwkNuTlmuar4LlfO9Hajko= -k8s.io/kube-openapi v0.0.0-20240103051144-eec4567ac022/go.mod h1:sIV51WBTkZrlGOJMCDZDA1IaPBUDTulPpD4y7oe038k= -k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= -k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= knative.dev/pkg v0.0.0-20231219072704-d513e487961e h1:br9VUyN8M4ZUaWsmKifLg5lIAy6JmNw2MdeHd6wgp9g= knative.dev/pkg v0.0.0-20231219072704-d513e487961e/go.mod h1:YWJGsIxySXQehfkslagVEpJJwHgSScUc21+KpEgBXcY= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= @@ -841,8 +815,6 @@ sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigw sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= sigs.k8s.io/gateway-api v1.0.0 h1:iPTStSv41+d9p0xFydll6d7f7MOBGuqXM6p2/zVYMAs= sigs.k8s.io/gateway-api v1.0.0/go.mod h1:4cUgr0Lnp5FZ0Cdq8FdRwCvpiWws7LVhLHGIudLlf4c= -sigs.k8s.io/gateway-api v1.0.0 h1:iPTStSv41+d9p0xFydll6d7f7MOBGuqXM6p2/zVYMAs= -sigs.k8s.io/gateway-api v1.0.0/go.mod h1:4cUgr0Lnp5FZ0Cdq8FdRwCvpiWws7LVhLHGIudLlf4c= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/secrets-store-csi-driver v1.4.0 h1:R9JVcKOs11fEuiOLlH1BWMeyb6WYzvElRVkq1BWJkr4= diff --git a/swaggerui/html/swagger.json b/swaggerui/html/swagger.json index a1eb6c02..b1cb9671 100644 --- a/swaggerui/html/swagger.json +++ b/swaggerui/html/swagger.json @@ -3546,6 +3546,12 @@ "in": "path", "required": true }, + { + "type": "string", + "description": "Name of the job replica", + "name": "replicaName", + "in": "query" + }, { "type": "string", "format": "date-time", @@ -7438,6 +7444,7 @@ "type": "string", "enum": [ "Running", + "Active", "Succeeded", "Failed", "Waiting", From 198286cdda7ff70bd0c7abe8b1a6bc45eb0b79b3 Mon Sep 17 00:00:00 2001 From: Sergey Smolnikov Date: Mon, 25 Mar 2024 16:37:03 +0100 Subject: [PATCH 05/11] Extended scheduled job and pods status messages with event message --- api/deployments/component_handler.go | 24 +++++---- .../models/component_deployment.go | 11 ++-- api/deployments/models/deployment_builder.go | 5 +- .../environment_controller_test.go | 51 ++++++++++++++++--- api/environments/environment_handler.go | 6 ++- api/environments/job_handler.go | 4 +- api/kubequery/event_test.go | 28 ++++++++++ api/kubequery/events.go | 20 ++++++++ api/models/auxiliary_resource.go | 12 ++--- api/models/component.go | 21 ++++---- api/models/deployment.go | 8 +-- api/models/environment.go | 7 ++- api/models/replica_summary.go | 11 ++-- api/utils/event/event.go | 24 +++++++++ swaggerui/html/swagger.json | 3 +- 15 files changed, 180 insertions(+), 55 deletions(-) create mode 100644 api/kubequery/event_test.go create mode 100644 api/kubequery/events.go create mode 100644 api/utils/event/event.go diff --git a/api/deployments/component_handler.go b/api/deployments/component_handler.go index 8a298d62..5be9b9c1 100644 --- a/api/deployments/component_handler.go +++ b/api/deployments/component_handler.go @@ -5,9 +5,12 @@ import ( "strings" deploymentModels "github.com/equinor/radix-api/api/deployments/models" + "github.com/equinor/radix-api/api/kubequery" "github.com/equinor/radix-api/api/utils" + "github.com/equinor/radix-api/api/utils/event" "github.com/equinor/radix-api/api/utils/labelselector" radixutils "github.com/equinor/radix-common/utils" + "github.com/equinor/radix-common/utils/slice" "github.com/equinor/radix-operator/pkg/apis/defaults" "github.com/equinor/radix-operator/pkg/apis/deployment" "github.com/equinor/radix-operator/pkg/apis/kube" @@ -169,7 +172,12 @@ func GetComponentStateFromSpec( } componentPodNames = getPodNames(componentPods) environmentVariables = getRadixEnvironmentVariables(componentPods) - replicaSummaryList = getReplicaSummaryList(componentPods) + eventList, err := kubequery.GetEventsForEnvironment(ctx, kubeClient, appName, deployment.Environment) + if err != nil { + return nil, err + } + lastEventWarnings := event.ConvertToEventWarnings(eventList) + replicaSummaryList = getReplicaSummaryList(componentPods, lastEventWarnings) auxResource, err = getAuxiliaryResources(ctx, kubeClient, appName, component, envNs) if err != nil { return nil, err @@ -305,14 +313,10 @@ func getRadixEnvironmentVariables(pods []corev1.Pod) map[string]string { return radixEnvironmentVariables } -func getReplicaSummaryList(pods []corev1.Pod) []deploymentModels.ReplicaSummary { - replicaSummaryList := make([]deploymentModels.ReplicaSummary, 0, len(pods)) - - for _, pod := range pods { - replicaSummaryList = append(replicaSummaryList, deploymentModels.GetReplicaSummary(pod)) - } - - return replicaSummaryList +func getReplicaSummaryList(pods []corev1.Pod, lastEventWarnings event.LastEventWarnings) []deploymentModels.ReplicaSummary { + return slice.Map(pods, func(pod corev1.Pod) deploymentModels.ReplicaSummary { + return deploymentModels.GetReplicaSummary(pod, lastEventWarnings[pod.GetName()]) + }) } func getAuxiliaryResources(ctx context.Context, kubeClient kubernetes.Interface, appName string, component v1.RadixCommonDeployComponent, envNamespace string) (auxResource deploymentModels.AuxiliaryResource, err error) { @@ -357,7 +361,7 @@ func getAuxiliaryResourceDeployment(ctx context.Context, kubeClient kubernetes.I if err != nil { return nil, err } - auxResourceDeployment.ReplicaList = getReplicaSummaryList(pods.Items) + auxResourceDeployment.ReplicaList = getReplicaSummaryList(pods.Items, nil) auxResourceDeployment.Status = deploymentModels.ComponentStatusFromDeployment(&deployment).String() return &auxResourceDeployment, nil } diff --git a/api/deployments/models/component_deployment.go b/api/deployments/models/component_deployment.go index 9e80e495..0881323f 100644 --- a/api/deployments/models/component_deployment.go +++ b/api/deployments/models/component_deployment.go @@ -348,12 +348,14 @@ type ReplicaSummary struct { type ReplicaStatus struct { // Status of the container // - Pending = Container in Waiting state and the reason is ContainerCreating - // - Failing = Container in Waiting state and the reason is anything else but ContainerCreating + // - Failed = Container is failed + // - Failing = Container is failed // - Running = Container in Running state + // - Succeeded = Container in Succeeded state // - Terminated = Container in Terminated state // // required: true - // enum: Pending,Failing,Running,Terminated,Starting + // enum: Pending,Succeeded,Failing,Failed,Running,Terminated,Starting // example: Running Status string `json:"status"` } @@ -418,7 +420,7 @@ type ResourceRequirements struct { Requests Resources `json:"requests,omitempty"` } -func GetReplicaSummary(pod corev1.Pod) ReplicaSummary { +func GetReplicaSummary(pod corev1.Pod, lastEventWarning string) ReplicaSummary { replicaSummary := ReplicaSummary{} replicaSummary.Name = pod.GetName() creationTimestamp := pod.GetCreationTimestamp() @@ -474,6 +476,9 @@ func GetReplicaSummary(pod corev1.Pod) ReplicaSummary { if len(pod.Spec.Containers) > 0 { replicaSummary.Resources = pointers.Ptr(ConvertResourceRequirements(pod.Spec.Containers[0].Resources)) } + if len(replicaSummary.StatusMessage) == 0 && (replicaSummary.Status.Status == Failing.String() || replicaSummary.Status.Status == Pending.String()) { + replicaSummary.StatusMessage = lastEventWarning + } return replicaSummary } diff --git a/api/deployments/models/deployment_builder.go b/api/deployments/models/deployment_builder.go index 73549e95..138210ed 100644 --- a/api/deployments/models/deployment_builder.go +++ b/api/deployments/models/deployment_builder.go @@ -4,12 +4,11 @@ import ( "errors" "time" - "github.com/equinor/radix-common/utils/slice" - crdUtils "github.com/equinor/radix-operator/pkg/apis/utils" - radixutils "github.com/equinor/radix-common/utils" + "github.com/equinor/radix-common/utils/slice" "github.com/equinor/radix-operator/pkg/apis/kube" v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" + crdUtils "github.com/equinor/radix-operator/pkg/apis/utils" ) // DeploymentBuilder Builds DTOs diff --git a/api/environments/environment_controller_test.go b/api/environments/environment_controller_test.go index 781acf6c..3460b147 100644 --- a/api/environments/environment_controller_test.go +++ b/api/environments/environment_controller_test.go @@ -1360,12 +1360,31 @@ func Test_GetJobs_Status(t *testing.T) { Jobs: []v1.RadixBatchJob{{Name: "no1"}, {Name: "no2"}, {Name: "no3"}, {Name: "no4"}, {Name: "no5"}, {Name: "no6"}, {Name: "no7"}}}, Status: v1.RadixBatchStatus{ JobStatuses: []v1.RadixBatchJobStatus{ - {Name: "no2"}, - {Name: "no3", Phase: v1.BatchJobPhaseWaiting}, - {Name: "no4", Phase: v1.BatchJobPhaseActive}, - {Name: "no5", Phase: v1.BatchJobPhaseSucceeded}, - {Name: "no6", Phase: v1.BatchJobPhaseFailed}, - {Name: "no7", Phase: v1.BatchJobPhaseStopped}, + { + Name: "no2", + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{time.Now()}, Phase: v1.PodPending}}, + }, + { + Name: "no3", + Phase: v1.BatchJobPhaseWaiting, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{time.Now()}, Phase: v1.PodPending}}, + }, + { + Name: "no4", Phase: v1.BatchJobPhaseActive, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{time.Now()}, Phase: v1.PodPending}}, + }, + { + Name: "no5", Phase: v1.BatchJobPhaseSucceeded, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{time.Now()}, Phase: v1.PodSucceeded}}, + }, + { + Name: "no6", Phase: v1.BatchJobPhaseFailed, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{time.Now()}, Phase: v1.PodFailed}}, + }, + { + Name: "no7", Phase: v1.BatchJobPhaseStopped, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{time.Now()}, Phase: v1.PodSucceeded}}, + }, {Name: "not-defined"}, }, }, @@ -1396,7 +1415,7 @@ func Test_GetJobs_Status(t *testing.T) { {Name: anyBatchName + "-no1", Status: jobSchedulerModels.Waiting.String()}, {Name: anyBatchName + "-no2", Status: jobSchedulerModels.Waiting.String()}, {Name: anyBatchName + "-no3", Status: jobSchedulerModels.Waiting.String()}, - {Name: anyBatchName + "-no4", Status: jobSchedulerModels.Running.String()}, + {Name: anyBatchName + "-no4", Status: jobSchedulerModels.Active.String()}, {Name: anyBatchName + "-no5", Status: jobSchedulerModels.Succeeded.String()}, {Name: anyBatchName + "-no6", Status: jobSchedulerModels.Failed.String()}, {Name: anyBatchName + "-no7", Status: jobSchedulerModels.Stopped.String()}, @@ -1577,6 +1596,7 @@ func Test_GetJob_AllProps(t *testing.T) { namespace := operatorutils.GetEnvironmentNamespace(anyAppName, anyEnvironment) creationTime := metav1.NewTime(time.Date(2022, 1, 2, 3, 4, 5, 0, time.UTC)) startTime := metav1.NewTime(time.Date(2022, 1, 2, 3, 4, 10, 0, time.UTC)) + podCreationTime := metav1.NewTime(time.Date(2022, 1, 2, 3, 4, 15, 0, time.UTC)) endTime := metav1.NewTime(time.Date(2022, 1, 2, 3, 4, 15, 0, time.UTC)) defaultBackoffLimit := numbers.Int32Ptr(3) @@ -1645,7 +1665,18 @@ func Test_GetJob_AllProps(t *testing.T) { }, Status: v1.RadixBatchStatus{ JobStatuses: []v1.RadixBatchJobStatus{ - {Name: "job1", Phase: v1.BatchJobPhaseSucceeded, Message: "anymessage", CreationTime: &creationTime, StartTime: &startTime, EndTime: &endTime}, + { + Name: "job1", + Phase: v1.BatchJobPhaseSucceeded, + Message: "anymessage", + CreationTime: &creationTime, + StartTime: &startTime, + EndTime: &endTime, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ + CreationTime: &podCreationTime, + Phase: v1.PodSucceeded, + }}, + }, }, }, }, @@ -1676,6 +1707,10 @@ func Test_GetJob_AllProps(t *testing.T) { }, Node: &deploymentModels.Node{Gpu: "gpu1", GpuCount: "2"}, DeploymentName: anyDeployment, + ReplicaList: []deploymentModels.ReplicaSummary{{ + Created: radixutils.FormatTimestamp(podCreationTime.Time), + Status: deploymentModels.ReplicaStatus{Status: "Succeeded"}, + }}, }, actual) // Test job2 props - override props from RD jobComponent diff --git a/api/environments/environment_handler.go b/api/environments/environment_handler.go index 9b9d674c..b06b09c3 100644 --- a/api/environments/environment_handler.go +++ b/api/environments/environment_handler.go @@ -203,8 +203,12 @@ func (eh EnvironmentHandler) GetEnvironment(ctx context.Context, appName, envNam if err != nil { return nil, err } + eventList, err := kubequery.GetEventsForEnvironment(ctx, eh.accounts.ServiceAccount.Client, appName, envName) + if err != nil { + return nil, err + } - env := apimodels.BuildEnvironment(rr, ra, re, rdList, rjList, deploymentList, componentPodList, hpaList, secretList, secretProviderClassList, eh.tlsValidator) + env := apimodels.BuildEnvironment(rr, ra, re, rdList, rjList, deploymentList, componentPodList, hpaList, secretList, secretProviderClassList, eventList, eh.tlsValidator) return env, nil } diff --git a/api/environments/job_handler.go b/api/environments/job_handler.go index 98d3ec9a..c8d9d322 100644 --- a/api/environments/job_handler.go +++ b/api/environments/job_handler.go @@ -561,11 +561,11 @@ func getReplicaStatusByJobPodStatusPhase(statusPhase radixv1.RadixBatchJobPodPha replicaStatus := deploymentModels.ReplicaStatus{} switch statusPhase { case radixv1.PodFailed: - replicaStatus.Status = "Failing" + replicaStatus.Status = "Failed" case radixv1.PodRunning: replicaStatus.Status = "Running" case radixv1.PodSucceeded: - replicaStatus.Status = "Terminated" + replicaStatus.Status = "Succeeded" default: replicaStatus.Status = "Pending" } diff --git a/api/kubequery/event_test.go b/api/kubequery/event_test.go new file mode 100644 index 00000000..eb031d3a --- /dev/null +++ b/api/kubequery/event_test.go @@ -0,0 +1,28 @@ +package kubequery + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kubefake "k8s.io/client-go/kubernetes/fake" +) + +func Test_GetEvents(t *testing.T) { + matched := corev1.Event{ObjectMeta: metav1.ObjectMeta{Name: "event1", Namespace: "app1-dev"}} + unmatched := corev1.Event{ObjectMeta: metav1.ObjectMeta{Name: "event2", Namespace: "app2-dev"}} + client := kubefake.NewSimpleClientset(&matched, &unmatched) + + // Get existing events + actual, err := GetEventsForEnvironment(context.Background(), client, "app1", "dev") + require.NoError(t, err) + assert.Len(t, actual, 1) + assert.Equal(t, matched.GetName(), actual[0].GetName()) + + // Get non-existing events (wrong namespace) + actual, err = GetEventsForEnvironment(context.Background(), client, "app3", "dev") + assert.Len(t, actual, 0) +} diff --git a/api/kubequery/events.go b/api/kubequery/events.go new file mode 100644 index 00000000..7059efbd --- /dev/null +++ b/api/kubequery/events.go @@ -0,0 +1,20 @@ +package kubequery + +import ( + "context" + + "github.com/equinor/radix-operator/pkg/apis/utils" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +// GetEventsForEnvironment returns all Events for the specified application and environment. +func GetEventsForEnvironment(ctx context.Context, client kubernetes.Interface, appName, envName string) ([]corev1.Event, error) { + ns := utils.GetEnvironmentNamespace(appName, envName) + eventList, err := client.CoreV1().Events(ns).List(ctx, v1.ListOptions{}) + if err != nil { + return nil, err + } + return eventList.Items, nil +} diff --git a/api/models/auxiliary_resource.go b/api/models/auxiliary_resource.go index 554ed419..19a3b08b 100644 --- a/api/models/auxiliary_resource.go +++ b/api/models/auxiliary_resource.go @@ -10,21 +10,21 @@ import ( corev1 "k8s.io/api/core/v1" ) -func getAuxiliaryResources(appName string, component radixv1.RadixCommonDeployComponent, deploymentList []appsv1.Deployment, podList []corev1.Pod) deploymentModels.AuxiliaryResource { +func getAuxiliaryResources(appName string, component radixv1.RadixCommonDeployComponent, deploymentList []appsv1.Deployment, podList []corev1.Pod, eventWarnings map[string]string) deploymentModels.AuxiliaryResource { var auxResource deploymentModels.AuxiliaryResource if auth := component.GetAuthentication(); component.IsPublic() && auth != nil && auth.OAuth2 != nil { - auxResource.OAuth2 = getOAuth2AuxiliaryResource(appName, component.GetName(), deploymentList, podList) + auxResource.OAuth2 = getOAuth2AuxiliaryResource(appName, component.GetName(), deploymentList, podList, eventWarnings) } return auxResource } -func getOAuth2AuxiliaryResource(appName, componentName string, deploymentList []appsv1.Deployment, podList []corev1.Pod) *deploymentModels.OAuth2AuxiliaryResource { +func getOAuth2AuxiliaryResource(appName, componentName string, deploymentList []appsv1.Deployment, podList []corev1.Pod, eventWarnings map[string]string) *deploymentModels.OAuth2AuxiliaryResource { return &deploymentModels.OAuth2AuxiliaryResource{ - Deployment: getAuxiliaryResourceDeployment(appName, componentName, operatordefaults.OAuthProxyAuxiliaryComponentType, deploymentList, podList), + Deployment: getAuxiliaryResourceDeployment(appName, componentName, operatordefaults.OAuthProxyAuxiliaryComponentType, deploymentList, podList, eventWarnings), } } -func getAuxiliaryResourceDeployment(appName, componentName, auxType string, deploymentList []appsv1.Deployment, podList []corev1.Pod) deploymentModels.AuxiliaryResourceDeployment { +func getAuxiliaryResourceDeployment(appName, componentName, auxType string, deploymentList []appsv1.Deployment, podList []corev1.Pod, eventWarnings map[string]string) deploymentModels.AuxiliaryResourceDeployment { var auxResourceDeployment deploymentModels.AuxiliaryResourceDeployment auxDeployments := slice.FindAll(deploymentList, predicate.IsDeploymentForAuxComponent(appName, componentName, auxType)) if len(auxDeployments) == 0 { @@ -33,7 +33,7 @@ func getAuxiliaryResourceDeployment(appName, componentName, auxType string, depl } deployment := auxDeployments[0] auxPods := slice.FindAll(podList, predicate.IsPodForAuxComponent(appName, componentName, auxType)) - auxResourceDeployment.ReplicaList = BuildReplicaSummaryList(auxPods) + auxResourceDeployment.ReplicaList = BuildReplicaSummaryList(auxPods, eventWarnings) auxResourceDeployment.Status = deploymentModels.ComponentStatusFromDeployment(&deployment).String() return auxResourceDeployment } diff --git a/api/models/component.go b/api/models/component.go index 4cd53c64..22cae155 100644 --- a/api/models/component.go +++ b/api/models/component.go @@ -5,6 +5,7 @@ import ( deploymentModels "github.com/equinor/radix-api/api/deployments/models" "github.com/equinor/radix-api/api/utils" + "github.com/equinor/radix-api/api/utils/event" "github.com/equinor/radix-api/api/utils/predicate" "github.com/equinor/radix-api/api/utils/tlsvalidation" commonutils "github.com/equinor/radix-common/utils" @@ -20,22 +21,25 @@ import ( ) // BuildComponents builds a list of Component models. -func BuildComponents(ra *radixv1.RadixApplication, rd *radixv1.RadixDeployment, deploymentList []appsv1.Deployment, podList []corev1.Pod, hpaList []autoscalingv2.HorizontalPodAutoscaler, secretList []corev1.Secret, tlsValidator tlsvalidation.Validator) []*deploymentModels.Component { +func BuildComponents(ra *radixv1.RadixApplication, rd *radixv1.RadixDeployment, deploymentList []appsv1.Deployment, podList []corev1.Pod, + hpaList []autoscalingv2.HorizontalPodAutoscaler, secretList []corev1.Secret, eventList []corev1.Event, + tlsValidator tlsvalidation.Validator) []*deploymentModels.Component { + lastEventWarnings := event.ConvertToEventWarnings(eventList) var components []*deploymentModels.Component - for _, component := range rd.Spec.Components { - components = append(components, buildComponent(&component, ra, rd, deploymentList, podList, hpaList, secretList, tlsValidator)) + components = append(components, buildComponent(&component, ra, rd, deploymentList, podList, hpaList, secretList, lastEventWarnings, tlsValidator)) } for _, job := range rd.Spec.Jobs { - components = append(components, buildComponent(&job, ra, rd, deploymentList, podList, hpaList, secretList, tlsValidator)) + components = append(components, buildComponent(&job, ra, rd, deploymentList, podList, hpaList, secretList, lastEventWarnings, tlsValidator)) } return components } -func buildComponent(radixComponent radixv1.RadixCommonDeployComponent, ra *radixv1.RadixApplication, rd *radixv1.RadixDeployment, deploymentList []appsv1.Deployment, podList []corev1.Pod, hpaList []autoscalingv2.HorizontalPodAutoscaler, secretList []corev1.Secret, tlsValidator tlsvalidation.Validator) *deploymentModels.Component { - +func buildComponent(radixComponent radixv1.RadixCommonDeployComponent, ra *radixv1.RadixApplication, rd *radixv1.RadixDeployment, + deploymentList []appsv1.Deployment, podList []corev1.Pod, hpaList []autoscalingv2.HorizontalPodAutoscaler, + secretList []corev1.Secret, lastEventWarnings map[string]string, tlsValidator tlsvalidation.Validator) *deploymentModels.Component { builder := deploymentModels.NewComponentBuilder(). WithComponent(radixComponent). WithStatus(deploymentModels.ConsistentComponent). @@ -43,13 +47,12 @@ func buildComponent(radixComponent radixv1.RadixCommonDeployComponent, ra *radix WithExternalDNS(getComponentExternalDNS(radixComponent, secretList, tlsValidator)) componentPods := slice.FindAll(podList, predicate.IsPodForComponent(ra.Name, radixComponent.GetName())) - if rd.Status.ActiveTo.IsZero() { builder.WithPodNames(slice.Map(componentPods, func(pod corev1.Pod) string { return pod.Name })) builder.WithRadixEnvironmentVariables(getRadixEnvironmentVariables(componentPods)) - builder.WithReplicaSummaryList(BuildReplicaSummaryList(componentPods)) + builder.WithReplicaSummaryList(BuildReplicaSummaryList(componentPods, lastEventWarnings)) builder.WithStatus(getComponentStatus(radixComponent, ra, rd, componentPods)) - builder.WithAuxiliaryResource(getAuxiliaryResources(ra.Name, radixComponent, deploymentList, podList)) + builder.WithAuxiliaryResource(getAuxiliaryResources(ra.Name, radixComponent, deploymentList, podList, lastEventWarnings)) } // TODO: Use radixComponent.GetType() instead? diff --git a/api/models/deployment.go b/api/models/deployment.go index f1a47c61..ab2c88b6 100644 --- a/api/models/deployment.go +++ b/api/models/deployment.go @@ -12,13 +12,15 @@ import ( ) // BuildDeployment builds a Deployment model. -func BuildDeployment(rr *radixv1.RadixRegistration, ra *radixv1.RadixApplication, rd *radixv1.RadixDeployment, deploymentList []appsv1.Deployment, podList []corev1.Pod, hpaList []autoscalingv2.HorizontalPodAutoscaler, secretList []corev1.Secret, tlsValidator tlsvalidation.Validator, rjList []radixv1.RadixJob) *deploymentModels.Deployment { - components := BuildComponents(ra, rd, deploymentList, podList, hpaList, secretList, tlsValidator) +func BuildDeployment(rr *radixv1.RadixRegistration, ra *radixv1.RadixApplication, rd *radixv1.RadixDeployment, deploymentList []appsv1.Deployment, + podList []corev1.Pod, hpaList []autoscalingv2.HorizontalPodAutoscaler, secretList []corev1.Secret, eventList []corev1.Event, + tlsValidator tlsvalidation.Validator, rjList []radixv1.RadixJob) *deploymentModels.Deployment { + components := BuildComponents(ra, rd, deploymentList, podList, hpaList, secretList, eventList, tlsValidator) // The only error that can be returned from DeploymentBuilder is related to errors from github.com/imdario/mergo // This type of error will only happen if incorrect objects (e.g. incompatible structs) are sent as arguments to mergo, // and we should consider to panic the error in the code calling merge. - // For now we will panic the error here. + // It will currently panic the error here. radixJob, _ := slice.FindFirst(rjList, func(radixJob radixv1.RadixJob) bool { return radixJob.GetName() == rd.GetLabels()[kube.RadixJobNameLabel] }) diff --git a/api/models/environment.go b/api/models/environment.go index 9a0853bb..6bf1bf7d 100644 --- a/api/models/environment.go +++ b/api/models/environment.go @@ -14,7 +14,10 @@ import ( ) // BuildEnvironment builds and Environment model. -func BuildEnvironment(rr *radixv1.RadixRegistration, ra *radixv1.RadixApplication, re *radixv1.RadixEnvironment, rdList []radixv1.RadixDeployment, rjList []radixv1.RadixJob, deploymentList []appsv1.Deployment, podList []corev1.Pod, hpaList []autoscalingv2.HorizontalPodAutoscaler, secretList []corev1.Secret, secretProviderClassList []secretsstorev1.SecretProviderClass, tlsValidator tlsvalidation.Validator) *environmentModels.Environment { +func BuildEnvironment(rr *radixv1.RadixRegistration, ra *radixv1.RadixApplication, re *radixv1.RadixEnvironment, rdList []radixv1.RadixDeployment, + rjList []radixv1.RadixJob, deploymentList []appsv1.Deployment, podList []corev1.Pod, hpaList []autoscalingv2.HorizontalPodAutoscaler, + secretList []corev1.Secret, secretProviderClassList []secretsstorev1.SecretProviderClass, eventList []corev1.Event, + tlsValidator tlsvalidation.Validator) *environmentModels.Environment { var buildFromBranch string var activeDeployment *deploymentModels.Deployment var secrets []secretModels.Secret @@ -24,7 +27,7 @@ func BuildEnvironment(rr *radixv1.RadixRegistration, ra *radixv1.RadixApplicatio } if activeRd, ok := slice.FindFirst(rdList, isActiveDeploymentForAppAndEnv(ra.Name, re.Spec.EnvName)); ok { - activeDeployment = BuildDeployment(rr, ra, &activeRd, deploymentList, podList, hpaList, secretList, tlsValidator, rjList) + activeDeployment = BuildDeployment(rr, ra, &activeRd, deploymentList, podList, hpaList, secretList, eventList, tlsValidator, rjList) secrets = BuildSecrets(secretList, secretProviderClassList, &activeRd) } diff --git a/api/models/replica_summary.go b/api/models/replica_summary.go index ff9d38ff..2e49c3db 100644 --- a/api/models/replica_summary.go +++ b/api/models/replica_summary.go @@ -7,11 +7,8 @@ import ( ) // BuildReplicaSummaryList builds a list of ReplicaSummary models. -func BuildReplicaSummaryList(podList []corev1.Pod) []deploymentModels.ReplicaSummary { - return slice.Map(podList, BuildReplicaSummary) -} - -// BuildReplicaSummary builds a ReplicaSummary model. -func BuildReplicaSummary(pod corev1.Pod) deploymentModels.ReplicaSummary { - return deploymentModels.GetReplicaSummary(pod) +func BuildReplicaSummaryList(podList []corev1.Pod, lastEventWarnings map[string]string) []deploymentModels.ReplicaSummary { + return slice.Map(podList, func(pod corev1.Pod) deploymentModels.ReplicaSummary { + return deploymentModels.GetReplicaSummary(pod, lastEventWarnings[pod.GetName()]) + }) } diff --git a/api/utils/event/event.go b/api/utils/event/event.go new file mode 100644 index 00000000..f21cffc4 --- /dev/null +++ b/api/utils/event/event.go @@ -0,0 +1,24 @@ +package event + +import ( + "sort" + "strings" + + "github.com/equinor/radix-common/utils/slice" + corev1 "k8s.io/api/core/v1" +) + +type LastEventWarnings map[string]string + +// ConvertToEventWarnings converts Kubernetes Events to EventWarning +func ConvertToEventWarnings(events []corev1.Event) LastEventWarnings { + sort.Slice(events, func(i, j int) bool { + return events[i].CreationTimestamp.Before(&events[j].CreationTimestamp) + }) + return slice.Reduce(events, make(LastEventWarnings), func(acc LastEventWarnings, event corev1.Event) LastEventWarnings { + if strings.EqualFold(event.Type, "Warning") && strings.EqualFold(event.InvolvedObject.Kind, "Pod") { + acc[event.InvolvedObject.Name] = event.Message + } + return acc + }) +} diff --git a/swaggerui/html/swagger.json b/swaggerui/html/swagger.json index b1cb9671..9e99b6df 100644 --- a/swaggerui/html/swagger.json +++ b/swaggerui/html/swagger.json @@ -7120,11 +7120,12 @@ ], "properties": { "status": { - "description": "Status of the container\nPending = Container in Waiting state and the reason is ContainerCreating\nFailing = Container in Waiting state and the reason is anything else but ContainerCreating\nRunning = Container in Running state\nTerminated = Container in Terminated state", + "description": "Status of the container\nPending = Container in Waiting state and the reason is ContainerCreating\nFailed = Container is failed\nFailing = Container is failed\nRunning = Container in Running state\nTerminated = Container in Terminated state", "type": "string", "enum": [ "Pending", "Failing", + "Failed", "Running", "Terminated", "Starting" From fff54b82ccc5235198009d2d1db66ff83c91d102 Mon Sep 17 00:00:00 2001 From: Sergey Smolnikov Date: Mon, 25 Mar 2024 16:37:42 +0100 Subject: [PATCH 06/11] Generated swagger --- swaggerui/html/swagger.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/swaggerui/html/swagger.json b/swaggerui/html/swagger.json index 9e99b6df..df0324f6 100644 --- a/swaggerui/html/swagger.json +++ b/swaggerui/html/swagger.json @@ -7120,10 +7120,11 @@ ], "properties": { "status": { - "description": "Status of the container\nPending = Container in Waiting state and the reason is ContainerCreating\nFailed = Container is failed\nFailing = Container is failed\nRunning = Container in Running state\nTerminated = Container in Terminated state", + "description": "Status of the container\nPending = Container in Waiting state and the reason is ContainerCreating\nFailed = Container is failed\nFailing = Container is failed\nRunning = Container in Running state\nSucceeded = Container in Succeeded state\nTerminated = Container in Terminated state", "type": "string", "enum": [ "Pending", + "Succeeded", "Failing", "Failed", "Running", From a79b3770f17b0f9ea9f8161cef4155cb0649cacb Mon Sep 17 00:00:00 2001 From: Sergey Smolnikov Date: Mon, 25 Mar 2024 17:05:09 +0100 Subject: [PATCH 07/11] Fixing unit-tests --- api/environments/environment_controller_test.go | 15 +++++++++++++-- api/environments/job_handler.go | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/api/environments/environment_controller_test.go b/api/environments/environment_controller_test.go index 3460b147..8cf81400 100644 --- a/api/environments/environment_controller_test.go +++ b/api/environments/environment_controller_test.go @@ -2032,8 +2032,19 @@ func Test_GetBatches_Status(t *testing.T) { Labels: labels.Merge(labels.ForApplicationName(anyAppName), labels.ForComponentName(anyJobName), labels.ForBatchType(kube.RadixBatchTypeBatch)), }, Status: v1.RadixBatchStatus{ - Condition: v1.RadixBatchCondition{Type: v1.BatchConditionTypeCompleted}, - JobStatuses: []v1.RadixBatchJobStatus{{Name: "j1"}, {Name: "j2", Phase: v1.BatchJobPhaseFailed}}, + Condition: v1.RadixBatchCondition{Type: v1.BatchConditionTypeCompleted}, + JobStatuses: []v1.RadixBatchJobStatus{{Name: "j1"}, { + Name: "j2", + Phase: v1.BatchJobPhaseFailed, + EndTime: &metav1.Time{time.Now()}, + Failed: 1, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ + Phase: v1.PodFailed, + CreationTime: &metav1.Time{time.Now()}, + StartTime: &metav1.Time{time.Now()}, + EndTime: &metav1.Time{time.Now()}, + }}, + }}, }, }, { diff --git a/api/environments/job_handler.go b/api/environments/job_handler.go index c8d9d322..75676964 100644 --- a/api/environments/job_handler.go +++ b/api/environments/job_handler.go @@ -479,7 +479,7 @@ func getScheduledBatchStatus(batch *radixv1.RadixBatch) (status jobSchedulerMode case batch.Status.Condition.Type == radixv1.BatchConditionTypeActive: return jobSchedulerModels.Running case batch.Status.Condition.Type == radixv1.BatchConditionTypeCompleted: - if slice.All(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { + if len(batch.Status.JobStatuses) > 0 && slice.All(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { return jobStatus.Phase == radixv1.BatchJobPhaseFailed }) { return jobSchedulerModels.Failed @@ -512,7 +512,7 @@ func getScheduledJobStatus(job radixv1.RadixBatchJob, jobStatus radixv1.RadixBat if stop && (status == jobSchedulerModels.Waiting || status == jobSchedulerModels.Active || status == jobSchedulerModels.Running) { return jobSchedulerModels.Stopping } - if slice.All(jobStatus.RadixBatchJobPodStatuses, func(jobPodStatus radixv1.RadixBatchJobPodStatus) bool { + if len(jobStatus.RadixBatchJobPodStatuses) > 0 && slice.All(jobStatus.RadixBatchJobPodStatuses, func(jobPodStatus radixv1.RadixBatchJobPodStatus) bool { return jobPodStatus.Phase == radixv1.PodFailed }) { return jobSchedulerModels.Failed From a8e9fed5d61f68c40629d675b3d603c9207de7a4 Mon Sep 17 00:00:00 2001 From: Sergey Smolnikov Date: Tue, 26 Mar 2024 09:28:51 +0100 Subject: [PATCH 08/11] Fixing unit-tests --- api/environments/environment_controller_test.go | 11 ++++++++++- api/environments/job_handler.go | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/api/environments/environment_controller_test.go b/api/environments/environment_controller_test.go index 8cf81400..efe4e28d 100644 --- a/api/environments/environment_controller_test.go +++ b/api/environments/environment_controller_test.go @@ -1452,7 +1452,16 @@ func Test_GetJobs_Status_StopIsTrue(t *testing.T) { Labels: labels.Merge(labels.ForApplicationName(anyAppName), labels.ForComponentName(anyJobName), labels.ForBatchType(kube.RadixBatchTypeJob)), }, Spec: v1.RadixBatchSpec{ - Jobs: []v1.RadixBatchJob{{Name: "no1", Stop: radixutils.BoolPtr(true)}, {Name: "no2", Stop: radixutils.BoolPtr(true)}, {Name: "no3", Stop: radixutils.BoolPtr(true)}, {Name: "no4", Stop: radixutils.BoolPtr(true)}, {Name: "no5", Stop: radixutils.BoolPtr(true)}, {Name: "no6", Stop: radixutils.BoolPtr(true)}, {Name: "no7", Stop: radixutils.BoolPtr(true)}}}, + Jobs: []v1.RadixBatchJob{ + {Name: "no1", Stop: radixutils.BoolPtr(true)}, + {Name: "no2", Stop: radixutils.BoolPtr(true)}, + {Name: "no3", Stop: radixutils.BoolPtr(true)}, + {Name: "no4", Stop: radixutils.BoolPtr(true)}, + {Name: "no5", Stop: radixutils.BoolPtr(true)}, + {Name: "no6", Stop: radixutils.BoolPtr(true)}, + {Name: "no7", Stop: radixutils.BoolPtr(true)}, + }, + }, Status: v1.RadixBatchStatus{ JobStatuses: []v1.RadixBatchJobStatus{ {Name: "no2"}, diff --git a/api/environments/job_handler.go b/api/environments/job_handler.go index 75676964..3e6a6ecb 100644 --- a/api/environments/job_handler.go +++ b/api/environments/job_handler.go @@ -461,7 +461,8 @@ func (eh EnvironmentHandler) getScheduledJobSummary(batch *radixv1.RadixBatch, j summary.Resources = deploymentModels.ConvertRadixResourceRequirements(jobComponent.Resources) } } - if status, ok := slice.FindFirst(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { return jobStatus.Name == job.Name }); ok { + if statuses := slice.FindAll(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { return jobStatus.Name == job.Name }); len(statuses) == 1 { + status := statuses[0] summary.Status = getScheduledJobStatus(job, status).String() summary.Created = radixutils.FormatTime(status.CreationTime) summary.Started = radixutils.FormatTime(status.StartTime) From d220bec9fe010b3d2f283234ecfce48ff2b722a2 Mon Sep 17 00:00:00 2001 From: Sergey Smolnikov Date: Tue, 26 Mar 2024 10:11:42 +0100 Subject: [PATCH 09/11] Fixing unit-tests --- .../environment_controller_test.go | 153 +++++++++++++++--- api/environments/job_handler.go | 23 +-- 2 files changed, 142 insertions(+), 34 deletions(-) diff --git a/api/environments/environment_controller_test.go b/api/environments/environment_controller_test.go index efe4e28d..8333d665 100644 --- a/api/environments/environment_controller_test.go +++ b/api/environments/environment_controller_test.go @@ -1858,15 +1858,16 @@ func Test_GetBatch_JobList(t *testing.T) { Labels: labels.Merge(labels.ForApplicationName(anyAppName), labels.ForComponentName(anyJobName), labels.ForBatchType(kube.RadixBatchTypeBatch)), }, Spec: v1.RadixBatchSpec{ - Jobs: []v1.RadixBatchJob{{Name: "no1"}, {Name: "no2"}, {Name: "no3"}, {Name: "no4"}, {Name: "no5"}, {Name: "no6"}, {Name: "no7"}}}, + Jobs: []v1.RadixBatchJob{{Name: "no1"}, {Name: "no2"}, {Name: "no3"}, {Name: "no4"}, {Name: "no5"}, {Name: "no6"}, {Name: "no7"}, {Name: "no8"}}}, Status: v1.RadixBatchStatus{ JobStatuses: []v1.RadixBatchJobStatus{ {Name: "no2"}, {Name: "no3", Phase: v1.BatchJobPhaseWaiting}, {Name: "no4", Phase: v1.BatchJobPhaseActive}, - {Name: "no5", Phase: v1.BatchJobPhaseSucceeded}, - {Name: "no6", Phase: v1.BatchJobPhaseFailed}, - {Name: "no7", Phase: v1.BatchJobPhaseStopped}, + {Name: "no5", Phase: v1.BatchJobPhaseRunning}, + {Name: "no6", Phase: v1.BatchJobPhaseSucceeded}, + {Name: "no7", Phase: v1.BatchJobPhaseFailed}, + {Name: "no8", Phase: v1.BatchJobPhaseStopped}, {Name: "not-defined"}, }, }, @@ -1884,7 +1885,7 @@ func Test_GetBatch_JobList(t *testing.T) { var actual deploymentModels.ScheduledBatchSummary err = controllertest.GetResponseBody(response, &actual) require.NoError(t, err) - require.Len(t, actual.JobList, 7) + require.Len(t, actual.JobList, 8) type assertMapped struct { Name string Status string @@ -1896,10 +1897,11 @@ func Test_GetBatch_JobList(t *testing.T) { {Name: anyBatchName + "-no1", Status: jobSchedulerModels.Waiting.String()}, {Name: anyBatchName + "-no2", Status: jobSchedulerModels.Waiting.String()}, {Name: anyBatchName + "-no3", Status: jobSchedulerModels.Waiting.String()}, - {Name: anyBatchName + "-no4", Status: jobSchedulerModels.Running.String()}, - {Name: anyBatchName + "-no5", Status: jobSchedulerModels.Succeeded.String()}, - {Name: anyBatchName + "-no6", Status: jobSchedulerModels.Failed.String()}, - {Name: anyBatchName + "-no7", Status: jobSchedulerModels.Stopped.String()}, + {Name: anyBatchName + "-no4", Status: jobSchedulerModels.Active.String()}, + {Name: anyBatchName + "-no5", Status: jobSchedulerModels.Running.String()}, + {Name: anyBatchName + "-no6", Status: jobSchedulerModels.Succeeded.String()}, + {Name: anyBatchName + "-no7", Status: jobSchedulerModels.Failed.String()}, + {Name: anyBatchName + "-no8", Status: jobSchedulerModels.Stopped.String()}, } assert.ElementsMatch(t, expected, actualMapped) } @@ -2023,6 +2025,26 @@ func Test_GetBatches_Status(t *testing.T) { Labels: labels.Merge(labels.ForApplicationName(anyAppName), labels.ForComponentName(anyJobName), labels.ForBatchType(kube.RadixBatchTypeBatch)), }, Status: v1.RadixBatchStatus{ + JobStatuses: []v1.RadixBatchJobStatus{ + {Name: "j1"}, + { + Name: "j2", + Phase: v1.BatchJobPhaseActive, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ + Phase: v1.PodRunning, + CreationTime: &metav1.Time{time.Now()}, + StartTime: &metav1.Time{time.Now()}, + }}, + }, + { + Name: "j3", + Phase: v1.BatchJobPhaseWaiting, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ + Phase: v1.PodPending, + CreationTime: &metav1.Time{time.Now()}, + }}, + }, + }, Condition: v1.RadixBatchCondition{Type: v1.BatchConditionTypeActive}, }, }, @@ -2032,7 +2054,29 @@ func Test_GetBatches_Status(t *testing.T) { Labels: labels.Merge(labels.ForApplicationName(anyAppName), labels.ForComponentName(anyJobName), labels.ForBatchType(kube.RadixBatchTypeBatch)), }, Status: v1.RadixBatchStatus{ - Condition: v1.RadixBatchCondition{Type: v1.BatchConditionTypeCompleted}, + JobStatuses: []v1.RadixBatchJobStatus{ + {Name: "j1"}, + { + Name: "j2", + Phase: v1.BatchJobPhaseRunning, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ + Phase: v1.PodRunning, + CreationTime: &metav1.Time{time.Now()}, + StartTime: &metav1.Time{time.Now()}, + }}, + }, + { + Name: "j3", + Phase: v1.BatchJobPhaseSucceeded, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ + Phase: v1.PodSucceeded, + CreationTime: &metav1.Time{time.Now()}, + StartTime: &metav1.Time{time.Now()}, + EndTime: &metav1.Time{time.Now()}, + }}, + }, + }, + Condition: v1.RadixBatchCondition{Type: v1.BatchConditionTypeActive}, }, }, { @@ -2042,18 +2086,75 @@ func Test_GetBatches_Status(t *testing.T) { }, Status: v1.RadixBatchStatus{ Condition: v1.RadixBatchCondition{Type: v1.BatchConditionTypeCompleted}, - JobStatuses: []v1.RadixBatchJobStatus{{Name: "j1"}, { - Name: "j2", - Phase: v1.BatchJobPhaseFailed, - EndTime: &metav1.Time{time.Now()}, - Failed: 1, - RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ - Phase: v1.PodFailed, - CreationTime: &metav1.Time{time.Now()}, - StartTime: &metav1.Time{time.Now()}, - EndTime: &metav1.Time{time.Now()}, - }}, - }}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "batch-job6", + Labels: labels.Merge(labels.ForApplicationName(anyAppName), labels.ForComponentName(anyJobName), labels.ForBatchType(kube.RadixBatchTypeBatch)), + }, + Status: v1.RadixBatchStatus{ + Condition: v1.RadixBatchCondition{Type: v1.BatchConditionTypeCompleted}, + JobStatuses: []v1.RadixBatchJobStatus{ + { + Name: "j1", + Phase: v1.BatchJobPhaseFailed, + EndTime: &metav1.Time{time.Now()}, + Failed: 1, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ + Phase: v1.PodFailed, + CreationTime: &metav1.Time{time.Now()}, + StartTime: &metav1.Time{time.Now()}, + EndTime: &metav1.Time{time.Now()}, + }}, + }, + { + Name: "j2", + Phase: v1.BatchJobPhaseFailed, + EndTime: &metav1.Time{time.Now()}, + Failed: 1, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ + Phase: v1.PodFailed, + CreationTime: &metav1.Time{time.Now()}, + StartTime: &metav1.Time{time.Now()}, + EndTime: &metav1.Time{time.Now()}, + }}, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "batch-job7", + Labels: labels.Merge(labels.ForApplicationName(anyAppName), labels.ForComponentName(anyJobName), labels.ForBatchType(kube.RadixBatchTypeBatch)), + }, + Status: v1.RadixBatchStatus{ + Condition: v1.RadixBatchCondition{Type: v1.BatchConditionTypeCompleted}, + JobStatuses: []v1.RadixBatchJobStatus{ + { + Name: "j1", + Phase: v1.BatchJobPhaseFailed, + EndTime: &metav1.Time{time.Now()}, + Failed: 1, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ + Phase: v1.PodFailed, + CreationTime: &metav1.Time{time.Now()}, + StartTime: &metav1.Time{time.Now()}, + EndTime: &metav1.Time{time.Now()}, + }}, + }, + { + Name: "j2", + Phase: v1.BatchJobPhaseSucceeded, + EndTime: &metav1.Time{time.Now()}, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ + Phase: v1.PodSucceeded, + CreationTime: &metav1.Time{time.Now()}, + StartTime: &metav1.Time{time.Now()}, + EndTime: &metav1.Time{time.Now()}, + }}, + }, + }, }, }, { @@ -2090,9 +2191,11 @@ func Test_GetBatches_Status(t *testing.T) { expected := []assertMapped{ {Name: "batch-job1", Status: jobSchedulerModels.Waiting.String()}, {Name: "batch-job2", Status: jobSchedulerModels.Waiting.String()}, - {Name: "batch-job3", Status: jobSchedulerModels.Running.String()}, - {Name: "batch-job4", Status: jobSchedulerModels.Succeeded.String()}, - {Name: "batch-job5", Status: jobSchedulerModels.Failed.String()}, + {Name: "batch-job3", Status: jobSchedulerModels.Active.String()}, + {Name: "batch-job4", Status: jobSchedulerModels.Running.String()}, + {Name: "batch-job5", Status: jobSchedulerModels.Succeeded.String()}, + {Name: "batch-job6", Status: jobSchedulerModels.Failed.String()}, + {Name: "batch-job7", Status: jobSchedulerModels.Succeeded.String()}, } assert.ElementsMatch(t, expected, actualMapped) } diff --git a/api/environments/job_handler.go b/api/environments/job_handler.go index 3e6a6ecb..957c9500 100644 --- a/api/environments/job_handler.go +++ b/api/environments/job_handler.go @@ -461,24 +461,33 @@ func (eh EnvironmentHandler) getScheduledJobSummary(batch *radixv1.RadixBatch, j summary.Resources = deploymentModels.ConvertRadixResourceRequirements(jobComponent.Resources) } } + + stopJob := job.Stop != nil && *job.Stop + if statuses := slice.FindAll(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { return jobStatus.Name == job.Name }); len(statuses) == 1 { status := statuses[0] - summary.Status = getScheduledJobStatus(job, status).String() + summary.Status = getScheduledJobStatus(job, status, stopJob).String() summary.Created = radixutils.FormatTime(status.CreationTime) summary.Started = radixutils.FormatTime(status.StartTime) summary.Ended = radixutils.FormatTime(status.EndTime) summary.Message = status.Message summary.FailedCount = status.Failed summary.Restart = status.Restart + } else if len(statuses) == 0 && stopJob { + summary.Status = jobSchedulerModels.Stopping.String() } - return summary } func getScheduledBatchStatus(batch *radixv1.RadixBatch) (status jobSchedulerModels.ProgressStatus) { switch { case batch.Status.Condition.Type == radixv1.BatchConditionTypeActive: - return jobSchedulerModels.Running + if slice.Any(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { + return jobStatus.Phase == radixv1.BatchJobPhaseRunning + }) { + return jobSchedulerModels.Running + } + return jobSchedulerModels.Active case batch.Status.Condition.Type == radixv1.BatchConditionTypeCompleted: if len(batch.Status.JobStatuses) > 0 && slice.All(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { return jobStatus.Phase == radixv1.BatchJobPhaseFailed @@ -490,7 +499,7 @@ func getScheduledBatchStatus(batch *radixv1.RadixBatch) (status jobSchedulerMode return jobSchedulerModels.Waiting } -func getScheduledJobStatus(job radixv1.RadixBatchJob, jobStatus radixv1.RadixBatchJobStatus) (status jobSchedulerModels.ProgressStatus) { +func getScheduledJobStatus(job radixv1.RadixBatchJob, jobStatus radixv1.RadixBatchJobStatus, stopJob bool) (status jobSchedulerModels.ProgressStatus) { status = jobSchedulerModels.Waiting switch jobStatus.Phase { case radixv1.BatchJobPhaseActive: @@ -506,11 +515,7 @@ func getScheduledJobStatus(job radixv1.RadixBatchJob, jobStatus radixv1.RadixBat case radixv1.BatchJobPhaseWaiting: status = jobSchedulerModels.Waiting } - var stop bool - if job.Stop != nil { - stop = *job.Stop - } - if stop && (status == jobSchedulerModels.Waiting || status == jobSchedulerModels.Active || status == jobSchedulerModels.Running) { + if stopJob && (status == jobSchedulerModels.Waiting || status == jobSchedulerModels.Active || status == jobSchedulerModels.Running) { return jobSchedulerModels.Stopping } if len(jobStatus.RadixBatchJobPodStatuses) > 0 && slice.All(jobStatus.RadixBatchJobPodStatuses, func(jobPodStatus radixv1.RadixBatchJobPodStatus) bool { From d125069a2dacba91cbb4ed91d8d6475862d236a3 Mon Sep 17 00:00:00 2001 From: Sergey Smolnikov Date: Tue, 26 Mar 2024 10:56:06 +0100 Subject: [PATCH 10/11] Cleanup --- .../environment_controller_test.go | 60 +++++++++---------- api/environments/job_handler.go | 32 +--------- api/kubequery/event_test.go | 1 + 3 files changed, 33 insertions(+), 60 deletions(-) diff --git a/api/environments/environment_controller_test.go b/api/environments/environment_controller_test.go index 8333d665..5f1777fb 100644 --- a/api/environments/environment_controller_test.go +++ b/api/environments/environment_controller_test.go @@ -1362,28 +1362,28 @@ func Test_GetJobs_Status(t *testing.T) { JobStatuses: []v1.RadixBatchJobStatus{ { Name: "no2", - RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{time.Now()}, Phase: v1.PodPending}}, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{Time: time.Now()}, Phase: v1.PodPending}}, }, { Name: "no3", Phase: v1.BatchJobPhaseWaiting, - RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{time.Now()}, Phase: v1.PodPending}}, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{Time: time.Now()}, Phase: v1.PodPending}}, }, { Name: "no4", Phase: v1.BatchJobPhaseActive, - RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{time.Now()}, Phase: v1.PodPending}}, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{Time: time.Now()}, Phase: v1.PodPending}}, }, { Name: "no5", Phase: v1.BatchJobPhaseSucceeded, - RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{time.Now()}, Phase: v1.PodSucceeded}}, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{Time: time.Now()}, Phase: v1.PodSucceeded}}, }, { Name: "no6", Phase: v1.BatchJobPhaseFailed, - RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{time.Now()}, Phase: v1.PodFailed}}, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{Time: time.Now()}, Phase: v1.PodFailed}}, }, { Name: "no7", Phase: v1.BatchJobPhaseStopped, - RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{time.Now()}, Phase: v1.PodSucceeded}}, + RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{CreationTime: &metav1.Time{Time: time.Now()}, Phase: v1.PodSucceeded}}, }, {Name: "not-defined"}, }, @@ -2032,8 +2032,8 @@ func Test_GetBatches_Status(t *testing.T) { Phase: v1.BatchJobPhaseActive, RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ Phase: v1.PodRunning, - CreationTime: &metav1.Time{time.Now()}, - StartTime: &metav1.Time{time.Now()}, + CreationTime: &metav1.Time{Time: time.Now()}, + StartTime: &metav1.Time{Time: time.Now()}, }}, }, { @@ -2041,7 +2041,7 @@ func Test_GetBatches_Status(t *testing.T) { Phase: v1.BatchJobPhaseWaiting, RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ Phase: v1.PodPending, - CreationTime: &metav1.Time{time.Now()}, + CreationTime: &metav1.Time{Time: time.Now()}, }}, }, }, @@ -2061,8 +2061,8 @@ func Test_GetBatches_Status(t *testing.T) { Phase: v1.BatchJobPhaseRunning, RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ Phase: v1.PodRunning, - CreationTime: &metav1.Time{time.Now()}, - StartTime: &metav1.Time{time.Now()}, + CreationTime: &metav1.Time{Time: time.Now()}, + StartTime: &metav1.Time{Time: time.Now()}, }}, }, { @@ -2070,9 +2070,9 @@ func Test_GetBatches_Status(t *testing.T) { Phase: v1.BatchJobPhaseSucceeded, RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ Phase: v1.PodSucceeded, - CreationTime: &metav1.Time{time.Now()}, - StartTime: &metav1.Time{time.Now()}, - EndTime: &metav1.Time{time.Now()}, + CreationTime: &metav1.Time{Time: time.Now()}, + StartTime: &metav1.Time{Time: time.Now()}, + EndTime: &metav1.Time{Time: time.Now()}, }}, }, }, @@ -2099,25 +2099,25 @@ func Test_GetBatches_Status(t *testing.T) { { Name: "j1", Phase: v1.BatchJobPhaseFailed, - EndTime: &metav1.Time{time.Now()}, + EndTime: &metav1.Time{Time: time.Now()}, Failed: 1, RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ Phase: v1.PodFailed, - CreationTime: &metav1.Time{time.Now()}, - StartTime: &metav1.Time{time.Now()}, - EndTime: &metav1.Time{time.Now()}, + CreationTime: &metav1.Time{Time: time.Now()}, + StartTime: &metav1.Time{Time: time.Now()}, + EndTime: &metav1.Time{Time: time.Now()}, }}, }, { Name: "j2", Phase: v1.BatchJobPhaseFailed, - EndTime: &metav1.Time{time.Now()}, + EndTime: &metav1.Time{Time: time.Now()}, Failed: 1, RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ Phase: v1.PodFailed, - CreationTime: &metav1.Time{time.Now()}, - StartTime: &metav1.Time{time.Now()}, - EndTime: &metav1.Time{time.Now()}, + CreationTime: &metav1.Time{Time: time.Now()}, + StartTime: &metav1.Time{Time: time.Now()}, + EndTime: &metav1.Time{Time: time.Now()}, }}, }, }, @@ -2134,24 +2134,24 @@ func Test_GetBatches_Status(t *testing.T) { { Name: "j1", Phase: v1.BatchJobPhaseFailed, - EndTime: &metav1.Time{time.Now()}, + EndTime: &metav1.Time{Time: time.Now()}, Failed: 1, RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ Phase: v1.PodFailed, - CreationTime: &metav1.Time{time.Now()}, - StartTime: &metav1.Time{time.Now()}, - EndTime: &metav1.Time{time.Now()}, + CreationTime: &metav1.Time{Time: time.Now()}, + StartTime: &metav1.Time{Time: time.Now()}, + EndTime: &metav1.Time{Time: time.Now()}, }}, }, { Name: "j2", Phase: v1.BatchJobPhaseSucceeded, - EndTime: &metav1.Time{time.Now()}, + EndTime: &metav1.Time{Time: time.Now()}, RadixBatchJobPodStatuses: []v1.RadixBatchJobPodStatus{{ Phase: v1.PodSucceeded, - CreationTime: &metav1.Time{time.Now()}, - StartTime: &metav1.Time{time.Now()}, - EndTime: &metav1.Time{time.Now()}, + CreationTime: &metav1.Time{Time: time.Now()}, + StartTime: &metav1.Time{Time: time.Now()}, + EndTime: &metav1.Time{Time: time.Now()}, }}, }, }, diff --git a/api/environments/job_handler.go b/api/environments/job_handler.go index 957c9500..72143ec5 100644 --- a/api/environments/job_handler.go +++ b/api/environments/job_handler.go @@ -23,7 +23,6 @@ import ( radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" operatorUtils "github.com/equinor/radix-operator/pkg/apis/utils" radixLabels "github.com/equinor/radix-operator/pkg/apis/utils/labels" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -48,7 +47,6 @@ func (eh EnvironmentHandler) getJobs(ctx context.Context, appName, envName, jobC if err != nil { return nil, err } - return eh.getScheduledJobSummaryList(radixBatches), nil } @@ -337,32 +335,6 @@ func (eh EnvironmentHandler) getRadixBatch(ctx context.Context, appName, envName return batch, nil } -func (eh EnvironmentHandler) getPodsForBatch(ctx context.Context, appName, envName, batchName string) ([]corev1.Pod, error) { - namespace := operatorUtils.GetEnvironmentNamespace(appName, envName) - selector := radixLabels.ForBatchName(batchName) - - return eh.getPodsWithLabelSelector(ctx, namespace, selector.String()) -} - -func (eh EnvironmentHandler) getPodsForBatchJob(ctx context.Context, appName, envName, batchName, jobName string) ([]corev1.Pod, error) { - namespace := operatorUtils.GetEnvironmentNamespace(appName, envName) - selector := radixLabels.Merge( - radixLabels.ForBatchName(batchName), - radixLabels.ForBatchJobName(jobName), - ) - - return eh.getPodsWithLabelSelector(ctx, namespace, selector.String()) -} - -func (eh EnvironmentHandler) getPodsWithLabelSelector(ctx context.Context, namespace, labelSelector string) ([]corev1.Pod, error) { - pods, err := eh.accounts.UserAccount.Client.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector}) - if err != nil { - return nil, err - } - - return pods.Items, nil -} - func (eh EnvironmentHandler) getScheduledBatchSummaryList(batches []radixv1.RadixBatch) (summaries []deploymentModels.ScheduledBatchSummary) { for _, batch := range batches { summaries = append(summaries, eh.getScheduledBatchSummary(&batch)) @@ -466,7 +438,7 @@ func (eh EnvironmentHandler) getScheduledJobSummary(batch *radixv1.RadixBatch, j if statuses := slice.FindAll(batch.Status.JobStatuses, func(jobStatus radixv1.RadixBatchJobStatus) bool { return jobStatus.Name == job.Name }); len(statuses) == 1 { status := statuses[0] - summary.Status = getScheduledJobStatus(job, status, stopJob).String() + summary.Status = getScheduledJobStatus(status, stopJob).String() summary.Created = radixutils.FormatTime(status.CreationTime) summary.Started = radixutils.FormatTime(status.StartTime) summary.Ended = radixutils.FormatTime(status.EndTime) @@ -499,7 +471,7 @@ func getScheduledBatchStatus(batch *radixv1.RadixBatch) (status jobSchedulerMode return jobSchedulerModels.Waiting } -func getScheduledJobStatus(job radixv1.RadixBatchJob, jobStatus radixv1.RadixBatchJobStatus, stopJob bool) (status jobSchedulerModels.ProgressStatus) { +func getScheduledJobStatus(jobStatus radixv1.RadixBatchJobStatus, stopJob bool) (status jobSchedulerModels.ProgressStatus) { status = jobSchedulerModels.Waiting switch jobStatus.Phase { case radixv1.BatchJobPhaseActive: diff --git a/api/kubequery/event_test.go b/api/kubequery/event_test.go index eb031d3a..893c7d42 100644 --- a/api/kubequery/event_test.go +++ b/api/kubequery/event_test.go @@ -24,5 +24,6 @@ func Test_GetEvents(t *testing.T) { // Get non-existing events (wrong namespace) actual, err = GetEventsForEnvironment(context.Background(), client, "app3", "dev") + require.NoError(t, err) assert.Len(t, actual, 0) } From 552b05e18268154434737f71bc19bdbb28e6a849 Mon Sep 17 00:00:00 2001 From: Sergey Smolnikov Date: Wed, 27 Mar 2024 09:44:00 +0100 Subject: [PATCH 11/11] Updated versions, fixed unit-test --- api/environments/environment_controller_test.go | 6 +++--- api/environments/job_handler.go | 1 + go.mod | 5 ++--- go.sum | 12 ++++-------- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/api/environments/environment_controller_test.go b/api/environments/environment_controller_test.go index 5f1777fb..9cb9c0d3 100644 --- a/api/environments/environment_controller_test.go +++ b/api/environments/environment_controller_test.go @@ -2200,7 +2200,7 @@ func Test_GetBatches_Status(t *testing.T) { assert.ElementsMatch(t, expected, actualMapped) } -func Test_GetBatches_JobListShouldBeEmpty(t *testing.T) { +func Test_GetBatches_JobListShouldHaveJobWithStatusWaiting(t *testing.T) { namespace := operatorutils.GetEnvironmentNamespace(anyAppName, anyEnvironment) // Setup @@ -2246,8 +2246,8 @@ func Test_GetBatches_JobListShouldBeEmpty(t *testing.T) { err = controllertest.GetResponseBody(response, &actual) require.NoError(t, err) require.Len(t, actual, 1) - assert.Len(t, actual[0].JobList, 0) - + assert.Len(t, actual[0].JobList, 1) + assert.Equal(t, string(v1.BatchJobPhaseWaiting), actual[0].JobList[0].Status) } func Test_StopJob(t *testing.T) { diff --git a/api/environments/job_handler.go b/api/environments/job_handler.go index 72143ec5..ddb8262c 100644 --- a/api/environments/job_handler.go +++ b/api/environments/job_handler.go @@ -349,6 +349,7 @@ func (eh EnvironmentHandler) getScheduledBatchSummary(batch *radixv1.RadixBatch) DeploymentName: batch.Spec.RadixDeploymentJobRef.Name, Status: getScheduledBatchStatus(batch).String(), TotalJobCount: len(batch.Spec.Jobs), + JobList: eh.getScheduledJobSummaries(batch), Created: radixutils.FormatTimestamp(batch.GetCreationTimestamp().Time), Started: radixutils.FormatTime(batch.Status.Condition.ActiveTime), Ended: radixutils.FormatTime(batch.Status.Condition.CompletionTime), diff --git a/go.mod b/go.mod index ca8b1f85..ac27ed7b 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.21 require ( github.com/cert-manager/cert-manager v1.14.2 github.com/equinor/radix-common v1.9.2 - github.com/equinor/radix-job-scheduler v1.9.1-0.20240314135648-a2487d847d4c - github.com/equinor/radix-operator v1.50.3-0.20240313152237-1dc5d2699a2e + github.com/equinor/radix-job-scheduler v1.9.1 + github.com/equinor/radix-operator v1.50.7 github.com/evanphx/json-patch/v5 v5.7.0 github.com/felixge/httpsnoop v1.0.4 github.com/go-swagger/go-swagger v0.30.5 @@ -88,7 +88,6 @@ require ( github.com/prometheus/statsd_exporter v0.22.7 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect diff --git a/go.sum b/go.sum index 094b2e20..e2f78d59 100644 --- a/go.sum +++ b/go.sum @@ -89,10 +89,10 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/equinor/radix-common v1.9.2 h1:pOYN/mSAoPe6KO/Nvudfd5DUETbLv4nLTLzFPr62ADw= github.com/equinor/radix-common v1.9.2/go.mod h1:ekn86U68NT4ccSdt3GT+ukpiclzfuhr96a7zBJKv/jw= -github.com/equinor/radix-job-scheduler v1.9.1-0.20240314135648-a2487d847d4c h1:TKPscZOxBsN41EBpGtBHOaBsrYWHLT96PfhG/wyvHbk= -github.com/equinor/radix-job-scheduler v1.9.1-0.20240314135648-a2487d847d4c/go.mod h1:8220ViUF4YLOvl2z+VJQnMOaZ6jhPMfHYWdIOuUGPdo= -github.com/equinor/radix-operator v1.50.3-0.20240313152237-1dc5d2699a2e h1:lWm6hwdHJe/sZtmT2B5QijihTK2NDTKImDAytPF0vWU= -github.com/equinor/radix-operator v1.50.3-0.20240313152237-1dc5d2699a2e/go.mod h1:rl8Tbor0wvKfol67nd/p72MRh0iDTClGeQ2HcMRG/LQ= +github.com/equinor/radix-job-scheduler v1.9.1 h1:B71xs8ucCG0yD6Zy2z7MVwaC0RknJOXe+EHEEfAN9AU= +github.com/equinor/radix-job-scheduler v1.9.1/go.mod h1:R2c3jrcKA7cLhHBY+3UDLZ6shEeA399JI19qMS/E4xg= +github.com/equinor/radix-operator v1.50.7 h1:/dV00+u3DhRrevdLKy/Xk7051KJ0exJW86Mcq/9Io0I= +github.com/equinor/radix-operator v1.50.7/go.mod h1:bLL8hVfdEUuucNRGUit33uBjUhuunpNWO5youmZz8e8= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc= @@ -373,8 +373,6 @@ github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWR github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= @@ -395,7 +393,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -589,7 +586,6 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=