Skip to content

Commit

Permalink
add completion time field to builds
Browse files Browse the repository at this point in the history
  • Loading branch information
bparees committed Mar 18, 2015
1 parent 0ac00c6 commit 066cb50
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 75 deletions.
12 changes: 12 additions & 0 deletions pkg/build/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)

// Build encapsulates the inputs needed to produce a new deployable image, as well as
Expand All @@ -24,6 +25,17 @@ type Build struct {

// Cancelled describes if a cancelling event was triggered for the build.
Cancelled bool `json:"cancelled,omitempty"`

// StartTimestamp is a timestamp representing the server time when this Build started
// running in a Pod.
// It is represented in RFC3339 form and is in UTC.
StartTimestamp *util.Time `json:"startTimestamp,omitempty"`

// CompletionTimestamp is a timestamp representing the server time when this Build was
// finished, whether that build failed or succeeded. It reflects the time at which
// the Pod running the Build terminated.
// It is represented in RFC3339 form and is in UTC.
CompletionTimestamp *util.Time `json:"completionTimestamp,omitempty"`
}

// BuildParameters encapsulates all the inputs necessary to represent a build.
Expand Down
12 changes: 12 additions & 0 deletions pkg/build/api/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v1beta1

import (
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)

// Build encapsulates the inputs needed to produce a new deployable image, as well as
Expand All @@ -24,6 +25,17 @@ type Build struct {

// Cancelled describes if a cancelling event was triggered for the build.
Cancelled bool `json:"cancelled,omitempty"`

// StartTimestamp is a timestamp representing the server time when this Build started
// running in a Pod.
// It is represented in RFC3339 form and is in UTC.
StartTimestamp *util.Time `json:"startTimestamp,omitempty"`

// CompletionTimestamp is a timestamp representing the server time when this Build was
// finished, whether that build failed or succeeded. It reflects the time at which
// the Pod running the Build terminated.
// It is represented in RFC3339 form and is in UTC.
CompletionTimestamp *util.Time `json:"completionTimestamp,omitempty"`
}

// BuildParameters encapsulates all the inputs necessary to represent a build.
Expand Down
11 changes: 11 additions & 0 deletions pkg/build/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
errors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

