Skip to content
This repository has been archived by the owner on Jan 30, 2020. It is now read-only.

Commit

Permalink
fleetctl: Support service uptime as part of a list-units output
Browse files Browse the repository at this point in the history
We can get the uptime from systemctl, the resolve is like below:
localhost # /home/wood/fleet/fleetctl list-units
UNIT                    MACHINE                         ACTIVE  SUB     UPTIME
world.service           06ecd4f7.../192.168.122.30      active  running 2015-07-06 07:38:38 AM, Since 11m39s
world2.service          1d3430ef.../192.168.122.31      active  running 2015-07-06 07:48:24 AM, Since 1m54s
world_glob.service      06ecd4f7.../192.168.122.30      active  running 2015-07-06 07:48:00 AM, Since 2m18s
world_glob.service      1d3430ef.../192.168.122.31      active  running 2015-07-06 07:47:59 AM, Since 2m18s

Fixed #1128
  • Loading branch information
wuqixuan committed Jul 6, 2015
1 parent a2174a2 commit b406881
Show file tree
Hide file tree
Showing 12 changed files with 48 additions and 11 deletions.
2 changes: 1 addition & 1 deletion agent/unit_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ func TestMarshalJSON(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error marshalling: %v", err)
}
want = `{"Cache":{"bar.service":{"LoadState":"","ActiveState":"inactive","SubState":"","MachineID":"asdf","UnitHash":"","UnitName":"bar.service"},"foo.service":{"LoadState":"","ActiveState":"active","SubState":"","MachineID":"asdf","UnitHash":"","UnitName":"foo.service"}},"ToPublish":{"woof.service":{"LoadState":"","ActiveState":"active","SubState":"","MachineID":"asdf","UnitHash":"","UnitName":"woof.service"}}}`
want = `{"Cache":{"bar.service":{"LoadState":"","ActiveState":"inactive","SubState":"","MachineID":"asdf","UnitHash":"","UnitName":"bar.service","ActiveEnterTimestamp":0},"foo.service":{"LoadState":"","ActiveState":"active","SubState":"","MachineID":"asdf","UnitHash":"","UnitName":"foo.service","ActiveEnterTimestamp":0}},"ToPublish":{"woof.service":{"LoadState":"","ActiveState":"active","SubState":"","MachineID":"asdf","UnitHash":"","UnitName":"woof.service","ActiveEnterTimestamp":0}}}`
if string(got) != want {
t.Fatalf("Bad JSON representation: got\n%s\n\nwant\n%s", string(got), want)
}
Expand Down
11 changes: 10 additions & 1 deletion fleetctl/list_units.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ import (
"fmt"
"sort"
"strings"
"time"

"github.com/coreos/fleet/machine"
"github.com/coreos/fleet/schema"
)

const (
defaultListUnitsFields = "unit,machine,active,sub"
defaultListUnitsFields = "unit,machine,active,sub,uptime"
)

var (
Expand Down Expand Up @@ -90,6 +91,14 @@ Or, choose the columns to display:
}
return us.Hash
},
"uptime": func(us *schema.UnitState, full bool) string {
if us == nil || us.SystemdActiveState != "active"{
return "-"
}
tm := time.Unix(0, int64(us.SystemdActiveEnterTimestamp)*1000)
duration := time.Now().Sub(tm)
return fmt.Sprintf("%s, Since %ss", tm.Format("2006-01-02 03:04:05 PM"), strings.Split(duration.String(),".")[0])
},
}
)

Expand Down
3 changes: 3 additions & 0 deletions registry/unit_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ type unitStateModel struct {
SubState string `json:"subState"`
MachineState *machine.MachineState `json:"machineState"`
UnitHash string `json:"unitHash"`
ActiveEnterTimestamp uint64 `json:"ActiveEnterTimestamp"`
}

