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

Commit e2a8370

Browse files
committed
Merge pull request #1544 from endocode/dongsu/fleetctl-unit-action-fxtests
functional: more fleetctl unit action tests
2 parents 071230a + 7bcdb51 commit e2a8370

4 files changed

Lines changed: 299 additions & 17 deletions

File tree

functional/platform/cluster.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ type Cluster interface {
3535
// client operations
3636
Fleetctl(m Member, args ...string) (string, string, error)
3737
FleetctlWithInput(m Member, input string, args ...string) (string, string, error)
38+
WaitForNUnits(Member, int) (map[string][]util.UnitState, error)
3839
WaitForNActiveUnits(Member, int) (map[string][]util.UnitState, error)
40+
WaitForNUnitFiles(Member, int) (map[string][]util.UnitFileState, error)
3941
WaitForNMachines(Member, int) ([]string, error)
4042
}
4143

functional/platform/nspawn.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,47 @@ func (nc *nspawnCluster) FleetctlWithInput(m Member, input string, args ...strin
128128
return util.RunFleetctlWithInput(input, args...)
129129
}
130130

131+
// WaitForNUnits runs fleetctl list-units to verify the actual number of units
132+
// matched with the given expected number. It periodically runs list-units
133+
// waiting until list-units actually shows the expected units.
134+
func (nc *nspawnCluster) WaitForNUnits(m Member, expectedUnits int) (map[string][]util.UnitState, error) {
135+
var nUnits int
136+
retStates := make(map[string][]util.UnitState)
137+
checkListUnits := func() bool {
138+
outListUnits, _, err := nc.Fleetctl(m, "list-units", "--no-legend", "--full", "--fields", "unit,active,machine")
139+
if err != nil {
140+
return false
141+
}
142+
// NOTE: There's no need to check if outListUnits is expected to be empty,
143+
// because ParseUnitStates() implicitly filters out such cases.
144+
// However, in case of ParseUnitStates() going away, we should not
145+
// forget about such special cases.
146+
units := strings.Split(strings.TrimSpace(outListUnits), "\n")
147+
allStates := util.ParseUnitStates(units)
148+
nUnits = len(allStates)
149+
if nUnits != expectedUnits {
150+
return false
151+
}
152+
153+
for _, state := range allStates {
154+
name := state.Name
155+
if _, ok := retStates[name]; !ok {
156+
retStates[name] = []util.UnitState{}
157+
}
158+
retStates[name] = append(retStates[name], state)
159+
}
160+
return true
161+
}
162+
163+
timeout, err := util.WaitForState(checkListUnits)
164+
if err != nil {
165+
return nil, fmt.Errorf("failed to find %d units within %v (last found: %d)",
166+
expectedUnits, timeout, nUnits)
167+
}
168+
169+
return retStates, nil
170+
}
171+
131172
func (nc *nspawnCluster) WaitForNActiveUnits(m Member, count int) (map[string][]util.UnitState, error) {
132173
var nactive int
133174
states := make(map[string][]util.UnitState)
@@ -165,6 +206,49 @@ func (nc *nspawnCluster) WaitForNActiveUnits(m Member, count int) (map[string][]
165206
return states, nil
166207
}
167208

209+
// WaitForNUnitFiles runs fleetctl list-unit-files to verify the actual number of units
210+
// matched with the given expected number. It periodically runs list-unit-files
211+
// waiting until list-unit-files actually shows the expected units.
212+
func (nc *nspawnCluster) WaitForNUnitFiles(m Member, expectedUnits int) (map[string][]util.UnitFileState, error) {
213+
var nUnits int
214+
retStates := make(map[string][]util.UnitFileState)
215+
216+
checkListUnitFiles := func() bool {
217+
outListUnitFiles, _, err := nc.Fleetctl(m, "list-unit-files", "--no-legend", "--full", "--fields", "unit,dstate,state")
218+
if err != nil {
219+
return false
220+
}
221+
// NOTE: There's no need to check if outListUnits is expected to be empty,
222+
// because ParseUnitFileStates() implicitly filters out such cases.
223+
// However, in case of ParseUnitFileStates() going away, we should not
224+
// forget about such special cases.
225+
units := strings.Split(strings.TrimSpace(outListUnitFiles), "\n")
226+
allStates := util.ParseUnitFileStates(units)
227+
nUnits = len(allStates)
228+
if nUnits != expectedUnits {
229+
// retry until number of units matched
230+
return false
231+
}
232+
233+
for _, state := range allStates {
234+
name := state.Name
235+
if _, ok := retStates[name]; !ok {
236+
retStates[name] = []util.UnitFileState{}
237+
}
238+
retStates[name] = append(retStates[name], state)
239+
}
240+
return true
241+
}
242+
243+
timeout, err := util.WaitForState(checkListUnitFiles)
244+
if err != nil {
245+
return nil, fmt.Errorf("failed to find %d units within %v (last found: %d)",
246+
expectedUnits, timeout, nUnits)
247+
}
248+
249+
return retStates, nil
250+
}
251+
168252
func (nc *nspawnCluster) WaitForNMachines(m Member, count int) ([]string, error) {
169253
var machines []string
170254
timeout := 10 * time.Second

functional/unit_action_test.go

Lines changed: 197 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
package functional
1616

1717
import (
18+
"io/ioutil"
19+
"path"
1820
"strings"
1921
"testing"
2022

@@ -53,6 +55,9 @@ func TestUnitRunnable(t *testing.T) {
5355
}
5456
}
5557

58+
// TestUnitSubmit checks if a unit becomes submitted and destroyed successfully.
59+
// First it submits a unit, and destroys the unit, verifies it's destroyed,
60+
// finally submits the unit again.
5661
func TestUnitSubmit(t *testing.T) {
5762
cluster, err := platform.NewNspawnCluster("smoke")
5863
if err != nil {
@@ -69,47 +74,133 @@ func TestUnitSubmit(t *testing.T) {
6974
t.Fatal(err)
7075
}
7176

77+
unitFile := "fixtures/units/hello.service"
78+
7279
// submit a unit and assert it shows up
73-
if _, _, err := cluster.Fleetctl(m, "submit", "fixtures/units/hello.service"); err != nil {
80+
if _, _, err := cluster.Fleetctl(m, "submit", unitFile); err != nil {
7481
t.Fatalf("Unable to submit fleet unit: %v", err)
7582
}
76-
stdout, _, err := cluster.Fleetctl(m, "list-units", "--no-legend")
83+
84+
// wait until the unit gets submitted up to 15 seconds
85+
listUnitStates, err := cluster.WaitForNUnitFiles(m, 1)
7786
if err != nil {
78-
t.Fatalf("Failed to run list-units: %v", err)
87+
t.Fatalf("Failed to run list-unit-files: %v", err)
7988
}
80-
units := strings.Split(strings.TrimSpace(stdout), "\n")
81-
if len(units) != 1 {
82-
t.Fatalf("Did not find 1 unit in cluster: \n%s", stdout)
89+
90+
// given unit name must be there in list-unit-files
91+
_, found := listUnitStates[path.Base(unitFile)]
92+
if len(listUnitStates) != 1 || !found {
93+
t.Fatalf("Expected %s to be unit file, got %v", path.Base(unitFile), listUnitStates)
8394
}
8495

8596
// submitting the same unit should not fail
86-
if _, _, err = cluster.Fleetctl(m, "submit", "fixtures/units/hello.service"); err != nil {
97+
if _, _, err = cluster.Fleetctl(m, "submit", unitFile); err != nil {
8798
t.Fatalf("Expected no failure when double-submitting unit, got this: %v", err)
8899
}
89100

90101
// destroy the unit and ensure it disappears from the unit list
91-
if _, _, err := cluster.Fleetctl(m, "destroy", "fixtures/units/hello.service"); err != nil {
102+
if _, _, err := cluster.Fleetctl(m, "destroy", unitFile); err != nil {
92103
t.Fatalf("Failed to destroy unit: %v", err)
93104
}
94-
stdout, _, err = cluster.Fleetctl(m, "list-units", "--no-legend")
105+
// wait until the unit gets destroyed up to 15 seconds
106+
listUnitStates, err = cluster.WaitForNUnitFiles(m, 0)
95107
if err != nil {
96-
t.Fatalf("Failed to run list-units: %v", err)
108+
t.Fatalf("Failed to run list-unit-files: %v", err)
97109
}
98-
if strings.TrimSpace(stdout) != "" {
99-
t.Fatalf("Did not find 0 units in cluster: \n%s", stdout)
110+
if len(listUnitStates) != 0 {
111+
t.Fatalf("Expected nil unit file list, got %v", listUnitStates)
100112
}
101113

102114
// submitting the unit after destruction should succeed
103-
if _, _, err := cluster.Fleetctl(m, "submit", "fixtures/units/hello.service"); err != nil {
115+
if _, _, err := cluster.Fleetctl(m, "submit", unitFile); err != nil {
104116
t.Fatalf("Unable to submit fleet unit: %v", err)
105117
}
106-
stdout, _, err = cluster.Fleetctl(m, "list-units", "--no-legend")
118+
119+
// wait until the unit gets submitted up to 15 seconds
120+
listUnitStates, err = cluster.WaitForNUnitFiles(m, 1)
121+
if err != nil {
122+
t.Fatalf("Failed to run list-unit-files: %v", err)
123+
}
124+
125+
// given unit name must be there in list-unit-files
126+
_, found = listUnitStates[path.Base(unitFile)]
127+
if len(listUnitStates) != 1 || !found {
128+
t.Fatalf("Expected %s to be unit file, got %v", path.Base(unitFile), listUnitStates)
129+
}
130+
}
131+
132+
// TestUnitLoad checks if a unit becomes loaded and unloaded successfully.
133+
// First it load a unit, and unloads the unit, verifies it's unloaded,
134+
// finally loads the unit again.
135+
func TestUnitLoad(t *testing.T) {
136+
cluster, err := platform.NewNspawnCluster("smoke")
137+
if err != nil {
138+
t.Fatal(err)
139+
}
140+
defer cluster.Destroy()
141+
142+
m, err := cluster.CreateMember()
143+
if err != nil {
144+
t.Fatal(err)
145+
}
146+
_, err = cluster.WaitForNMachines(m, 1)
147+
if err != nil {
148+
t.Fatal(err)
149+
}
150+
151+
unitFile := "fixtures/units/hello.service"
152+
153+
// load a unit and assert it shows up
154+
_, _, err = cluster.Fleetctl(m, "load", unitFile)
155+
if err != nil {
156+
t.Fatalf("Unable to load fleet unit: %v", err)
157+
}
158+
159+
// wait until the unit gets loaded up to 15 seconds
160+
listUnitStates, err := cluster.WaitForNUnits(m, 1)
161+
if err != nil {
162+
t.Fatalf("Failed to run list-units: %v", err)
163+
}
164+
165+
// given unit name must be there in list-units
166+
_, found := listUnitStates[path.Base(unitFile)]
167+
if len(listUnitStates) != 1 || !found {
168+
t.Fatalf("Expected %s to be unit, got %v", path.Base(unitFile), listUnitStates)
169+
}
170+
171+
// unload the unit and ensure it disappears from the unit list
172+
_, _, err = cluster.Fleetctl(m, "unload", unitFile)
173+
if err != nil {
174+
t.Fatalf("Failed to unload unit: %v", err)
175+
}
176+
177+
// wait until the unit gets unloaded up to 15 seconds
178+
listUnitStates, err = cluster.WaitForNUnits(m, 0)
107179
if err != nil {
108180
t.Fatalf("Failed to run list-units: %v", err)
109181
}
110-
units = strings.Split(strings.TrimSpace(stdout), "\n")
111-
if len(units) != 1 {
112-
t.Fatalf("Did not find 1 unit in cluster: \n%s", stdout)
182+
183+
// given unit name must be there in list-units
184+
if len(listUnitStates) != 0 {
185+
t.Fatalf("Expected nil unit list, got %v", listUnitStates)
186+
}
187+
188+
// loading the unit after destruction should succeed
189+
_, _, err = cluster.Fleetctl(m, "load", unitFile)
190+
if err != nil {
191+
t.Fatalf("Unable to load fleet unit: %v", err)
192+
}
193+
194+
// wait until the unit gets loaded up to 15 seconds
195+
listUnitStates, err = cluster.WaitForNUnits(m, 1)
196+
if err != nil {
197+
t.Fatalf("Failed to run list-units: %v", err)
198+
}
199+
200+
// given unit name must be there in list-units
201+
_, found = listUnitStates[path.Base(unitFile)]
202+
if len(listUnitStates) != 1 || !found {
203+
t.Fatalf("Expected %s to be unit, got %v", path.Base(unitFile), listUnitStates)
113204
}
114205
}
115206

@@ -224,3 +315,92 @@ func TestUnitSSHActions(t *testing.T) {
224315
t.Errorf("Could not find expected string in journal output:\n%s", stdout)
225316
}
226317
}
318+
319+
// TestUnitCat simply compares body of a unit file with that of a unit fetched
320+
// from the remote cluster using "fleetctl cat".
321+
func TestUnitCat(t *testing.T) {
322+
cluster, err := platform.NewNspawnCluster("smoke")
323+
if err != nil {
324+
t.Fatal(err)
325+
}
326+
defer cluster.Destroy()
327+
328+
m, err := cluster.CreateMember()
329+
if err != nil {
330+
t.Fatal(err)
331+
}
332+
_, err = cluster.WaitForNMachines(m, 1)
333+
if err != nil {
334+
t.Fatal(err)
335+
}
336+
337+
// read a sample unit file to a buffer
338+
unitFile := "fixtures/units/hello.service"
339+
fileBuf, err := ioutil.ReadFile(unitFile)
340+
if err != nil {
341+
t.Fatal(err)
342+
}
343+
fileBody := strings.TrimSpace(string(fileBuf))
344+
345+
// submit a unit and assert it shows up
346+
_, _, err = cluster.Fleetctl(m, "submit", unitFile)
347+
if err != nil {
348+
t.Fatalf("Unable to submit fleet unit: %v", err)
349+
}
350+
// wait until the unit gets submitted up to 15 seconds
351+
_, err = cluster.WaitForNUnitFiles(m, 1)
352+
if err != nil {
353+
t.Fatalf("Failed to run list-units: %v", err)
354+
}
355+
356+
// cat the unit file and compare it with the original unit body
357+
stdout, _, err := cluster.Fleetctl(m, "cat", path.Base(unitFile))
358+
if err != nil {
359+
t.Fatalf("Unable to submit fleet unit: %v", err)
360+
}
361+
catBody := strings.TrimSpace(stdout)
362+
363+
if strings.Compare(catBody, fileBody) != 0 {
364+
t.Fatalf("unit body changed across fleetctl cat: \noriginal:%s\nnew:%s", fileBody, catBody)
365+
}
366+
}
367+
368+
// TestUnitStatus simply checks "fleetctl status hello.service" actually works.
369+
func TestUnitStatus(t *testing.T) {
370+
cluster, err := platform.NewNspawnCluster("smoke")
371+
if err != nil {
372+
t.Fatal(err)
373+
}
374+
defer cluster.Destroy()
375+
376+
m, err := cluster.CreateMember()
377+
if err != nil {
378+
t.Fatal(err)
379+
}
380+
_, err = cluster.WaitForNMachines(m, 1)
381+
if err != nil {
382+
t.Fatal(err)
383+
}
384+
385+
unitFile := "fixtures/units/hello.service"
386+
387+
// Load a unit and print out status.
388+
// Without loading a unit, it's impossible to run fleetctl status
389+
_, _, err = cluster.Fleetctl(m, "load", unitFile)
390+
if err != nil {
391+
t.Fatalf("Unable to load a fleet unit: %v", err)
392+
}
393+
394+
// wait until the unit gets loaded up to 15 seconds
395+
_, err = cluster.WaitForNUnits(m, 1)
396+
if err != nil {
397+
t.Fatalf("Failed to run list-units: %v", err)
398+
}
399+
400+
stdout, stderr, err := cluster.Fleetctl(m,
401+
"--strict-host-key-checking=false", "status", path.Base(unitFile))
402+
if !strings.Contains(stdout, "Loaded: loaded") {
403+
t.Errorf("Could not find expected string in status output:\n%s\nstderr:\n%s",
404+
stdout, stderr)
405+
}
406+
}

0 commit comments

Comments
 (0)