buildapi "github.com/openshift/origin/pkg/build/api"
buildclient "github.com/openshift/origin/pkg/build/client"
Expand Down Expand Up @@ -180,6 +181,14 @@ func (bc *BuildPodController) HandlePod(pod *kapi.Pod) error {
if build.Status != nextStatus {
glog.V(4).Infof("Updating build %s status %s -> %s", build.Name, build.Status, nextStatus)
build.Status = nextStatus
if build.Status == buildapi.BuildStatusComplete || build.Status == buildapi.BuildStatusFailed || build.Status == buildapi.BuildStatusCancelled {
dummy := util.Now()
build.CompletionTimestamp = &dummy
}
if build.Status == buildapi.BuildStatusRunning {
dummy := util.Now()
build.StartTimestamp = &dummy
}
if err := bc.BuildUpdater.Update(build.Namespace, build); err != nil {
return fmt.Errorf("Failed to update build %s: %#v", build.Name, err)
}
Expand All @@ -200,6 +209,8 @@ func (bc *BuildPodController) CancelBuild(build *buildapi.Build, pod *kapi.Pod)
}

build.Status = buildapi.BuildStatusCancelled
dummy := util.Now()
build.CompletionTimestamp = &dummy
if err := bc.BuildUpdater.Update(build.Namespace, build); err != nil {
return err
}
Expand Down
224 changes: 149 additions & 75 deletions pkg/build/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

buildapi "github.com/openshift/origin/pkg/build/api"
buildclient "github.com/openshift/origin/pkg/build/client"
Expand Down Expand Up @@ -345,58 +346,73 @@ func TestHandleBuild(t *testing.T) {

func TestHandlePod(t *testing.T) {
type handlePodTest struct {
matchID bool
inStatus buildapi.BuildStatus
outStatus buildapi.BuildStatus
podStatus kapi.PodPhase
exitCode int
buildUpdater buildclient.BuildUpdater
podManager podManager
matchID bool
inStatus buildapi.BuildStatus
outStatus buildapi.BuildStatus
startTimestamp *util.Time
completionTimestamp *util.Time
podStatus kapi.PodPhase
exitCode int
buildUpdater buildclient.BuildUpdater
podManager podManager
}

dummy := util.Now()
curtime := &dummy
tests := []handlePodTest{
{ // 0
matchID: false,
inStatus: buildapi.BuildStatusPending,
outStatus: buildapi.BuildStatusPending,
podStatus: kapi.PodPending,
exitCode: 0,
matchID: false,
inStatus: buildapi.BuildStatusPending,
outStatus: buildapi.BuildStatusPending,
podStatus: kapi.PodPending,
exitCode: 0,
startTimestamp: nil,
completionTimestamp: nil,
},
{ // 1
matchID: true,
inStatus: buildapi.BuildStatusPending,
outStatus: buildapi.BuildStatusPending,
podStatus: kapi.PodPending,
exitCode: 0,
matchID: true,
inStatus: buildapi.BuildStatusPending,
outStatus: buildapi.BuildStatusPending,
podStatus: kapi.PodPending,
exitCode: 0,
startTimestamp: nil,
completionTimestamp: nil,
},
{ // 2
matchID: true,
inStatus: buildapi.BuildStatusPending,
outStatus: buildapi.BuildStatusRunning,
podStatus: kapi.PodRunning,
exitCode: 0,
matchID: true,
inStatus: buildapi.BuildStatusPending,
outStatus: buildapi.BuildStatusRunning,
podStatus: kapi.PodRunning,
exitCode: 0,
startTimestamp: curtime,
completionTimestamp: nil,
},
{ // 3
matchID: true,
inStatus: buildapi.BuildStatusRunning,
outStatus: buildapi.BuildStatusComplete,
podStatus: kapi.PodSucceeded,
exitCode: 0,
matchID: true,
inStatus: buildapi.BuildStatusRunning,
outStatus: buildapi.BuildStatusComplete,
podStatus: kapi.PodSucceeded,
exitCode: 0,
startTimestamp: nil,
completionTimestamp: curtime,
},
{ // 4
matchID: true,
inStatus: buildapi.BuildStatusRunning,
outStatus: buildapi.BuildStatusFailed,
podStatus: kapi.PodFailed,
exitCode: -1,
matchID: true,
inStatus: buildapi.BuildStatusRunning,
outStatus: buildapi.BuildStatusFailed,
podStatus: kapi.PodFailed,
exitCode: -1,
startTimestamp: nil,
completionTimestamp: curtime,
},
{ // 5
matchID: true,
inStatus: buildapi.BuildStatusRunning,
outStatus: buildapi.BuildStatusComplete,
podStatus: kapi.PodSucceeded,
exitCode: 0,
buildUpdater: &errBuildUpdater{},
matchID: true,
inStatus: buildapi.BuildStatusRunning,
outStatus: buildapi.BuildStatusComplete,
podStatus: kapi.PodSucceeded,
exitCode: 0,
buildUpdater: &errBuildUpdater{},
startTimestamp: nil,
completionTimestamp: curtime,
},
}

Expand Down Expand Up @@ -424,62 +440,100 @@ func TestHandlePod(t *testing.T) {
if build.Status != tc.outStatus {
t.Errorf("(%d) Expected %s, got %s!", i, tc.outStatus, build.Status)
}

if tc.startTimestamp == nil && build.StartTimestamp != nil {
t.Errorf("(%d) Expected nil start timestamp, got %v!", i, build.StartTimestamp)
}
if tc.startTimestamp != nil && build.StartTimestamp == nil {
t.Errorf("(%d) nil start timestamp!", i)
}
if tc.startTimestamp != nil && !tc.startTimestamp.Before(build.StartTimestamp.Time) && tc.startTimestamp.Time != build.StartTimestamp.Time {
t.Errorf("(%d) Expected build start timestamp %v to be equal to or later than %v!", i, build.StartTimestamp, tc.startTimestamp)
}

if tc.completionTimestamp == nil && build.CompletionTimestamp != nil {
t.Errorf("(%d) Expected nil completion timestamp, got %v!", i, build.CompletionTimestamp)
}
if tc.completionTimestamp != nil && build.CompletionTimestamp == nil {
t.Errorf("(%d) nil completion timestamp!", i)
}
if tc.completionTimestamp != nil && !tc.completionTimestamp.Before(build.CompletionTimestamp.Time) && tc.completionTimestamp.Time != build.CompletionTimestamp.Time {
t.Errorf("(%d) Expected build completion timestamp %v to be equal to or later than %v!", i, build.CompletionTimestamp, tc.completionTimestamp)
}
}
}

func TestCancelBuild(t *testing.T) {
type handleCancelBuildTest struct {
inStatus buildapi.BuildStatus
outStatus buildapi.BuildStatus
podStatus kapi.PodPhase
exitCode int
buildUpdater buildclient.BuildUpdater
podManager podManager
inStatus buildapi.BuildStatus
outStatus buildapi.BuildStatus
podStatus kapi.PodPhase
exitCode int
buildUpdater buildclient.BuildUpdater
podManager podManager
startTimestamp *util.Time
completionTimestamp *util.Time
}
dummy := util.Now()
curtime := &dummy

tests := []handleCancelBuildTest{
{ // 0
inStatus: buildapi.BuildStatusNew,
outStatus: buildapi.BuildStatusCancelled,
exitCode: 0,
inStatus: buildapi.BuildStatusNew,
outStatus: buildapi.BuildStatusCancelled,
exitCode: 0,
startTimestamp: nil,
completionTimestamp: curtime,
},
{ // 1
inStatus: buildapi.BuildStatusPending,
outStatus: buildapi.BuildStatusCancelled,
podStatus: kapi.PodRunning,
exitCode: 0,
inStatus: buildapi.BuildStatusPending,
outStatus: buildapi.BuildStatusCancelled,
podStatus: kapi.PodRunning,
exitCode: 0,
startTimestamp: nil,
completionTimestamp: curtime,
},
{ // 2
inStatus: buildapi.BuildStatusRunning,
outStatus: buildapi.BuildStatusCancelled,
podStatus: kapi.PodRunning,
exitCode: 0,
inStatus: buildapi.BuildStatusRunning,
outStatus: buildapi.BuildStatusCancelled,
podStatus: kapi.PodRunning,
exitCode: 0,
startTimestamp: nil,
completionTimestamp: curtime,
},
{ // 3
inStatus: buildapi.BuildStatusComplete,
outStatus: buildapi.BuildStatusComplete,
podStatus: kapi.PodSucceeded,
exitCode: 0,
inStatus: buildapi.BuildStatusComplete,
outStatus: buildapi.BuildStatusComplete,
podStatus: kapi.PodSucceeded,
exitCode: 0,
startTimestamp: nil,
completionTimestamp: nil,
},
{ // 4
inStatus: buildapi.BuildStatusFailed,
outStatus: buildapi.BuildStatusFailed,
podStatus: kapi.PodFailed,
exitCode: 1,
inStatus: buildapi.BuildStatusFailed,
outStatus: buildapi.BuildStatusFailed,
podStatus: kapi.PodFailed,
exitCode: 1,
startTimestamp: nil,
completionTimestamp: nil,
},
{ // 5
inStatus: buildapi.BuildStatusNew,
outStatus: buildapi.BuildStatusNew,
podStatus: kapi.PodFailed,
exitCode: 1,
podManager: &errPodManager{},
inStatus: buildapi.BuildStatusNew,
outStatus: buildapi.BuildStatusNew,
podStatus: kapi.PodFailed,
exitCode: 1,
podManager: &errPodManager{},
startTimestamp: nil,
completionTimestamp: nil,
},
{ // 6
inStatus: buildapi.BuildStatusNew,
outStatus: buildapi.BuildStatusNew,
podStatus: kapi.PodFailed,
exitCode: 1,
buildUpdater: &errBuildUpdater{},
inStatus: buildapi.BuildStatusNew,
outStatus: buildapi.BuildStatusNew,
podStatus: kapi.PodFailed,
exitCode: 1,
buildUpdater: &errBuildUpdater{},
startTimestamp: nil,
completionTimestamp: nil,
},
}

Expand All @@ -506,10 +560,30 @@ func TestCancelBuild(t *testing.T) {
t.Errorf("(%d) Expected error, got none", i)
}
// can't check tc.outStatus because the local build object does get updated
// in this test (but would not updated in etcd)
// in this test (but would not be updated in etcd)
continue
}

if tc.startTimestamp == nil && build.StartTimestamp != nil {
t.Errorf("(%d) Expected nil start timestamp, got %v!", i, build.StartTimestamp)
}
if tc.startTimestamp != nil && build.StartTimestamp == nil {
t.Errorf("(%d) nil start timestamp!", i)
}
if tc.startTimestamp != nil && !tc.startTimestamp.Before(build.StartTimestamp.Time) && tc.startTimestamp.Time != build.StartTimestamp.Time {
t.Errorf("(%d) Expected build start timestamp %v to be equal to or later than %v!", i, build.StartTimestamp, tc.startTimestamp)
}

if tc.completionTimestamp == nil && build.CompletionTimestamp != nil {
t.Errorf("(%d) Expected nil completion timestamp, got %v!", i, build.CompletionTimestamp)
}
if tc.completionTimestamp != nil && build.CompletionTimestamp == nil {
t.Errorf("(%d) nil start timestamp!", i)
}
if tc.completionTimestamp != nil && !tc.completionTimestamp.Before(build.CompletionTimestamp.Time) && tc.completionTimestamp.Time != build.CompletionTimestamp.Time {
t.Errorf("(%d) Expected build completion timestamp %v to be equal to or later than %v!", i, build.CompletionTimestamp, tc.completionTimestamp)
}

if build.Status != tc.outStatus {
t.Errorf("(%d) Expected %s, got %s!", i, tc.outStatus, build.Status)
}
Expand Down
20 changes: 20 additions & 0 deletions pkg/cmd/cli/describe/describer.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,26 @@ func (d *BuildDescriber) Describe(namespace, name string) (string, error) {
return tabbedString(func(out *tabwriter.Writer) error {
formatMeta(out, build.ObjectMeta)
formatString(out, "Status", bold(build.Status))
if build.StartTimestamp != nil {
formatString(out, "Started", build.StartTimestamp.Time)
}
if build.CompletionTimestamp != nil {
formatString(out, "Finished", build.CompletionTimestamp.Time)
}
// Create the time object with second-level precision so we don't get
// output like "duration: 1.2724395728934s"
t := util.Now().Rfc3339Copy()
if build.StartTimestamp != nil && build.CompletionTimestamp != nil {
// time a build ran from pod creation to build finish or cancel
formatString(out, "Duration", build.CompletionTimestamp.Sub(build.StartTimestamp.Rfc3339Copy().Time))
} else if build.StartTimestamp == nil && build.Status != buildapi.BuildStatusCancelled {
// time a new build has been waiting for its pod to be created so it can run
formatString(out, "Duration", fmt.Sprintf("waiting for %s", t.Sub(build.CreationTimestamp.Rfc3339Copy().Time)))
} else if build.CompletionTimestamp == nil {
// time a still running build has been running in a pod
formatString(out, "Duration", fmt.Sprintf("running for %s", t.Sub(build.StartTimestamp.Rfc3339Copy().Time)))
}

formatString(out, "Build Pod", build.PodName)
d.DescribeParameters(build.Parameters, out)
return nil
Expand Down

0 comments on commit 066cb50

Please sign in to comment.