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

Commit b406881

Browse files
author
wuqixuan
committed
fleetctl: Support service uptime as part of a list-units output
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
1 parent a2174a2 commit b406881

12 files changed

+48
-11
lines changed

agent/unit_state_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ func TestMarshalJSON(t *testing.T) {
780780
if err != nil {
781781
t.Fatalf("unexpected error marshalling: %v", err)
782782
}
783-
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"}}}`
783+
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}}}`
784784
if string(got) != want {
785785
t.Fatalf("Bad JSON representation: got\n%s\n\nwant\n%s", string(got), want)
786786
}

fleetctl/list_units.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@ import (
1818
"fmt"
1919
"sort"
2020
"strings"
21+
"time"
2122

2223
"github.com/coreos/fleet/machine"
2324
"github.com/coreos/fleet/schema"
2425
)
2526

2627
const (
27-
defaultListUnitsFields = "unit,machine,active,sub"
28+
defaultListUnitsFields = "unit,machine,active,sub,uptime"
2829
)
2930

3031
var (
@@ -90,6 +91,14 @@ Or, choose the columns to display:
9091
}
9192
return us.Hash
9293
},
94+
"uptime": func(us *schema.UnitState, full bool) string {
95+
if us == nil || us.SystemdActiveState != "active"{
96+
return "-"
97+
}
98+
tm := time.Unix(0, int64(us.SystemdActiveEnterTimestamp)*1000)
99+
duration := time.Now().Sub(tm)
100+
return fmt.Sprintf("%s, Since %ss", tm.Format("2006-01-02 03:04:05 PM"), strings.Split(duration.String(),".")[0])
101+
},
93102
}
94103
)
95104

registry/unit_state.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ type unitStateModel struct {
195195
SubState string `json:"subState"`
196196
MachineState *machine.MachineState `json:"machineState"`
197197
UnitHash string `json:"unitHash"`
198+
ActiveEnterTimestamp uint64 `json:"ActiveEnterTimestamp"`
198199
}
199200

200201
func modelToUnitState(usm *unitStateModel, name string) *unit.UnitState {
@@ -208,6 +209,7 @@ func modelToUnitState(usm *unitStateModel, name string) *unit.UnitState {
208209
SubState: usm.SubState,
209210
UnitHash: usm.UnitHash,
210211
UnitName: name,
212+
ActiveEnterTimestamp: usm.ActiveEnterTimestamp,
211213
}
212214

213215
if usm.MachineState != nil {
@@ -233,6 +235,7 @@ func unitStateToModel(us *unit.UnitState) *unitStateModel {
233235
ActiveState: us.ActiveState,
234236
SubState: us.SubState,
235237
UnitHash: us.UnitHash,
238+
ActiveEnterTimestamp: us.ActiveEnterTimestamp,
236239
}
237240

238241
if us.MachineID != "" {

registry/unit_state_test.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ func TestSaveUnitState(t *testing.T) {
101101
r := &EtcdRegistry{kAPI: e, keyPrefix: "/fleet/"}
102102
j := "foo.service"
103103
mID := "mymachine"
104-
us := unit.NewUnitState("abc", "def", "ghi", mID)
104+
us := unit.NewUnitState("abc", "def", "ghi", mID, 1234567890)
105105

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

126-
json := `{"loadState":"abc","activeState":"def","subState":"ghi","machineState":{"ID":"mymachine","PublicIP":"","Metadata":null,"Version":""},"unitHash":"quickbrownfox"}`
126+
json := `{"loadState":"abc","activeState":"def","subState":"ghi","machineState":{"ID":"mymachine","PublicIP":"","Metadata":null,"Version":""},"unitHash":"quickbrownfox","ActiveEnterTimestamp":1234567890}`
127127
p1 := "/fleet/state/foo.service"
128128
p2 := "/fleet/states/foo.service/mymachine"
129129
want := []action{
@@ -204,13 +204,15 @@ func TestUnitStateToModel(t *testing.T) {
204204
MachineID: "",
205205
UnitHash: "",
206206
UnitName: "name",
207+
ActiveEnterTimestamp: 0,
207208
},
208209
want: &unitStateModel{
209210
LoadState: "foo",
210211
ActiveState: "bar",
211212
SubState: "baz",
212213
MachineState: nil,
213214
UnitHash: "",
215+
ActiveEnterTimestamp: 0,
214216
},
215217
},
216218
{
@@ -222,13 +224,15 @@ func TestUnitStateToModel(t *testing.T) {
222224
MachineID: "",
223225
UnitHash: "heh",
224226
UnitName: "name",
227+
ActiveEnterTimestamp: 1234567890,
225228
},
226229
want: &unitStateModel{
227230
LoadState: "foo",
228231
ActiveState: "bar",
229232
SubState: "baz",
230233
MachineState: nil,
231234
UnitHash: "heh",
235+
ActiveEnterTimestamp: 1234567890,
232236
},
233237
},
234238
{
@@ -239,13 +243,15 @@ func TestUnitStateToModel(t *testing.T) {
239243
MachineID: "woof",
240244
UnitHash: "miaow",
241245
UnitName: "name",
246+
ActiveEnterTimestamp: 54321,
242247
},
243248
want: &unitStateModel{
244249
LoadState: "foo",
245250
ActiveState: "bar",
246251
SubState: "baz",
247252
MachineState: &machine.MachineState{ID: "woof"},
248253
UnitHash: "miaow",
254+
ActiveEnterTimestamp: 54321,
249255
},
250256
},
251257
} {
@@ -266,25 +272,27 @@ func TestModelToUnitState(t *testing.T) {
266272
want: nil,
267273
},
268274
{
269-
in: &unitStateModel{"foo", "bar", "baz", nil, ""},
275+
in: &unitStateModel{"foo", "bar", "baz", nil, "", 1234567890},
270276
want: &unit.UnitState{
271277
LoadState: "foo",
272278
ActiveState: "bar",
273279
SubState: "baz",
274280
MachineID: "",
275281
UnitHash: "",
276282
UnitName: "name",
283+
ActiveEnterTimestamp: 1234567890,
277284
},
278285
},
279286
{
280-
in: &unitStateModel{"z", "x", "y", &machine.MachineState{ID: "abcd"}, ""},
287+
in: &unitStateModel{"z", "x", "y", &machine.MachineState{ID: "abcd"}, "", 987654321},
281288
want: &unit.UnitState{
282289
LoadState: "z",
283290
ActiveState: "x",
284291
SubState: "y",
285292
MachineID: "abcd",
286293
UnitHash: "",
287294
UnitName: "name",
295+
ActiveEnterTimestamp: 987654321,
288296
},
289297
},
290298
} {

schema/mapper.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ func MapUnitStateToSchemaUnitState(entity *unit.UnitState) *UnitState {
121121
SystemdLoadState: entity.LoadState,
122122
SystemdActiveState: entity.ActiveState,
123123
SystemdSubState: entity.SubState,
124+
SystemdActiveEnterTimestamp: entity.ActiveEnterTimestamp,
124125
}
125126

126127
return &us
@@ -136,6 +137,7 @@ func MapSchemaUnitStatesToUnitStates(entities []*UnitState) []*unit.UnitState {
136137
LoadState: e.SystemdLoadState,
137138
ActiveState: e.SystemdActiveState,
138139
SubState: e.SystemdSubState,
140+
ActiveEnterTimestamp: e.SystemdActiveEnterTimestamp,
139141
}
140142
}
141143

schema/v1-gen.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ type UnitState struct {
141141
SystemdLoadState string `json:"systemdLoadState,omitempty"`
142142

143143
SystemdSubState string `json:"systemdSubState,omitempty"`
144+
SystemdActiveEnterTimestamp uint64 `json:"systemdActiveEnterTimestamp,omitempty"`
144145
}
145146

146147
type UnitStatePage struct {

systemd/manager.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,17 @@ func (m *systemdUnitManager) GetUnitStates(filter pkg.Set) (map[string]*unit.Uni
238238
states[name] = us
239239
}
240240

241+
// add Active enter time to UnitState
242+
for name, us := range states {
243+
prop, err := m.systemd.GetUnitProperty(name, "ActiveEnterTimestamp")
244+
if err != nil {
245+
return nil, err
246+
}
247+
248+
us.ActiveEnterTimestamp = prop.Value.Value().(uint64)
249+
states[name] = us
250+
}
251+
241252
return states, nil
242253
}
243254

unit/fake.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func (fum *FakeUnitManager) GetUnitStates(filter pkg.Set) (map[string]*UnitState
8383
states := make(map[string]*UnitState)
8484
for _, name := range filter.Values() {
8585
if _, ok := fum.u[name]; ok {
86-
states[name] = &UnitState{"loaded", "active", "running", "", "", name}
86+
states[name] = &UnitState{"loaded", "active", "running", "", "", name, 0}
8787
}
8888
}
8989

unit/fake_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func TestFakeUnitManagerLoadUnload(t *testing.T) {
6060
t.Fatalf("Expected non-nil UnitState")
6161
}
6262

63-
eus := NewUnitState("loaded", "active", "running", "")
63+
eus := NewUnitState("loaded", "active", "running", "", 0)
6464
if !reflect.DeepEqual(*us, *eus) {
6565
t.Fatalf("Expected UnitState %v, got %v", eus, *us)
6666
}

unit/generator_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestUnitStateGeneratorSubscribeLifecycle(t *testing.T) {
4949

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

unit/unit.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,14 +177,16 @@ type UnitState struct {
177177
MachineID string
178178
UnitHash string
179179
UnitName string
180+
ActiveEnterTimestamp uint64
180181
}
181182

182-
func NewUnitState(loadState, activeState, subState, mID string) *UnitState {
183+
func NewUnitState(loadState, activeState, subState, mID string, activeEnterTimestamp uint64) *UnitState {
183184
return &UnitState{
184185
LoadState: loadState,
185186
ActiveState: activeState,
186187
SubState: subState,
187188
MachineID: mID,
189+
ActiveEnterTimestamp: activeEnterTimestamp,
188190
}
189191
}
190192

unit/unit_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,10 @@ func TestNewUnitState(t *testing.T) {
9797
ActiveState: "as",
9898
SubState: "ss",
9999
MachineID: "id",
100+
ActiveEnterTimestamp: 1234567890,
100101
}
101102

102-
got := NewUnitState("ls", "as", "ss", "id")
103+
got := NewUnitState("ls", "as", "ss", "id", 1234567890)
103104
if !reflect.DeepEqual(got, want) {
104105
t.Fatalf("NewUnitState did not create a correct UnitState: got %s, want %s", got, want)
105106
}

0 commit comments

Comments
 (0)