func modelToUnitState(usm *unitStateModel, name string) *unit.UnitState {
Expand All @@ -208,6 +209,7 @@ func modelToUnitState(usm *unitStateModel, name string) *unit.UnitState {
SubState: usm.SubState,
UnitHash: usm.UnitHash,
UnitName: name,
ActiveEnterTimestamp: usm.ActiveEnterTimestamp,
}

if usm.MachineState != nil {
Expand All @@ -233,6 +235,7 @@ func unitStateToModel(us *unit.UnitState) *unitStateModel {
ActiveState: us.ActiveState,
SubState: us.SubState,
UnitHash: us.UnitHash,
ActiveEnterTimestamp: us.ActiveEnterTimestamp,
}

if us.MachineID != "" {
Expand Down
16 changes: 12 additions & 4 deletions registry/unit_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func TestSaveUnitState(t *testing.T) {
r := &EtcdRegistry{kAPI: e, keyPrefix: "/fleet/"}
j := "foo.service"
mID := "mymachine"
us := unit.NewUnitState("abc", "def", "ghi", mID)
us := unit.NewUnitState("abc", "def", "ghi", mID, 1234567890)

// Saving nil unit state should fail
r.SaveUnitState(j, nil, time.Second)
Expand All @@ -123,7 +123,7 @@ func TestSaveUnitState(t *testing.T) {
us.UnitHash = "quickbrownfox"
r.SaveUnitState(j, us, time.Second)

json := `{"loadState":"abc","activeState":"def","subState":"ghi","machineState":{"ID":"mymachine","PublicIP":"","Metadata":null,"Version":""},"unitHash":"quickbrownfox"}`
json := `{"loadState":"abc","activeState":"def","subState":"ghi","machineState":{"ID":"mymachine","PublicIP":"","Metadata":null,"Version":""},"unitHash":"quickbrownfox","ActiveEnterTimestamp":1234567890}`
p1 := "/fleet/state/foo.service"
p2 := "/fleet/states/foo.service/mymachine"
want := []action{
Expand Down Expand Up @@ -204,13 +204,15 @@ func TestUnitStateToModel(t *testing.T) {
MachineID: "",
UnitHash: "",
UnitName: "name",
ActiveEnterTimestamp: 0,
},
want: &unitStateModel{
LoadState: "foo",
ActiveState: "bar",
SubState: "baz",
MachineState: nil,
UnitHash: "",
ActiveEnterTimestamp: 0,
},
},
{
Expand All @@ -222,13 +224,15 @@ func TestUnitStateToModel(t *testing.T) {
MachineID: "",
UnitHash: "heh",
UnitName: "name",
ActiveEnterTimestamp: 1234567890,
},
want: &unitStateModel{
LoadState: "foo",
ActiveState: "bar",
SubState: "baz",
MachineState: nil,
UnitHash: "heh",
ActiveEnterTimestamp: 1234567890,
},
},
{
Expand All @@ -239,13 +243,15 @@ func TestUnitStateToModel(t *testing.T) {
MachineID: "woof",
UnitHash: "miaow",
UnitName: "name",
ActiveEnterTimestamp: 54321,
},
want: &unitStateModel{
LoadState: "foo",
ActiveState: "bar",
SubState: "baz",
MachineState: &machine.MachineState{ID: "woof"},
UnitHash: "miaow",
ActiveEnterTimestamp: 54321,
},
},
} {
Expand All @@ -266,25 +272,27 @@ func TestModelToUnitState(t *testing.T) {
want: nil,
},
{
in: &unitStateModel{"foo", "bar", "baz", nil, ""},
in: &unitStateModel{"foo", "bar", "baz", nil, "", 1234567890},
want: &unit.UnitState{
LoadState: "foo",
ActiveState: "bar",
SubState: "baz",
MachineID: "",
UnitHash: "",
UnitName: "name",
ActiveEnterTimestamp: 1234567890,
},
},
{
in: &unitStateModel{"z", "x", "y", &machine.MachineState{ID: "abcd"}, ""},
in: &unitStateModel{"z", "x", "y", &machine.MachineState{ID: "abcd"}, "", 987654321},
want: &unit.UnitState{
LoadState: "z",
ActiveState: "x",
SubState: "y",
MachineID: "abcd",
UnitHash: "",
UnitName: "name",
ActiveEnterTimestamp: 987654321,
},
},
} {
Expand Down
2 changes: 2 additions & 0 deletions schema/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ func MapUnitStateToSchemaUnitState(entity *unit.UnitState) *UnitState {
SystemdLoadState: entity.LoadState,
SystemdActiveState: entity.ActiveState,
SystemdSubState: entity.SubState,
SystemdActiveEnterTimestamp: entity.ActiveEnterTimestamp,
}

return &us
Expand All @@ -136,6 +137,7 @@ func MapSchemaUnitStatesToUnitStates(entities []*UnitState) []*unit.UnitState {
LoadState: e.SystemdLoadState,
ActiveState: e.SystemdActiveState,
SubState: e.SystemdSubState,
ActiveEnterTimestamp: e.SystemdActiveEnterTimestamp,
}
}

Expand Down
1 change: 1 addition & 0 deletions schema/v1-gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ type UnitState struct {
SystemdLoadState string `json:"systemdLoadState,omitempty"`

SystemdSubState string `json:"systemdSubState,omitempty"`
SystemdActiveEnterTimestamp uint64 `json:"systemdActiveEnterTimestamp,omitempty"`
}

type UnitStatePage struct {
Expand Down
11 changes: 11 additions & 0 deletions systemd/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,17 @@ func (m *systemdUnitManager) GetUnitStates(filter pkg.Set) (map[string]*unit.Uni
states[name] = us
}

// add Active enter time to UnitState
for name, us := range states {
prop, err := m.systemd.GetUnitProperty(name, "ActiveEnterTimestamp")
if err != nil {
return nil, err
}

us.ActiveEnterTimestamp = prop.Value.Value().(uint64)
states[name] = us
}

return states, nil
}

Expand Down
2 changes: 1 addition & 1 deletion unit/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (fum *FakeUnitManager) GetUnitStates(filter pkg.Set) (map[string]*UnitState
states := make(map[string]*UnitState)
for _, name := range filter.Values() {
if _, ok := fum.u[name]; ok {
states[name] = &UnitState{"loaded", "active", "running", "", "", name}
states[name] = &UnitState{"loaded", "active", "running", "", "", name, 0}
}
}

Expand Down
2 changes: 1 addition & 1 deletion unit/fake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func TestFakeUnitManagerLoadUnload(t *testing.T) {
t.Fatalf("Expected non-nil UnitState")
}

eus := NewUnitState("loaded", "active", "running", "")
eus := NewUnitState("loaded", "active", "running", "", 0)
if !reflect.DeepEqual(*us, *eus) {
t.Fatalf("Expected UnitState %v, got %v", eus, *us)
}
Expand Down
2 changes: 1 addition & 1 deletion unit/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestUnitStateGeneratorSubscribeLifecycle(t *testing.T) {

// subscribed to foo.service so we should get a heartbeat
expect := []UnitStateHeartbeat{
UnitStateHeartbeat{Name: "foo.service", State: &UnitState{"loaded", "active", "running", "", "", "foo.service"}},
UnitStateHeartbeat{Name: "foo.service", State: &UnitState{"loaded", "active", "running", "", "", "foo.service", 0}},
}
assertGenerateUnitStateHeartbeats(t, um, gen, expect)

Expand Down
4 changes: 3 additions & 1 deletion unit/unit.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,16 @@ type UnitState struct {
MachineID string
UnitHash string
UnitName string
ActiveEnterTimestamp uint64
}

func NewUnitState(loadState, activeState, subState, mID string) *UnitState {
func NewUnitState(loadState, activeState, subState, mID string, activeEnterTimestamp uint64) *UnitState {
return &UnitState{
LoadState: loadState,
ActiveState: activeState,
SubState: subState,
MachineID: mID,
ActiveEnterTimestamp: activeEnterTimestamp,
}
}

Expand Down
3 changes: 2 additions & 1 deletion unit/unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,10 @@ func TestNewUnitState(t *testing.T) {
ActiveState: "as",
SubState: "ss",
MachineID: "id",
ActiveEnterTimestamp: 1234567890,
}

got := NewUnitState("ls", "as", "ss", "id")
got := NewUnitState("ls", "as", "ss", "id", 1234567890)
if !reflect.DeepEqual(got, want) {
t.Fatalf("NewUnitState did not create a correct UnitState: got %s, want %s", got, want)
}
Expand Down

0 comments on commit b406881

Please sign in to comment.