From 835c7304c15e4bf8c98587d726cbad45a0ca14ef Mon Sep 17 00:00:00 2001 From: Richard Hagen Date: Thu, 28 Nov 2024 08:30:55 +0100 Subject: [PATCH] Replace timestamp strings with time.Time (#143) * bugfix swaggerReplace timestamp strings with time.Time Also set "created" statuses equal to parent created status when missing * fix jobStatus * go mod tidy * merge in main * change Updated to time.Time * update documentation * update documentation * remove unused example comment * set job created as nullable --- README.md | 15 +--- api/v1/batches/batch_handler.go | 2 +- api/v1/controllers/batches/controller_test.go | 42 +++++------ api/v1/controllers/jobs/controller_test.go | 34 ++++----- api/v1/jobs.go | 12 ++-- api/v1/jobs/job_handler.go | 1 + go.mod | 2 +- go.sum | 4 +- main.go | 2 +- models/v1/job_status.go | 36 +++++----- models/v2/batch_status.go | 42 ++++++----- pkg/batch/batch.go | 54 ++++++++++---- pkg/notifications/webhook_notifier.go | 59 +++++++++++---- pkg/notifications/webhook_notifier_test.go | 9 ++- swaggerui/html/swagger.json | 71 +++++++++---------- 15 files changed, 218 insertions(+), 167 deletions(-) diff --git a/README.md b/README.md index 4aaecc2..cce3c5d 100644 --- a/README.md +++ b/README.md @@ -19,16 +19,7 @@ Request from application container URLs ## Developing -You need Go installed. Make sure `GOPATH` and `GOROOT` are properly set up. - -Clone the repo into your `GOPATH` and run `go mod download`. - -Also needed: - -- [`go-swagger`](https://github.com/go-swagger/go-swagger) (on a Mac, you can install it with Homebrew: `brew install go-swagger`) -- [`statik`](https://github.com/rakyll/statik) (install with `go get github.com/rakyll/statik`) - -Clone the repo into your `GOPATH` and run `go mod download`. +You need Go installed. Run `make bootstrap` to install required tools. #### Update version We follow the [semantic version](https://semver.org/) as recommended by [go](https://blog.golang.org/publishing-go-modules). @@ -59,7 +50,7 @@ We use gomock to generate mocks used in unit test. [https://github.com/golang/mo You need to regenerate mocks if you make changes to any of the interfaces in the code, e.g. the job Handler interface -Run `make mock` to regenerate mocks +Run `make mocks` to regenerate mocks #### Update version We follow the [semantic version](https://semver.org/) as recommended by [go](https://blog.golang.org/publishing-go-modules). @@ -76,4 +67,4 @@ Want to contribute? Read our [contributing guidelines](./CONTRIBUTING.md) ## Security -This is how we handle [security issues](./SECURITY.md) \ No newline at end of file +This is how we handle [security issues](./SECURITY.md) diff --git a/api/v1/batches/batch_handler.go b/api/v1/batches/batch_handler.go index d89ba9f..c3e1753 100644 --- a/api/v1/batches/batch_handler.go +++ b/api/v1/batches/batch_handler.go @@ -163,7 +163,7 @@ func (handler *batchHandler) getBatchStatusFromRadixBatch(radixBatch *modelsv2.R JobStatus: modelsv1.JobStatus{ Name: radixBatch.Name, BatchId: getBatchId(radixBatch), - Created: radixBatch.CreationTime, + Created: &radixBatch.CreationTime, Started: radixBatch.Started, Ended: radixBatch.Ended, Status: string(handler.getBatchStatus(radixBatch)), diff --git a/api/v1/controllers/batches/controller_test.go b/api/v1/controllers/batches/controller_test.go index df35c9b..2309e44 100644 --- a/api/v1/controllers/batches/controller_test.go +++ b/api/v1/controllers/batches/controller_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - commonUtils "github.com/equinor/radix-common/utils" + "github.com/equinor/radix-common/utils/pointers" apiErrors "github.com/equinor/radix-job-scheduler/api/errors" "github.com/equinor/radix-job-scheduler/api/test" api "github.com/equinor/radix-job-scheduler/api/v1/batches" @@ -37,8 +37,8 @@ func TestGetBatches(t *testing.T) { batchState := modelsV1.BatchStatus{ JobStatus: modelsV1.JobStatus{ Name: "batchname", - Started: commonUtils.FormatTimestamp(time.Now()), - Ended: commonUtils.FormatTimestamp(time.Now().Add(1 * time.Minute)), + Started: pointers.Ptr(time.Now()), + Ended: pointers.Ptr(time.Now().Add(1 * time.Minute)), Status: "batchstatus", }, BatchType: string(kube.RadixBatchTypeBatch), @@ -62,8 +62,8 @@ func TestGetBatches(t *testing.T) { require.NoError(t, err) assert.Len(t, returnedBatches, 1) assert.Equal(t, batchState.JobStatus.Name, returnedBatches[0].Name) - assert.Equal(t, batchState.JobStatus.Started, returnedBatches[0].Started) - assert.Equal(t, batchState.JobStatus.Ended, returnedBatches[0].Ended) + assert.WithinDuration(t, *batchState.JobStatus.Started, *returnedBatches[0].Started, 1) + assert.WithinDuration(t, *batchState.JobStatus.Ended, *returnedBatches[0].Ended, 1) assert.Equal(t, batchState.JobStatus.Status, returnedBatches[0].Status) } }) @@ -107,8 +107,8 @@ func TestGetBatch(t *testing.T) { batchState := modelsV1.BatchStatus{ JobStatus: modelsV1.JobStatus{ Name: batchName, - Started: commonUtils.FormatTimestamp(time.Now()), - Ended: commonUtils.FormatTimestamp(time.Now().Add(1 * time.Minute)), + Started: pointers.Ptr(time.Now()), + Ended: pointers.Ptr(time.Now().Add(1 * time.Minute)), Status: "batchstatus", }, BatchType: string(kube.RadixBatchTypeBatch), @@ -131,8 +131,8 @@ func TestGetBatch(t *testing.T) { err := test.GetResponseBody(response, &returnedBatch) require.NoError(t, err) assert.Equal(t, batchState.Name, returnedBatch.Name) - assert.Equal(t, batchState.Started, returnedBatch.Started) - assert.Equal(t, batchState.Ended, returnedBatch.Ended) + assert.WithinDuration(t, *batchState.Started, *returnedBatch.Started, 1) + assert.WithinDuration(t, *batchState.Ended, *returnedBatch.Ended, 1) assert.Equal(t, batchState.Status, returnedBatch.Status) } }) @@ -205,8 +205,8 @@ func TestCreateBatch(t *testing.T) { createdBatch := modelsV1.BatchStatus{ JobStatus: modelsV1.JobStatus{ Name: "newbatch", - Started: commonUtils.FormatTimestamp(time.Now()), - Ended: commonUtils.FormatTimestamp(time.Now().Add(1 * time.Minute)), + Started: pointers.Ptr(time.Now()), + Ended: pointers.Ptr(time.Now().Add(1 * time.Minute)), Status: "batchstatus", }, BatchType: string(kube.RadixBatchTypeBatch), @@ -229,8 +229,8 @@ func TestCreateBatch(t *testing.T) { err := test.GetResponseBody(response, &returnedBatch) require.NoError(t, err) assert.Equal(t, createdBatch.Name, returnedBatch.Name) - assert.Equal(t, createdBatch.Started, returnedBatch.Started) - assert.Equal(t, createdBatch.Ended, returnedBatch.Ended) + assert.WithinDuration(t, *createdBatch.Started, *returnedBatch.Started, 1) + assert.WithinDuration(t, *createdBatch.Ended, *returnedBatch.Ended, 1) assert.Equal(t, createdBatch.Status, returnedBatch.Status) } }) @@ -265,8 +265,8 @@ func TestCreateBatch(t *testing.T) { createdBatch := modelsV1.BatchStatus{ JobStatus: modelsV1.JobStatus{ Name: "newbatch", - Started: commonUtils.FormatTimestamp(time.Now()), - Ended: commonUtils.FormatTimestamp(time.Now().Add(1 * time.Minute)), + Started: pointers.Ptr(time.Now()), + Ended: pointers.Ptr(time.Now().Add(1 * time.Minute)), Status: "batchstatus", }, BatchType: string(kube.RadixBatchTypeBatch), @@ -289,8 +289,8 @@ func TestCreateBatch(t *testing.T) { err := test.GetResponseBody(response, &returnedBatch) require.NoError(t, err) assert.Equal(t, createdBatch.Name, returnedBatch.Name) - assert.Equal(t, createdBatch.Started, returnedBatch.Started) - assert.Equal(t, createdBatch.Ended, returnedBatch.Ended) + assert.WithinDuration(t, *createdBatch.Started, *returnedBatch.Started, 1) + assert.WithinDuration(t, *createdBatch.Ended, *returnedBatch.Ended, 1) assert.Equal(t, createdBatch.Status, returnedBatch.Status) } }) @@ -656,8 +656,8 @@ func TestGetBatchJob(t *testing.T) { jobHandler := mock.NewMockBatchHandler(ctrl) jobState := modelsV1.JobStatus{ Name: jobName, - Started: commonUtils.FormatTimestamp(time.Now()), - Ended: commonUtils.FormatTimestamp(time.Now().Add(1 * time.Minute)), + Started: pointers.Ptr(time.Now()), + Ended: pointers.Ptr(time.Now().Add(1 * time.Minute)), Status: "jobstatus", } ctx := context.Background() @@ -679,8 +679,8 @@ func TestGetBatchJob(t *testing.T) { err := test.GetResponseBody(response, &returnedJob) require.NoError(t, err) assert.Equal(t, jobState.Name, returnedJob.Name) - assert.Equal(t, jobState.Started, returnedJob.Started) - assert.Equal(t, jobState.Ended, returnedJob.Ended) + assert.WithinDuration(t, *jobState.Started, *returnedJob.Started, 1) + assert.WithinDuration(t, *jobState.Ended, *returnedJob.Ended, 1) assert.Equal(t, jobState.Status, returnedJob.Status) } }) diff --git a/api/v1/controllers/jobs/controller_test.go b/api/v1/controllers/jobs/controller_test.go index ac84fbd..54f49b3 100644 --- a/api/v1/controllers/jobs/controller_test.go +++ b/api/v1/controllers/jobs/controller_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/equinor/radix-common/utils" + "github.com/equinor/radix-common/utils/pointers" apiErrors "github.com/equinor/radix-job-scheduler/api/errors" "github.com/equinor/radix-job-scheduler/api/test" "github.com/equinor/radix-job-scheduler/api/v1/jobs" @@ -35,8 +35,8 @@ func TestGetJobs(t *testing.T) { jobHandler := mock.NewMockJobHandler(ctrl) jobState := modelsV1.JobStatus{ Name: "jobname", - Started: utils.FormatTimestamp(time.Now()), - Ended: utils.FormatTimestamp(time.Now().Add(1 * time.Minute)), + Started: pointers.Ptr(time.Now()), + Ended: pointers.Ptr(time.Now().Add(1 * time.Minute)), Status: "jobstatus", } ctx := context.Background() @@ -59,8 +59,8 @@ func TestGetJobs(t *testing.T) { assert.Len(t, returnedJobs, 1) assert.Equal(t, jobState.Name, returnedJobs[0].Name) assert.Equal(t, "", returnedJobs[0].BatchName) - assert.Equal(t, jobState.Started, returnedJobs[0].Started) - assert.Equal(t, jobState.Ended, returnedJobs[0].Ended) + assert.WithinDuration(t, *jobState.Started, *returnedJobs[0].Started, 1) + assert.WithinDuration(t, *jobState.Ended, *returnedJobs[0].Ended, 1) assert.Equal(t, jobState.Status, returnedJobs[0].Status) } }) @@ -103,8 +103,8 @@ func TestGetJob(t *testing.T) { jobHandler := mock.NewMockJobHandler(ctrl) jobState := modelsV1.JobStatus{ Name: jobName, - Started: utils.FormatTimestamp(time.Now()), - Ended: utils.FormatTimestamp(time.Now().Add(1 * time.Minute)), + Started: pointers.Ptr(time.Now()), + Ended: pointers.Ptr(time.Now().Add(1 * time.Minute)), Status: "jobstatus", } ctx := context.Background() @@ -126,8 +126,8 @@ func TestGetJob(t *testing.T) { require.NoError(t, err) assert.Equal(t, jobState.Name, returnedJob.Name) assert.Equal(t, "", returnedJob.BatchName) - assert.Equal(t, jobState.Started, returnedJob.Started) - assert.Equal(t, jobState.Ended, returnedJob.Ended) + assert.WithinDuration(t, *jobState.Started, *returnedJob.Started, 1) + assert.WithinDuration(t, *jobState.Ended, *returnedJob.Ended, 1) assert.Equal(t, jobState.Status, returnedJob.Status) } }) @@ -199,8 +199,8 @@ func TestCreateJob(t *testing.T) { jobScheduleDescription := models.JobScheduleDescription{} createdJob := modelsV1.JobStatus{ Name: "newjob", - Started: utils.FormatTimestamp(time.Now()), - Ended: utils.FormatTimestamp(time.Now().Add(1 * time.Minute)), + Started: pointers.Ptr(time.Now()), + Ended: pointers.Ptr(time.Now().Add(1 * time.Minute)), Status: "jobstatus", } jobHandler := mock.NewMockJobHandler(ctrl) @@ -222,8 +222,8 @@ func TestCreateJob(t *testing.T) { require.NoError(t, err) assert.Equal(t, createdJob.Name, returnedJob.Name) assert.Equal(t, "", returnedJob.BatchName) - assert.Equal(t, createdJob.Started, returnedJob.Started) - assert.Equal(t, createdJob.Ended, returnedJob.Ended) + assert.WithinDuration(t, *createdJob.Started, *returnedJob.Started, 1) + assert.WithinDuration(t, *createdJob.Ended, *returnedJob.Ended, 1) assert.Equal(t, createdJob.Status, returnedJob.Status) } }) @@ -253,8 +253,8 @@ func TestCreateJob(t *testing.T) { } createdJob := modelsV1.JobStatus{ Name: "newjob", - Started: utils.FormatTimestamp(time.Now()), - Ended: utils.FormatTimestamp(time.Now().Add(1 * time.Minute)), + Started: pointers.Ptr(time.Now()), + Ended: pointers.Ptr(time.Now().Add(1 * time.Minute)), Status: "jobstatus", } jobHandler := mock.NewMockJobHandler(ctrl) @@ -276,8 +276,8 @@ func TestCreateJob(t *testing.T) { require.NoError(t, err) assert.Equal(t, createdJob.Name, returnedJob.Name) assert.Equal(t, "", returnedJob.BatchName) - assert.Equal(t, createdJob.Started, returnedJob.Started) - assert.Equal(t, createdJob.Ended, returnedJob.Ended) + assert.WithinDuration(t, *createdJob.Started, *returnedJob.Started, 1) + assert.WithinDuration(t, *createdJob.Ended, *returnedJob.Ended, 1) assert.Equal(t, createdJob.Status, returnedJob.Status) } }) diff --git a/api/v1/jobs.go b/api/v1/jobs.go index 9be9f71..14dd220 100644 --- a/api/v1/jobs.go +++ b/api/v1/jobs.go @@ -15,10 +15,10 @@ import ( ) // GetJobStatusFromRadixBatchJobsStatus Get Job status from RadixBatchJob -func GetJobStatusFromRadixBatchJobsStatus(batchName string, jobStatus modelsv2.RadixBatchJobStatus) modelsv1.JobStatus { +func GetJobStatusFromRadixBatchJobsStatus(radixBatch *modelsv2.RadixBatch, jobStatus modelsv2.RadixBatchJobStatus) modelsv1.JobStatus { return modelsv1.JobStatus{ JobId: jobStatus.JobId, - BatchName: batchName, + BatchName: getBatchName(radixBatch), Name: jobStatus.Name, Created: jobStatus.CreationTime, Started: jobStatus.Started, @@ -35,9 +35,8 @@ func GetJobStatusFromRadixBatchJobsStatus(batchName string, jobStatus modelsv2.R func GetJobStatusFromRadixBatchJobsStatuses(radixBatches ...modelsv2.RadixBatch) []modelsv1.JobStatus { jobStatuses := make([]modelsv1.JobStatus, 0, len(radixBatches)) for _, radixBatch := range radixBatches { - jobStatusBatchName := getBatchName(&radixBatch) for _, jobStatus := range radixBatch.JobStatuses { - jobStatuses = append(jobStatuses, GetJobStatusFromRadixBatchJobsStatus(jobStatusBatchName, jobStatus)) + jobStatuses = append(jobStatuses, GetJobStatusFromRadixBatchJobsStatus(&radixBatch, jobStatus)) } } return jobStatuses @@ -62,12 +61,11 @@ func GetBatchJob(ctx context.Context, handlerApiV2 apiv2.Handler, batchName, job if err != nil { return nil, err } - jobStatusBatchName := getBatchName(radixBatch) for _, jobStatus := range radixBatch.JobStatuses { if !strings.EqualFold(jobStatus.Name, jobName) { continue } - jobsStatus := GetJobStatusFromRadixBatchJobsStatus(jobStatusBatchName, jobStatus) + jobsStatus := GetJobStatusFromRadixBatchJobsStatus(radixBatch, jobStatus) return &jobsStatus, nil } return nil, fmt.Errorf("not found") @@ -78,7 +76,7 @@ func GetPodStatus(podStatuses []modelsv2.RadixBatchJobPodStatus) []modelsv1.PodS return slice.Map(podStatuses, func(status modelsv2.RadixBatchJobPodStatus) modelsv1.PodStatus { return modelsv1.PodStatus{ Name: status.Name, - Created: status.Created, + Created: &status.Created, StartTime: status.StartTime, EndTime: status.EndTime, ContainerStarted: status.StartTime, diff --git a/api/v1/jobs/job_handler.go b/api/v1/jobs/job_handler.go index 6e043a0..d5a01ad 100644 --- a/api/v1/jobs/job_handler.go +++ b/api/v1/jobs/job_handler.go @@ -178,6 +178,7 @@ func getSingleJobStatusFromRadixBatchJob(radixBatch *modelsv2.RadixBatch) (*mode return nil, fmt.Errorf("batch should have only one job") } radixBatchJobStatus := radixBatch.JobStatuses[0] + jobStatus := modelsv1.JobStatus{ JobId: radixBatchJobStatus.JobId, Name: radixBatchJobStatus.Name, diff --git a/go.mod b/go.mod index aa5fcbe..4c3e5c7 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.5 require ( dario.cat/mergo v1.0.0 - github.com/equinor/radix-common v1.9.3 + github.com/equinor/radix-common v1.9.4 github.com/equinor/radix-operator v1.58.1 github.com/gin-gonic/gin v1.10.0 github.com/go-swagger/go-swagger v0.31.0 diff --git a/go.sum b/go.sum index dcbccb5..58e4652 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/equinor/radix-common v1.9.3 h1:dLKFzYy8/XyEG9Zygi0rMWIYGCddai/ILwUqjBiGYxQ= -github.com/equinor/radix-common v1.9.3/go.mod h1:+g0Wj0D40zz29DjNkYKVmCVeYy4OsFWKI7Qi9rA6kpY= +github.com/equinor/radix-common v1.9.4 h1:ErSnB2tqlRwaQuQdaA0qzsReDtHDgubcvqRO098ncEw= +github.com/equinor/radix-common v1.9.4/go.mod h1:+g0Wj0D40zz29DjNkYKVmCVeYy4OsFWKI7Qi9rA6kpY= github.com/equinor/radix-operator v1.58.1 h1:Wb/UOP1m4wUdWCL/gynPcnf6axz01Z24fBvK2DRL5m0= github.com/equinor/radix-operator v1.58.1/go.mod h1:zCdAiP/wxyvlUO4qGoJuLW3O+ZSt9kTyHMnjmsR3fCU= github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= diff --git a/main.go b/main.go index c124459..20b4a64 100644 --- a/main.go +++ b/main.go @@ -85,7 +85,7 @@ func runApiServer(ctx context.Context, kubeUtil *kube.Kube, env *models.Env, rad } go func() { - log.Info().Msgf("Radix job API is serving on port %s", *port) + log.Info().Msgf("Radix job API is serving on port %s, http://localhost:%s/swaggerui", *port, *port) if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Fatal().Err(err).Msg("Radix job API server failed to start") } diff --git a/models/v1/job_status.go b/models/v1/job_status.go index 9297f26..cd4080e 100644 --- a/models/v1/job_status.go +++ b/models/v1/job_status.go @@ -1,5 +1,7 @@ package v1 +import "time" + // JobStatus holds general information about job status // swagger:model JobStatus type JobStatus struct { @@ -28,21 +30,21 @@ type JobStatus struct { // Created timestamp // - // required: true - // example: 2006-01-02T15:04:05Z - Created string `json:"created"` + // required: false + // swagger:strfmt date-time + Created *time.Time `json:"created"` // Started timestamp // // required: false - // example: 2006-01-02T15:04:05Z - Started string `json:"started,omitempty"` + // swagger:strfmt date-time + Started *time.Time `json:"started"` // Ended timestamp // // required: false - // example: 2006-01-02T15:04:05Z - Ended string `json:"ended,omitempty"` + // swagger:strfmt date-time + Ended *time.Time `json:"ended"` // Status of the job // - Running = Job is running @@ -68,8 +70,8 @@ type JobStatus struct { // Updated timestamp when the status was updated // // required: false - // example: 2006-01-02T15:04:05Z - Updated string `json:"updated,omitempty"` + // swagger:strfmt date-time + Updated *time.Time `json:"updated"` // The number of times the container for the job has failed. // +optional @@ -100,26 +102,26 @@ type PodStatus struct { // Created timestamp // // required: false - // example: 2006-01-02T15:04:05Z - Created string `json:"created,omitempty"` + // swagger:strfmt date-time + Created *time.Time `json:"created,omitempty"` // The time at which the batch job's pod startedAt // // required: false - // example: 2006-01-02T15:04:05Z - StartTime string `json:"startTime,omitempty"` + // swagger:strfmt date-time + StartTime *time.Time `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"` + // swagger:strfmt date-time + EndTime *time.Time `json:"endTime,omitempty"` // Container started timestamp // // required: false - // example: 2006-01-02T15:04:05Z - ContainerStarted string `json:"containerStarted,omitempty"` + // swagger:strfmt date-time + ContainerStarted *time.Time `json:"containerStarted,omitempty"` // Status describes the component container status // diff --git a/models/v2/batch_status.go b/models/v2/batch_status.go index 1dbaea4..5b28640 100644 --- a/models/v2/batch_status.go +++ b/models/v2/batch_status.go @@ -1,6 +1,8 @@ package v2 import ( + "time" + radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" ) @@ -19,19 +21,20 @@ type RadixBatch struct { // Radix batch creation timestamp // // required: true - CreationTime string `json:"creationTime"` + // swagger:strfmt date-time + CreationTime time.Time `json:"creationTime"` // Started timestamp // // required: false - // example: 2006-01-02T15:04:05Z - Started string `json:"started,omitempty"` + // swagger:strfmt date-time + Started *time.Time `json:"started"` // Ended timestamp // // required: false - // example: 2006-01-02T15:04:05Z - Ended string `json:"ended,omitempty"` + // swagger:strfmt date-time + Ended *time.Time `json:"ended"` // Status of the job // @@ -70,8 +73,9 @@ type RadixBatchJobStatus struct { // Radix batch job creation timestamp // - // required: true - CreationTime string `json:"creationTime"` + // required: false + // swagger:strfmt date-time + CreationTime *time.Time `json:"creationTime"` // JobId Optional ID of a job // @@ -82,14 +86,14 @@ type RadixBatchJobStatus struct { // Started timestamp // // required: false - // example: 2006-01-02T15:04:05Z - Started string `json:"started,omitempty"` + // swagger:strfmt date-time + Started *time.Time `json:"started"` // Ended timestamp // // required: false - // example: 2006-01-02T15:04:05Z - Ended string `json:"ended,omitempty"` + // swagger:strfmt date-time + Ended *time.Time `json:"ended"` // Status of the job // @@ -128,26 +132,26 @@ type RadixBatchJobPodStatus struct { // Created timestamp // // required: false - // example: 2006-01-02T15:04:05Z - Created string `json:"created,omitempty"` + // swagger:strfmt date-time + Created time.Time `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"` + // swagger:strfmt date-time + StartTime *time.Time `json:"startTime"` // The time at which the batch job's pod finishedAt. // // required: false - // example: 2006-01-02T15:04:05Z - EndTime string `json:"endTime,omitempty"` + // swagger:strfmt date-time + EndTime *time.Time `json:"endTime"` // Container started timestamp // // required: false - // example: 2006-01-02T15:04:05Z - ContainerStarted string `json:"containerStarted,omitempty"` + // swagger:strfmt date-time + ContainerStarted *time.Time `json:"containerStarted"` // Status describes the component container status // diff --git a/pkg/batch/batch.go b/pkg/batch/batch.go index bcf52d5..79897f9 100644 --- a/pkg/batch/batch.go +++ b/pkg/batch/batch.go @@ -32,13 +32,22 @@ type CompletedRadixBatches struct { // GetRadixBatchStatus Get radix batch func GetRadixBatchStatus(radixBatch *radixv1.RadixBatch, radixDeployJobComponent *radixv1.RadixDeployJobComponent) modelsv2.RadixBatch { + var started, ended *time.Time + if radixBatch.Status.Condition.ActiveTime != nil { + started = &radixBatch.Status.Condition.ActiveTime.Time + } + + if radixBatch.Status.Condition.CompletionTime != nil { + ended = &radixBatch.Status.Condition.CompletionTime.Time + } + return modelsv2.RadixBatch{ Name: radixBatch.GetName(), BatchId: radixBatch.Spec.BatchId, BatchType: radixBatch.Labels[kube.RadixBatchTypeLabel], - CreationTime: utils.FormatTime(pointers.Ptr(radixBatch.GetCreationTimestamp())), - Started: utils.FormatTime(radixBatch.Status.Condition.ActiveTime), - Ended: utils.FormatTime(radixBatch.Status.Condition.CompletionTime), + CreationTime: radixBatch.GetCreationTimestamp().Time, + Started: started, + Ended: ended, Status: jobs.GetRadixBatchStatus(radixBatch, radixDeployJobComponent), Message: radixBatch.Status.Condition.Message, JobStatuses: getRadixBatchJobStatusesFromRadixBatch(radixBatch, radixBatch.Status.JobStatuses), @@ -68,15 +77,24 @@ func getRadixBatchJobStatusesFromRadixBatch(radixBatch *radixv1.RadixBatch, radi Name: jobName, JobId: radixBatchJob.JobId, } + if jobStatus, ok := radixBatchJobsStatuses[radixBatchJob.Name]; ok { - radixBatchJobStatus.CreationTime = utils.FormatTime(jobStatus.CreationTime) - radixBatchJobStatus.Started = utils.FormatTime(jobStatus.StartTime) - radixBatchJobStatus.Ended = utils.FormatTime(jobStatus.EndTime) + if jobStatus.CreationTime != nil { + radixBatchJobStatus.CreationTime = &jobStatus.CreationTime.Time + } + if jobStatus.StartTime != nil { + radixBatchJobStatus.Started = &jobStatus.StartTime.Time + } + + if jobStatus.EndTime != nil { + radixBatchJobStatus.Ended = &jobStatus.EndTime.Time + } + radixBatchJobStatus.Status = jobs.GetScheduledJobStatus(jobStatus, stopJob) radixBatchJobStatus.Message = jobStatus.Message radixBatchJobStatus.Failed = jobStatus.Failed radixBatchJobStatus.Restart = jobStatus.Restart - radixBatchJobStatus.PodStatuses = getPodStatusByRadixBatchJobPodStatus(jobStatus.RadixBatchJobPodStatuses) + radixBatchJobStatus.PodStatuses = getPodStatusByRadixBatchJobPodStatus(jobStatus.RadixBatchJobPodStatuses, radixBatch.CreationTimestamp.Time) } else { radixBatchJobStatus.Status = jobs.GetScheduledJobStatus(radixv1.RadixBatchJobStatus{Phase: radixv1.RadixBatchJobApiStatusWaiting}, stopJob) } @@ -85,14 +103,26 @@ func getRadixBatchJobStatusesFromRadixBatch(radixBatch *radixv1.RadixBatch, radi return jobStatuses } -func getPodStatusByRadixBatchJobPodStatus(podStatuses []radixv1.RadixBatchJobPodStatus) []modelsv2.RadixBatchJobPodStatus { +func getPodStatusByRadixBatchJobPodStatus(podStatuses []radixv1.RadixBatchJobPodStatus, created time.Time) []modelsv2.RadixBatchJobPodStatus { return slice.Map(podStatuses, func(status radixv1.RadixBatchJobPodStatus) modelsv2.RadixBatchJobPodStatus { + if status.CreationTime != nil { + created = status.CreationTime.Time + } + var started, ended *time.Time + if status.StartTime != nil { + started = &status.StartTime.Time + } + + if status.EndTime != nil { + ended = &status.EndTime.Time + } + return modelsv2.RadixBatchJobPodStatus{ Name: status.Name, - Created: utils.FormatTime(status.CreationTime), - StartTime: utils.FormatTime(status.StartTime), - EndTime: utils.FormatTime(status.EndTime), - ContainerStarted: utils.FormatTime(status.StartTime), + Created: created, + StartTime: started, + EndTime: ended, + ContainerStarted: started, Status: modelsv2.ReplicaStatus{Status: string(status.Phase)}, StatusMessage: status.Message, RestartCount: status.RestartCount, diff --git a/pkg/notifications/webhook_notifier.go b/pkg/notifications/webhook_notifier.go index b4657a2..ba5adfa 100644 --- a/pkg/notifications/webhook_notifier.go +++ b/pkg/notifications/webhook_notifier.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net/http" + "time" "github.com/equinor/radix-common/utils" "github.com/equinor/radix-common/utils/pointers" @@ -13,10 +14,8 @@ import ( "github.com/equinor/radix-job-scheduler/models/v1/events" "github.com/equinor/radix-job-scheduler/utils/radix/jobs" "github.com/equinor/radix-operator/pkg/apis/kube" - "github.com/rs/zerolog/log" - v2 "k8s.io/apimachinery/pkg/apis/meta/v1" - radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" + "github.com/rs/zerolog/log" ) type webhookNotifier struct { @@ -80,17 +79,23 @@ func webhookIsNotEmpty(webhook *string) bool { func getRadixBatchEventFromRadixBatch(event events.Event, radixBatch *radixv1.RadixBatch, radixBatchJobStatuses []radixv1.RadixBatchJobStatus, radixDeployJobComponent *radixv1.RadixDeployJobComponent) events.BatchEvent { batchType := radixBatch.Labels[kube.RadixBatchTypeLabel] - startedTime := utils.FormatTime(radixBatch.Status.Condition.ActiveTime) - endedTime := utils.FormatTime(radixBatch.Status.Condition.CompletionTime) + var startedTime, endedTime *time.Time + if radixBatch.Status.Condition.ActiveTime != nil { + startedTime = &radixBatch.Status.Condition.ActiveTime.Time + } + if radixBatch.Status.Condition.CompletionTime != nil { + endedTime = &radixBatch.Status.Condition.CompletionTime.Time + } + batchStatus := v1.JobStatus{ Name: radixBatch.GetName(), BatchId: getBatchId(radixBatch), - Created: utils.FormatTime(pointers.Ptr(radixBatch.GetCreationTimestamp())), + Created: pointers.Ptr(radixBatch.GetCreationTimestamp().Time), Started: startedTime, Ended: endedTime, Status: string(jobs.GetRadixBatchStatus(radixBatch, radixDeployJobComponent)), Message: radixBatch.Status.Condition.Message, - Updated: utils.FormatTime(pointers.Ptr(v2.Now())), + Updated: pointers.Ptr(time.Now()), } jobStatuses := getRadixBatchJobStatusesFromRadixBatch(radixBatch, radixBatchJobStatuses) return events.BatchEvent{ @@ -108,6 +113,18 @@ func getRadixBatchJobStatusesFromRadixBatch(radixBatch *radixv1.RadixBatch, radi radixBatchJobsMap := getRadixBatchJobsMap(radixBatch.Spec.Jobs) jobStatuses := make([]v1.JobStatus, 0, len(radixBatchJobStatuses)) for _, radixBatchJobStatus := range radixBatchJobStatuses { + var started, ended, created *time.Time + if radixBatchJobStatus.CreationTime != nil { + created = &radixBatchJobStatus.CreationTime.Time + } + if radixBatchJobStatus.StartTime != nil { + started = &radixBatchJobStatus.StartTime.Time + } + + if radixBatchJobStatus.EndTime != nil { + ended = &radixBatchJobStatus.EndTime.Time + } + radixBatchJob, ok := radixBatchJobsMap[radixBatchJobStatus.Name] if !ok { continue @@ -118,14 +135,14 @@ func getRadixBatchJobStatusesFromRadixBatch(radixBatch *radixv1.RadixBatch, radi BatchName: batchName, Name: jobName, JobId: radixBatchJob.JobId, - Created: utils.FormatTime(radixBatchJobStatus.CreationTime), - Started: utils.FormatTime(radixBatchJobStatus.StartTime), - Ended: utils.FormatTime(radixBatchJobStatus.EndTime), + Created: created, + Started: started, + Ended: ended, Status: string(jobs.GetScheduledJobStatus(radixBatchJobStatus, stopJob)), Failed: radixBatchJobStatus.Failed, Restart: radixBatchJobStatus.Restart, Message: radixBatchJobStatus.Message, - Updated: utils.FormatTime(pointers.Ptr(v2.Now())), + Updated: pointers.Ptr(time.Now()), PodStatuses: getPodStatusByRadixBatchJobPodStatus(radixBatchJobStatus.RadixBatchJobPodStatuses), } jobStatuses = append(jobStatuses, jobStatus) @@ -143,12 +160,24 @@ func getRadixBatchJobsMap(radixBatchJobs []radixv1.RadixBatchJob) map[string]rad func getPodStatusByRadixBatchJobPodStatus(podStatuses []radixv1.RadixBatchJobPodStatus) []v1.PodStatus { return slice.Map(podStatuses, func(status radixv1.RadixBatchJobPodStatus) v1.PodStatus { + var started, ended, created *time.Time + if status.CreationTime != nil { + created = &status.CreationTime.Time + } + if status.StartTime != nil { + started = &status.StartTime.Time + } + + if status.EndTime != nil { + ended = &status.EndTime.Time + } + return v1.PodStatus{ Name: status.Name, - Created: utils.FormatTime(status.CreationTime), - StartTime: utils.FormatTime(status.StartTime), - EndTime: utils.FormatTime(status.EndTime), - ContainerStarted: utils.FormatTime(status.StartTime), + Created: created, + StartTime: started, + EndTime: ended, + ContainerStarted: started, Status: v1.ReplicaStatus{Status: string(status.Phase)}, StatusMessage: status.Message, RestartCount: status.RestartCount, diff --git a/pkg/notifications/webhook_notifier_test.go b/pkg/notifications/webhook_notifier_test.go index c6850e2..21455a6 100644 --- a/pkg/notifications/webhook_notifier_test.go +++ b/pkg/notifications/webhook_notifier_test.go @@ -10,7 +10,6 @@ import ( "github.com/equinor/radix-job-scheduler/models/v1/events" - commonUtils "github.com/equinor/radix-common/utils" "github.com/equinor/radix-common/utils/pointers" modelsv1 "github.com/equinor/radix-job-scheduler/models/v1" "github.com/equinor/radix-operator/pkg/apis/kube" @@ -319,15 +318,15 @@ func Test_webhookNotifier_Notify(t *testing.T) { } } -func assertTimesEqual(t *testing.T, expectedTime *metav1.Time, resultTime string, arg string) { - if expectedTime != nil && len(resultTime) == 0 { +func assertTimesEqual(t *testing.T, expectedTime *metav1.Time, resultTime *time.Time, arg string) { + if expectedTime != nil && resultTime == nil { assert.Fail(t, fmt.Sprintf("missing an expected %s", arg)) return - } else if expectedTime == nil && len(resultTime) > 0 { + } else if expectedTime == nil && resultTime != nil { assert.Fail(t, fmt.Sprintf("got a not expected %s", arg)) return } if expectedTime != nil { - assert.Equal(t, commonUtils.FormatTime(expectedTime), resultTime) + assert.WithinDuration(t, expectedTime.Time, *resultTime, 1, arg) } } diff --git a/swaggerui/html/swagger.json b/swaggerui/html/swagger.json index 9df3f24..c00dfa2 100644 --- a/swaggerui/html/swagger.json +++ b/swaggerui/html/swagger.json @@ -515,7 +515,6 @@ "type": "object", "required": [ "name", - "created", "event" ], "properties": { @@ -544,14 +543,14 @@ "created": { "description": "Created timestamp", "type": "string", - "x-go-name": "Created", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Created" }, "ended": { "description": "Ended timestamp", "type": "string", - "x-go-name": "Ended", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Ended" }, "event": { "$ref": "#/definitions/Event" @@ -604,8 +603,8 @@ "started": { "description": "Started timestamp", "type": "string", - "x-go-name": "Started", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Started" }, "status": { "description": "Status of the job\nRunning = Job is running\nSucceeded = Job has succeeded\nFailed = Job has failed\nWaiting = Job is waiting\nStopping = Job is stopping\nStopped = Job has been stopped\nActive = Job is active\nCompleted = Job is completed", @@ -626,8 +625,8 @@ "updated": { "description": "Updated timestamp when the status was updated", "type": "string", - "x-go-name": "Updated", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Updated" } }, "x-go-package": "github.com/equinor/radix-job-scheduler/models/v1/events" @@ -663,8 +662,7 @@ "description": "BatchStatus holds general information about batch status", "type": "object", "required": [ - "name", - "created" + "name" ], "properties": { "DeploymentName": { @@ -692,14 +690,14 @@ "created": { "description": "Created timestamp", "type": "string", - "x-go-name": "Created", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Created" }, "ended": { "description": "Ended timestamp", "type": "string", - "x-go-name": "Ended", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Ended" }, "failed": { "description": "The number of times the container for the job has failed.\n+optional", @@ -749,8 +747,8 @@ "started": { "description": "Started timestamp", "type": "string", - "x-go-name": "Started", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Started" }, "status": { "description": "Status of the job\nRunning = Job is running\nSucceeded = Job has succeeded\nFailed = Job has failed\nWaiting = Job is waiting\nStopping = Job is stopping\nStopped = Job has been stopped\nActive = Job is active\nCompleted = Job is completed", @@ -771,8 +769,8 @@ "updated": { "description": "Updated timestamp when the status was updated", "type": "string", - "x-go-name": "Updated", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Updated" } }, "x-go-package": "github.com/equinor/radix-job-scheduler/models/v1" @@ -829,8 +827,7 @@ "description": "JobStatus holds general information about job status", "type": "object", "required": [ - "name", - "created" + "name" ], "properties": { "DeploymentName": { @@ -852,14 +849,14 @@ "created": { "description": "Created timestamp", "type": "string", - "x-go-name": "Created", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Created" }, "ended": { "description": "Ended timestamp", "type": "string", - "x-go-name": "Ended", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Ended" }, "failed": { "description": "The number of times the container for the job has failed.\n+optional", @@ -901,8 +898,8 @@ "started": { "description": "Started timestamp", "type": "string", - "x-go-name": "Started", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Started" }, "status": { "description": "Status of the job\nRunning = Job is running\nSucceeded = Job has succeeded\nFailed = Job has failed\nWaiting = Job is waiting\nStopping = Job is stopping\nStopped = Job has been stopped\nActive = Job is active\nCompleted = Job is completed", @@ -923,8 +920,8 @@ "updated": { "description": "Updated timestamp when the status was updated", "type": "string", - "x-go-name": "Updated", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Updated" } }, "x-go-package": "github.com/equinor/radix-job-scheduler/models/v1" @@ -939,20 +936,20 @@ "containerStarted": { "description": "Container started timestamp", "type": "string", - "x-go-name": "ContainerStarted", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "ContainerStarted" }, "created": { "description": "Created timestamp", "type": "string", - "x-go-name": "Created", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "Created" }, "endTime": { "description": "The time at which the batch job's pod finishedAt.", "type": "string", - "x-go-name": "EndTime", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "EndTime" }, "exitCode": { "description": "Exit status from the last termination of the container", @@ -1001,8 +998,8 @@ "startTime": { "description": "The time at which the batch job's pod startedAt", "type": "string", - "x-go-name": "StartTime", - "example": "2006-01-02T15:04:05Z" + "format": "date-time", + "x-go-name": "StartTime" }, "statusMessage": { "description": "StatusMessage provides message describing the status of a component container inside a pod",