Example #1
0
func TestMachinesList(t *testing.T) {
	fr := registry.NewFakeRegistry()
	fr.SetMachines([]machine.MachineState{
		{ID: "XXX", PublicIP: "", Metadata: nil},
		{ID: "YYY", PublicIP: "1.2.3.4", Metadata: map[string]string{"ping": "pong"}},
	})
	resource := &machinesResource{fr}
	rw := httptest.NewRecorder()
	req, err := http.NewRequest("GET", "http://example.com", nil)
	if err != nil {
		t.Fatalf("Failed creating http.Request: %v", err)
	}

	resource.ServeHTTP(rw, req)
	if rw.Code != http.StatusOK {
		t.Errorf("Expected 200, got %d", rw.Code)
	}

	ct := rw.HeaderMap["Content-Type"]
	if len(ct) != 1 {
		t.Errorf("Response has wrong number of Content-Type values: %v", ct)
	} else if ct[0] != "application/json" {
		t.Errorf("Expected application/json, got %s", ct)
	}

	if rw.Body == nil {
		t.Error("Received nil response body")
	} else {
		body := rw.Body.String()
		expected := `{"machines":[{"id":"XXX"},{"id":"YYY","metadata":{"ping":"pong"},"primaryIP":"1.2.3.4"}]}`
		if body != expected {
			t.Errorf("Expected body:\n%s\n\nReceived body:\n%s\n", expected, body)
		}
	}
}
Example #2
0
func newFakeRegistryForSsh() registry.Registry {
	machines := []machine.MachineState{
		{"c31e44e1-f858-436e-933e-59c642517860", "1.2.3.4", map[string]string{"ping": "pong"}, "", resource.ResourceTuple{}},
		{"595989bb-cbb7-49ce-8726-722d6e157b4e", "5.6.7.8", map[string]string{"foo": "bar"}, "", resource.ResourceTuple{}},
		{"hello.service", "8.7.6.5", map[string]string{"foo": "bar"}, "", resource.ResourceTuple{}},
	}

	jobs := []job.Job{
		*job.NewJob("j1.service", unit.Unit{}),
		*job.NewJob("j2.service", unit.Unit{}),
		*job.NewJob("hello.service", unit.Unit{}),
	}

	states := map[string]*unit.UnitState{
		"j1.service":    unit.NewUnitState("loaded", "active", "listening", &machines[0]),
		"j2.service":    unit.NewUnitState("loaded", "inactive", "dead", &machines[1]),
		"hello.service": unit.NewUnitState("loaded", "inactive", "dead", &machines[2]),
	}

	reg := registry.NewFakeRegistry()
	reg.SetMachines(machines)
	reg.SetUnitStates(states)
	reg.SetJobs(jobs)

	return reg
}
Example #3
0
func setupRegistryForStart(echoAttempts int) {
	m1 := machine.MachineState{
		ID:       "c31e44e1-f858-436e-933e-59c642517860",
		PublicIP: "1.2.3.4",
		Metadata: map[string]string{"ping": "pong"},
	}
	m2 := machine.MachineState{
		ID:       "595989bb-cbb7-49ce-8726-722d6e157b4e",
		PublicIP: "5.6.7.8",
		Metadata: map[string]string{"foo": "bar"},
	}
	m3 := machine.MachineState{
		ID:       "520983A8-FB9C-4A68-B49C-CED5BB2E9D08",
		Metadata: map[string]string{"foo": "bar"},
	}

	js := unit.NewUnitState("loaded", "active", "listening", m1.ID)
	js2 := unit.NewUnitState("loaded", "inactive", "dead", m2.ID)
	js3 := unit.NewUnitState("loaded", "inactive", "dead", m2.ID)
	js4 := unit.NewUnitState("loaded", "inactive", "dead", m3.ID)

	states := map[string]*unit.UnitState{"pong.service": js, "hello.service": js2, "echo.service": js3, "private.service": js4}
	machines := []machine.MachineState{m1, m2, m3}

	reg := registry.NewFakeRegistry()
	reg.SetMachines(machines)
	reg.SetUnitStates(states)

	cAPI = &BlockedFakeRegistry{echoAttempts, *reg}
}
Example #4
0
func TestDefaultHandlers(t *testing.T) {
	tests := []struct {
		method string
		path   string
		code   int
	}{
		{"GET", "/", http.StatusMethodNotAllowed},
		{"GET", "/v1-alpha", http.StatusMethodNotAllowed},
		{"GET", "/bogus", http.StatusNotFound},
	}

	for i, tt := range tests {
		fr := registry.NewFakeRegistry()
		hdlr := NewServeMux(fr)
		rr := httptest.NewRecorder()

		req, err := http.NewRequest(tt.method, tt.path, nil)
		if err != nil {
			t.Errorf("case %d: failed setting up http.Request for test: %v", i, err)
			continue
		}

		hdlr.ServeHTTP(rr, req)

		err = assertErrorResponse(rr, tt.code)
		if err != nil {
			t.Errorf("case %d: %v", i, err)
		}
	}
}
Example #5
0
func newFakeRegistryForSsh() registry.Registry {
	// clear machineStates for every invocation
	machineStates = nil
	machines := []machine.MachineState{
		newMachineState("c31e44e1-f858-436e-933e-59c642517860", "1.2.3.4", map[string]string{"ping": "pong"}),
		newMachineState("595989bb-cbb7-49ce-8726-722d6e157b4e", "5.6.7.8", map[string]string{"foo": "bar"}),
		newMachineState("hello.service", "8.7.6.5", map[string]string{"foo": "bar"}),
	}

	jobs := []job.Job{
		*job.NewJob("j1.service", unit.Unit{}),
		*job.NewJob("j2.service", unit.Unit{}),
		*job.NewJob("hello.service", unit.Unit{}),
	}

	states := map[string]*unit.UnitState{
		"j1.service":    unit.NewUnitState("loaded", "active", "listening", machines[0].ID),
		"j2.service":    unit.NewUnitState("loaded", "inactive", "dead", machines[1].ID),
		"hello.service": unit.NewUnitState("loaded", "inactive", "dead", machines[2].ID),
	}

	reg := registry.NewFakeRegistry()
	reg.SetMachines(machines)
	reg.SetUnitStates(states)
	reg.SetJobs(jobs)

	return reg
}
Example #6
0
func TestAgentLoadUnloadJob(t *testing.T) {
	uManager := unit.NewFakeUnitManager()
	usGenerator := unit.NewUnitStateGenerator(uManager)
	fReg := registry.NewFakeRegistry()
	mach := &machine.FakeMachine{machine.MachineState{ID: "XXX"}}
	a, err := New(uManager, usGenerator, fReg, mach, DefaultTTL)
	if err != nil {
		t.Fatalf("Failed creating Agent: %v", err)
	}

	j := newTestJobFromUnitContents(t, "foo.service", "")
	err = a.loadJob(j)
	if err != nil {
		t.Fatalf("Failed calling Agent.loadJob: %v", err)
	}

	jobs, err := a.jobs()
	if err != nil {
		t.Fatalf("Failed calling Agent.jobs: %v", err)
	}

	jsLoaded := job.JobStateLoaded
	expectJobs := map[string]*job.Job{
		"foo.service": &job.Job{
			Name: "foo.service",
			UnitState: &unit.UnitState{
				LoadState:   "loaded",
				ActiveState: "active",
				SubState:    "running",
				MachineID:   "",
			},
			State: &jsLoaded,

			Unit:            unit.Unit{},
			TargetState:     job.JobState(""),
			TargetMachineID: "",
		},
	}

	if !reflect.DeepEqual(expectJobs, jobs) {
		t.Fatalf("Received unexpected collection of Jobs: %#v\nExpected: %#v", jobs, expectJobs)
	}

	a.unloadJob("foo.service")

	// This sucks, but we have to do it if Agent.unloadJob is going to spin
	// off the real work that matters in a goroutine
	time.Sleep(200)

	jobs, err = a.jobs()
	if err != nil {
		t.Fatalf("Failed calling Agent.jobs: %v", err)
	}

	expectJobs = map[string]*job.Job{}
	if !reflect.DeepEqual(expectJobs, jobs) {
		t.Fatalf("Received unexpected collection of Jobs: %#v\nExpected: %#v", jobs, expectJobs)
	}
}
Example #7
0
func TestAgentLoadStartStopUnit(t *testing.T) {
	uManager := unit.NewFakeUnitManager()
	usGenerator := unit.NewUnitStateGenerator(uManager)
	fReg := registry.NewFakeRegistry()
	mach := &machine.FakeMachine{MachineState: machine.MachineState{ID: "XXX"}}
	a := New(uManager, usGenerator, fReg, mach, time.Second)

	u := newTestUnitFromUnitContents(t, "foo.service", "")

	err := a.loadUnit(u)
	if err != nil {
		t.Fatalf("Failed calling Agent.loadUnit: %v", err)
	}

	err = a.startUnit("foo.service")
	if err != nil {
		t.Fatalf("Failed starting unit foo.service: %v", err)
	}

	units, err := a.units()
	if err != nil {
		t.Fatalf("Failed calling Agent.units: %v", err)
	}

	jsLaunched := job.JobStateLaunched
	expectUnits := unitStates{
		"foo.service": unitState{
			state: jsLaunched,
		},
	}

	if !reflect.DeepEqual(expectUnits, units) {
		t.Fatalf("Received unexpected collection of Units: %#v\nExpected: %#v", units, expectUnits)
	}

	err = a.stopUnit("foo.service")
	if err != nil {
		t.Fatalf("Failed stopping unit foo.service: %v", err)
	}

	units, err = a.units()
	if err != nil {
		t.Fatalf("Failed calling Agent.units: %v", err)
	}

	jsLoaded := job.JobStateLoaded
	expectUnits = unitStates{
		"foo.service": unitState{
			state: jsLoaded,
		},
	}

	if !reflect.DeepEqual(expectUnits, units) {
		t.Fatalf("Received unexpected collection of Units: %#v\nExpected: %#v", units, expectUnits)
	}
}
Example #8
0
func newFakeRegistryForCheckVersion(v string) registry.Registry {
	sv, err := semver.NewVersion(v)
	if err != nil {
		panic(err)
	}

	reg := registry.NewFakeRegistry()
	reg.SetLatestVersion(*sv)
	return reg
}
Example #9
0
func newFakeRegistryForCommands(unitPrefix string, unitCount int, template bool) client.API {
	// clear machineStates for every invocation
	machineStates = nil
	machines := []machine.MachineState{
		newMachineState("c31e44e1-f858-436e-933e-59c642517860", "1.2.3.4", map[string]string{"ping": "pong"}),
		newMachineState("595989bb-cbb7-49ce-8726-722d6e157b4e", "5.6.7.8", map[string]string{"foo": "bar"}),
	}

	jobs := make([]job.Job, 0)
	appendJobsForTests(&jobs, machines[0], unitPrefix, unitCount, template)
	appendJobsForTests(&jobs, machines[1], unitPrefix, unitCount, template)

	states := make([]unit.UnitState, 0)
	if template {
		state := unit.UnitState{
			UnitName:    fmt.Sprintf("%[email protected]", unitPrefix),
			LoadState:   "loaded",
			ActiveState: "inactive",
			SubState:    "dead",
			MachineID:   machines[0].ID,
		}
		states = append(states, state)
		state.MachineID = machines[1].ID
		states = append(states, state)
	} else {
		for i := 1; i <= unitCount; i++ {
			state := unit.UnitState{
				UnitName:    fmt.Sprintf("%s%d.service", unitPrefix, i),
				LoadState:   "loaded",
				ActiveState: "active",
				SubState:    "listening",
				MachineID:   machines[0].ID,
			}
			states = append(states, state)
		}

		for i := 1; i <= unitCount; i++ {
			state := unit.UnitState{
				UnitName:    fmt.Sprintf("%s%d.service", unitPrefix, i),
				LoadState:   "loaded",
				ActiveState: "inactive",
				SubState:    "dead",
				MachineID:   machines[1].ID,
			}
			states = append(states, state)
		}
	}

	reg := registry.NewFakeRegistry()
	reg.SetMachines(machines)
	reg.SetUnitStates(states)
	reg.SetJobs(jobs)

	return &client.RegistryClient{Registry: reg}
}
Example #10
0
func newTestRegistryForListMachines() registry.Registry {
	m := []machine.MachineState{
		machine.MachineState{ID: "mnopqr"},
		machine.MachineState{ID: "abcdef"},
		machine.MachineState{ID: "ghijkl"},
	}

	reg := registry.NewFakeRegistry()
	reg.SetMachines(m)

	return reg
}
Example #11
0
func fakeMachinesSetup() (*machinesResource, *httptest.ResponseRecorder) {
	fr := registry.NewFakeRegistry()
	fr.SetMachines([]machine.MachineState{
		{ID: "XXX", PublicIP: "", Metadata: map[string]string{}},
		{ID: "YYY", PublicIP: "1.2.3.4", Metadata: map[string]string{"ping": "pong"}},
	})
	fAPI := &client.RegistryClient{Registry: fr}
	resource := &machinesResource{cAPI: fAPI, tokenLimit: testTokenLimit}
	rw := httptest.NewRecorder()

	return resource, rw
}
Example #12
0
func newFakeRegistryForListUnits(jobs []job.Job) registry.Registry {
	j := []job.Job{*job.NewJob("pong.service", *unit.NewUnit("Echo"))}

	if jobs != nil {
		for _, job := range jobs {
			j = append(j, job)
		}
	}

	reg := registry.NewFakeRegistry()
	reg.SetJobs(j)

	return reg
}
Example #13
0
func setupRegistryForStart(echoAttempts int) {
	m1 := machine.MachineState{
		ID:       "c31e44e1-f858-436e-933e-59c642517860",
		PublicIP: "1.2.3.4",
		Metadata: map[string]string{"ping": "pong"},
	}
	m2 := machine.MachineState{
		ID:       "595989bb-cbb7-49ce-8726-722d6e157b4e",
		PublicIP: "5.6.7.8",
		Metadata: map[string]string{"foo": "bar"},
	}
	m3 := machine.MachineState{
		ID:       "520983A8-FB9C-4A68-B49C-CED5BB2E9D08",
		Metadata: map[string]string{"foo": "bar"},
	}

	states := []unit.UnitState{
		unit.UnitState{
			UnitName:    "pong.service",
			LoadState:   "loaded",
			ActiveState: "active",
			SubState:    "listening",
			MachineID:   m1.ID,
		},
		unit.UnitState{
			UnitName:    "hello.service",
			LoadState:   "loaded",
			ActiveState: "inactive",
			SubState:    "dead",
			MachineID:   m2.ID,
		},
		unit.UnitState{
			UnitName:    "echo.service",
			LoadState:   "loaded",
			ActiveState: "inactive",
			SubState:    "dead",
			MachineID:   m2.ID,
		},
	}

	machines := []machine.MachineState{m1, m2, m3}

	reg := registry.NewFakeRegistry()
	reg.SetMachines(machines)
	reg.SetUnitStates(states)

	cAPI = &client.RegistryClient{Registry: &BlockedFakeRegistry{EchoAttempts: echoAttempts, FakeRegistry: *reg}}
}
Example #14
0
func TestUnitsListBadNextPageToken(t *testing.T) {
	fr := registry.NewFakeRegistry()
	resource := &unitsResource{fr, "/units"}
	rw := httptest.NewRecorder()
	req, err := http.NewRequest("GET", "http://example.com/units?nextPageToken=EwBMLg==", nil)
	if err != nil {
		t.Fatalf("Failed creating http.Request: %v", err)
	}

	resource.list(rw, req)

	err = assertErrorResponse(rw, http.StatusBadRequest)
	if err != nil {
		t.Error(err.Error())
	}
}
Example #15
0
func newFakeRegistryForSsh() client.API {
	// clear machineStates for every invocation
	machineStates = nil
	machines := []machine.MachineState{
		newMachineState("c31e44e1-f858-436e-933e-59c642517860", "1.2.3.4", map[string]string{"ping": "pong"}),
		newMachineState("595989bb-cbb7-49ce-8726-722d6e157b4e", "5.6.7.8", map[string]string{"foo": "bar"}),
		newMachineState("hello.service", "8.7.6.5", map[string]string{"foo": "bar"}),
	}

	jobs := []job.Job{
		job.Job{Name: "j1.service", Unit: unit.UnitFile{}, TargetMachineID: machines[0].ID},
		job.Job{Name: "j2.service", Unit: unit.UnitFile{}, TargetMachineID: machines[1].ID},
		job.Job{Name: "hello.service", Unit: unit.UnitFile{}, TargetMachineID: machines[2].ID},
	}

	states := []unit.UnitState{
		unit.UnitState{
			UnitName:    "j1.service",
			LoadState:   "loaded",
			ActiveState: "active",
			SubState:    "listening",
			MachineID:   machines[0].ID,
		},
		unit.UnitState{
			UnitName:    "j2.service",
			LoadState:   "loaded",
			ActiveState: "inactive",
			SubState:    "dead",
			MachineID:   machines[1].ID,
		},
		unit.UnitState{
			UnitName:    "hello.service",
			LoadState:   "loaded",
			ActiveState: "inactive",
			SubState:    "dead",
			MachineID:   machines[2].ID,
		},
	}

	reg := registry.NewFakeRegistry()
	reg.SetMachines(machines)
	reg.SetUnitStates(states)
	reg.SetJobs(jobs)

	return &client.RegistryClient{Registry: reg}
}
Example #16
0
func TestUnitsSubResourceNotFound(t *testing.T) {
	fr := registry.NewFakeRegistry()
	ur := &unitsResource{fr, "/units"}
	rr := httptest.NewRecorder()

	req, err := http.NewRequest("GET", "/units/foo/bar", nil)
	if err != nil {
		t.Fatalf("Failed setting up http.Request for test: %v", err)
	}

	ur.ServeHTTP(rr, req)

	err = assertErrorResponse(rr, http.StatusNotFound)
	if err != nil {
		t.Error(err)
	}
}
Example #17
0
func TestAgentLoadUnloadJob(t *testing.T) {
	uManager := unit.NewFakeUnitManager()
	usGenerator := unit.NewUnitStateGenerator(uManager)
	fReg := registry.NewFakeRegistry()
	mach := &machine.FakeMachine{machine.MachineState{ID: "XXX"}}
	a := New(uManager, usGenerator, fReg, mach, time.Second)

	j := newTestJobFromUnitContents(t, "foo.service", "")
	err := a.loadJob(j)
	if err != nil {
		t.Fatalf("Failed calling Agent.loadJob: %v", err)
	}

	jobs, err := a.jobs()
	if err != nil {
		t.Fatalf("Failed calling Agent.jobs: %v", err)
	}

	jsLoaded := job.JobStateLoaded
	expectJobs := map[string]*job.Job{
		"foo.service": &job.Job{
			Name:  "foo.service",
			State: &jsLoaded,

			Unit:            unit.UnitFile{},
			TargetState:     job.JobState(""),
			TargetMachineID: "",
		},
	}

	if !reflect.DeepEqual(expectJobs, jobs) {
		t.Fatalf("Received unexpected collection of Jobs: %#v\nExpected: %#v", jobs, expectJobs)
	}

	a.unloadJob("foo.service")

	jobs, err = a.jobs()
	if err != nil {
		t.Fatalf("Failed calling Agent.jobs: %v", err)
	}

	expectJobs = map[string]*job.Job{}
	if !reflect.DeepEqual(expectJobs, jobs) {
		t.Fatalf("Received unexpected collection of Jobs: %#v\nExpected: %#v", jobs, expectJobs)
	}
}
Example #18
0
func TestUnitStateListBadNextPageToken(t *testing.T) {
	fr := registry.NewFakeRegistry()
	fAPI := &client.RegistryClient{Registry: fr}
	resource := &stateResource{fAPI, "/state", testTokenLimit}
	rw := httptest.NewRecorder()
	req, err := http.NewRequest("GET", "http://example.com/state?nextPageToken=EwBMLg==", nil)
	if err != nil {
		t.Fatalf("Failed creating http.Request: %v", err)
	}

	resource.list(rw, req)

	err = assertErrorResponse(rw, http.StatusBadRequest)
	if err != nil {
		t.Error(err.Error())
	}
}
Example #19
0
func TestMachinesListBadNextPageToken(t *testing.T) {
	fr := registry.NewFakeRegistry()
	fAPI := &client.RegistryClient{Registry: fr}
	resource := &machinesResource{fAPI}
	rw := httptest.NewRecorder()
	req, err := http.NewRequest("GET", "http://example.com/machines?nextPageToken=EwBMLg==", nil)
	if err != nil {
		t.Fatalf("Failed creating http.Request: %v", err)
	}

	resource.ServeHTTP(rw, req)

	err = assertErrorResponse(rw, http.StatusBadRequest)
	if err != nil {
		t.Error(err.Error())
	}
}
Example #20
0
func TestPurge(t *testing.T) {
	cache := map[string]*unit.UnitState{
		"foo.service": &unit.UnitState{
			UnitName:    "foo.service",
			ActiveState: "loaded",
		},
		"bar.service": nil,
	}
	initStates := []unit.UnitState{
		unit.UnitState{
			UnitName:    "foo.service",
			ActiveState: "active",
		},
		unit.UnitState{
			UnitName:    "bar.service",
			ActiveState: "loaded",
		},
		unit.UnitState{
			UnitName:    "baz.service",
			ActiveState: "inactive",
		},
	}
	want := []*unit.UnitState{
		&unit.UnitState{
			UnitName:    "baz.service",
			ActiveState: "inactive",
		},
	}
	freg := registry.NewFakeRegistry()
	freg.SetUnitStates(initStates)
	usp := NewUnitStatePublisher(freg, &machine.FakeMachine{}, 0)
	usp.cache = cache

	usp.Purge()

	us, err := freg.UnitStates()
	if err != nil {
		t.Errorf("unexpected error retrieving unit states: %v", err)
	} else if !reflect.DeepEqual(us, want) {
		msg := fmt.Sprintln("received unexpected unit states")
		msg += fmt.Sprintf("got: %#v\n", us)
		msg += fmt.Sprintf("want: %#v\n", want)
		t.Error(msg)
	}
}
Example #21
0
func TestUnitsSetDesiredStateBadContentType(t *testing.T) {
	fr := registry.NewFakeRegistry()
	fAPI := &client.RegistryClient{Registry: fr}
	resource := &unitsResource{fAPI, "/units"}
	rr := httptest.NewRecorder()

	body := ioutil.NopCloser(bytes.NewBuffer([]byte(`{"foo":"bar"}`)))
	req, err := http.NewRequest("PUT", "http://example.com/units/foo.service", body)
	if err != nil {
		t.Fatalf("Failed creating http.Request: %v", err)
	}

	req.Header.Set("Content-Type", "application/xml")

	resource.set(rr, req, "foo.service")

	err = assertErrorResponse(rr, http.StatusUnsupportedMediaType)
	if err != nil {
		t.Fatal(err)
	}
}
Example #22
0
func TestUnitsList(t *testing.T) {
	fr := registry.NewFakeRegistry()
	fr.SetJobs([]job.Job{
		{Name: "XXX.service"},
		{Name: "YYY.service"},
	})
	fAPI := &client.RegistryClient{fr}
	resource := &unitsResource{fAPI, "/units"}
	rw := httptest.NewRecorder()
	req, err := http.NewRequest("GET", "http://example.com/units", nil)
	if err != nil {
		t.Fatalf("Failed creating http.Request: %v", err)
	}

	resource.list(rw, req)
	if rw.Code != http.StatusOK {
		t.Errorf("Expected 200, got %d", rw.Code)
	}

	ct := rw.HeaderMap["Content-Type"]
	if len(ct) != 1 {
		t.Errorf("Response has wrong number of Content-Type values: %v", ct)
	} else if ct[0] != "application/json" {
		t.Errorf("Expected application/json, got %s", ct)
	}

	if rw.Body == nil {
		t.Error("Received nil response body")
	} else {
		var page schema.UnitPage
		err := json.Unmarshal(rw.Body.Bytes(), &page)
		if err != nil {
			t.Fatalf("Received unparseable body: %v", err)
		}

		if len(page.Units) != 2 || page.Units[0].Name != "XXX.service" || page.Units[1].Name != "YYY.service" {
			t.Errorf("Received incorrect UnitPage entity: %v", page)
		}
	}
}
Example #23
0
func TestUnitGet(t *testing.T) {
	tests := []struct {
		item string
		code int
	}{
		{item: "XXX.service", code: http.StatusOK},
		{item: "ZZZ", code: http.StatusNotFound},
	}

	fr := registry.NewFakeRegistry()
	fr.SetJobs([]job.Job{
		{Name: "XXX.service"},
		{Name: "YYY.service"},
	})
	fAPI := &client.RegistryClient{fr}
	resource := &unitsResource{fAPI, "/units"}

	for i, tt := range tests {
		rw := httptest.NewRecorder()
		req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/units/%s", tt.item), nil)
		if err != nil {
			t.Errorf("case %d: failed creating http.Request: %v", i, err)
			continue
		}

		resource.get(rw, req, tt.item)

		if tt.code/100 == 2 {
			if tt.code != rw.Code {
				t.Errorf("case %d: expected %d, got %d", i, tt.code, rw.Code)
			}
		} else {
			err = assertErrorResponse(rw, tt.code)
			if err != nil {
				t.Errorf("case %d: %v", i, err)
			}
		}
	}
}
Example #24
0
func TestDefaultHandlers(t *testing.T) {
	tests := []struct {
		method string
		path   string
		code   int
	}{
		{"GET", "/", http.StatusMethodNotAllowed},
		{"GET", "/v1-alpha", http.StatusMethodNotAllowed},
		{"GET", "/fleet/v1", http.StatusMethodNotAllowed},
		{"GET", "/bogus", http.StatusNotFound},
	}

	for i, tt := range tests {
		fr := registry.NewFakeRegistry()
		hdlr := NewServeMux(fr, testTokenLimit)
		rr := httptest.NewRecorder()

		req, err := http.NewRequest(tt.method, tt.path, nil)
		if err != nil {
			t.Errorf("case %d: failed setting up http.Request for test: %v", i, err)
			continue
		}

		hdlr.ServeHTTP(rr, req)

		err = assertErrorResponse(rr, tt.code)
		if err != nil {
			t.Errorf("case %d: %v", i, err)
		}

		wantServer := fmt.Sprintf("fleetd/%s", version.Version)
		gotServer := rr.HeaderMap["Server"][0]
		if wantServer != gotServer {
			t.Errorf("case %d: received incorrect Server header: want=%s, got=%s", i, wantServer, gotServer)
		}
	}
}
Example #25
0
func TestUnitsSetDesiredState(t *testing.T) {
	tests := []struct {
		// initial state of Registry
		initJobs   []job.Job
		initStates map[string]job.JobState
		// item path (name) of the Unit
		item string
		// Unit to attempt to set
		arg schema.Unit
		// expected HTTP status code
		code int
		// expected state of registry after request
		finalStates map[string]job.JobState
	}{
		// Modify the desired State of an existing Job
		{
			initJobs:    []job.Job{job.Job{Name: "XXX.service", Unit: newUnit(t, "[Service]\nFoo=Bar")}},
			initStates:  map[string]job.JobState{"XXX.service": "inactive"},
			item:        "XXX.service",
			arg:         schema.Unit{Name: "XXX.service", DesiredState: "launched"},
			code:        http.StatusNoContent,
			finalStates: map[string]job.JobState{"XXX.service": "launched"},
		},
		// Create a new Unit
		{
			initJobs:   []job.Job{},
			initStates: map[string]job.JobState{},
			item:       "YYY.service",
			arg: schema.Unit{
				Name:         "YYY.service",
				DesiredState: "loaded",
				Options: []*schema.UnitOption{
					&schema.UnitOption{Section: "Service", Name: "Foo", Value: "Baz"},
				},
			},
			code:        http.StatusNoContent,
			finalStates: map[string]job.JobState{"YYY.service": "loaded"},
		},
		// Creating a new Unit without Options fails
		{
			initJobs:   []job.Job{},
			initStates: map[string]job.JobState{},
			item:       "YYY.service",
			arg: schema.Unit{
				Name:         "YYY.service",
				DesiredState: "loaded",
				Options:      []*schema.UnitOption{},
			},
			code:        http.StatusConflict,
			finalStates: map[string]job.JobState{},
		},
		// Referencing a Unit where the name is inconsistent with the path should fail
		{
			initJobs: []job.Job{
				job.Job{Name: "XXX.service", Unit: newUnit(t, "[Service]\nFoo=Bar")},
				job.Job{Name: "YYY.service", Unit: newUnit(t, "[Service]\nFoo=Baz")},
			},
			initStates: map[string]job.JobState{
				"XXX.service": "inactive",
				"YYY.service": "inactive",
			},
			item: "XXX.service",
			arg: schema.Unit{
				Name:         "YYY.service",
				DesiredState: "loaded",
			},
			code: http.StatusBadRequest,
			finalStates: map[string]job.JobState{
				"XXX.service": "inactive",
				"YYY.service": "inactive",
			},
		},
		// Referencing a Unit where the name is omitted should substitute the name from the path
		{
			initJobs: []job.Job{
				job.Job{Name: "XXX.service", Unit: newUnit(t, "[Service]\nFoo=Bar")},
				job.Job{Name: "YYY.service", Unit: newUnit(t, "[Service]\nFoo=Baz")},
			},
			initStates: map[string]job.JobState{
				"XXX.service": "inactive",
				"YYY.service": "inactive",
			},
			item: "XXX.service",
			arg: schema.Unit{
				DesiredState: "loaded",
			},
			code: http.StatusNoContent,
			finalStates: map[string]job.JobState{
				"XXX.service": "loaded",
				"YYY.service": "inactive",
			},
		},
	}

	for i, tt := range tests {
		fr := registry.NewFakeRegistry()
		fr.SetJobs(tt.initJobs)
		for j, s := range tt.initStates {
			err := fr.SetUnitTargetState(j, s)
			if err != nil {
				t.Errorf("case %d: failed initializing unit's target state: %v", i, err)
			}
		}

		req, err := http.NewRequest("PUT", fmt.Sprintf("http://example.com/units/%s", tt.item), nil)
		if err != nil {
			t.Errorf("case %d: failed creating http.Request: %v", i, err)
			continue
		}

		enc, err := json.Marshal(tt.arg)
		if err != nil {
			t.Errorf("case %d: unable to JSON-encode request: %v", i, err)
			continue
		}
		req.Body = ioutil.NopCloser(bytes.NewBuffer(enc))
		req.Header.Set("Content-Type", "application/json")

		fAPI := &client.RegistryClient{fr}
		resource := &unitsResource{fAPI, "/units"}
		rw := httptest.NewRecorder()
		resource.set(rw, req, tt.item)

		if tt.code/100 == 2 {
			if tt.code != rw.Code {
				t.Errorf("case %d: expected %d, got %d", i, tt.code, rw.Code)
			}
		} else {
			err = assertErrorResponse(rw, tt.code)
			if err != nil {
				t.Errorf("case %d: %v", i, err)
			}
		}

		for name, expect := range tt.finalStates {
			u, err := fr.Unit(name)
			if err != nil {
				t.Errorf("case %d: failed fetching Job: %v", i, err)
			} else if u == nil {
				t.Errorf("case %d: fetched nil Unit(%s), expected non-nil", i, name)
				continue
			}

			if u.TargetState != expect {
				t.Errorf("case %d: expect Unit(%s) target state %q, got %q", i, name, expect, u.TargetState)
			}
		}
	}
}
Example #26
0
func TestUnitsDestroy(t *testing.T) {
	tests := []struct {
		// initial state of registry
		init []job.Job
		// name of unit to delete
		arg string
		// expected HTTP status code
		code int
		// expected state of registry after deletion attempt
		remaining []string
	}{
		// Deletion of an existing unit should succeed
		{
			init:      []job.Job{job.Job{Name: "XXX.service", Unit: newUnit(t, "[Service]\nFoo=Bar")}},
			arg:       "XXX.service",
			code:      http.StatusNoContent,
			remaining: []string{},
		},
		// Deletion of a nonexistent unit should fail
		{
			init:      []job.Job{job.Job{Name: "XXX.service", Unit: newUnit(t, "[Service]\nFoo=Bar")}},
			arg:       "YYY.service",
			code:      http.StatusNotFound,
			remaining: []string{"XXX.service"},
		},
	}

	for i, tt := range tests {
		fr := registry.NewFakeRegistry()
		fr.SetJobs(tt.init)

		req, err := http.NewRequest("DELETE", fmt.Sprintf("http://example.com/units/%s", tt.arg), nil)
		if err != nil {
			t.Errorf("case %d: failed creating http.Request: %v", i, err)
			continue
		}

		fAPI := &client.RegistryClient{fr}
		resource := &unitsResource{fAPI, "/units"}
		rw := httptest.NewRecorder()
		resource.destroy(rw, req, tt.arg)

		if tt.code/100 == 2 {
			if tt.code != rw.Code {
				t.Errorf("case %d: expected %d, got %d", i, tt.code, rw.Code)
			}
		} else {
			err = assertErrorResponse(rw, tt.code)
			if err != nil {
				t.Errorf("case %d: %v", i, err)
			}
		}

		units, err := fr.Units()
		if err != nil {
			t.Errorf("case %d: failed fetching Units after destruction: %v", i, err)
			continue
		}

		remaining := make([]string, len(units))
		for i, u := range units {
			remaining[i] = u.Name
		}

		if !reflect.DeepEqual(tt.remaining, remaining) {
			t.Errorf("case %d: expected Units %v, got %v", i, tt.remaining, remaining)
		}
	}
}
Example #27
0
func TestUnitStateList(t *testing.T) {
	us1 := unit.UnitState{UnitName: "AAA", ActiveState: "active"}
	us2 := unit.UnitState{UnitName: "BBB", ActiveState: "inactive", MachineID: "XXX"}
	us3 := unit.UnitState{UnitName: "CCC", ActiveState: "active", MachineID: "XXX"}
	us4 := unit.UnitState{UnitName: "CCC", ActiveState: "inactive", MachineID: "YYY"}
	sus1 := &schema.UnitState{Name: "AAA", SystemdActiveState: "active"}
	sus2 := &schema.UnitState{Name: "BBB", SystemdActiveState: "inactive", MachineID: "XXX"}
	sus3 := &schema.UnitState{Name: "CCC", SystemdActiveState: "active", MachineID: "XXX"}
	sus4 := &schema.UnitState{Name: "CCC", SystemdActiveState: "inactive", MachineID: "YYY"}

	for i, tt := range []struct {
		url      string
		expected []*schema.UnitState
	}{
		{
			// Standard query - return all results
			"http://example.com/state",
			[]*schema.UnitState{sus1, sus2, sus3, sus4},
		},
		{
			// Query for specific unit name should be fine
			"http://example.com/state?unitName=AAA",
			[]*schema.UnitState{sus1},
		},
		{
			// Query for a different specific unit name should be fine
			"http://example.com/state?unitName=CCC",
			[]*schema.UnitState{sus3, sus4},
		},
		{
			// Query for nonexistent unit name should return nothing
			"http://example.com/state?unitName=nope",
			nil,
		},
		{
			// Query for a specific machine ID should be fine
			"http://example.com/state?machineID=XXX",
			[]*schema.UnitState{sus2, sus3},
		},
		{
			// Query for nonexistent machine ID should return nothing
			"http://example.com/state?machineID=nope",
			nil,
		},
		{
			// Query for specific unit name and specific machine ID should filter by both
			"http://example.com/state?unitName=CCC&machineID=XXX",
			[]*schema.UnitState{sus3},
		},
	} {
		fr := registry.NewFakeRegistry()
		fr.SetUnitStates([]unit.UnitState{us1, us2, us3, us4})
		fAPI := &client.RegistryClient{Registry: fr}
		resource := &stateResource{fAPI, "/state", testTokenLimit}
		rw := httptest.NewRecorder()
		req, err := http.NewRequest("GET", tt.url, nil)
		if err != nil {
			t.Fatalf("case %d: Failed creating http.Request: %v", i, err)
		}

		resource.list(rw, req)
		if rw.Code != http.StatusOK {
			t.Errorf("case %d: Expected 200, got %d", i, rw.Code)
		}
		ct := rw.HeaderMap["Content-Type"]
		if len(ct) != 1 {
			t.Errorf("case %d: Response has wrong number of Content-Type values: %v", i, ct)
		} else if ct[0] != "application/json" {
			t.Errorf("case %d: Expected application/json, got %s", i, ct)
		}

		if rw.Body == nil {
			t.Errorf("case %d: Received nil response body", i)
			continue
		}

		var page schema.UnitStatePage
		if err := json.Unmarshal(rw.Body.Bytes(), &page); err != nil {
			t.Fatalf("case %d: Received unparseable body: %v", i, err)
		}

		got := page.States
		if !reflect.DeepEqual(got, tt.expected) {
			t.Errorf("case %d: Unexpected UnitStates received.", i)
			t.Logf("Got UnitStates:")
			for _, us := range got {
				t.Logf("%#v", us)
			}
			t.Logf("Expected UnitStates:")
			for _, us := range tt.expected {
				t.Logf("%#v", us)
			}

		}
	}

	fr := registry.NewFakeRegistry()
	fr.SetUnitStates([]unit.UnitState{
		unit.UnitState{UnitName: "XXX", ActiveState: "active"},
		unit.UnitState{UnitName: "YYY", ActiveState: "inactive"},
	})
	fAPI := &client.RegistryClient{Registry: fr}
	resource := &stateResource{fAPI, "/state", testTokenLimit}
	rw := httptest.NewRecorder()
	req, err := http.NewRequest("GET", "http://example.com/state", nil)
	if err != nil {
		t.Fatalf("Failed creating http.Request: %v", err)
	}

	resource.list(rw, req)
	if rw.Code != http.StatusOK {
		t.Errorf("Expected 200, got %d", rw.Code)
	}

	ct := rw.HeaderMap["Content-Type"]
	if len(ct) != 1 {
		t.Errorf("Response has wrong number of Content-Type values: %v", ct)
	} else if ct[0] != "application/json" {
		t.Errorf("Expected application/json, got %s", ct)
	}

	if rw.Body == nil {
		t.Error("Received nil response body")
	} else {
		var page schema.UnitStatePage
		err := json.Unmarshal(rw.Body.Bytes(), &page)
		if err != nil {
			t.Fatalf("Received unparseable body: %v", err)
		}

		if len(page.States) != 2 {
			t.Errorf("Expected 2 UnitState objects, got %d", len(page.States))
			return
		}

		expect1 := &schema.UnitState{Name: "XXX", SystemdActiveState: "active"}
		if !reflect.DeepEqual(expect1, page.States[0]) {
			t.Errorf("expected first entity %#v, got %#v", expect1, page.States[0])
		}

		expect2 := &schema.UnitState{Name: "YYY", SystemdActiveState: "inactive"}
		if !reflect.DeepEqual(expect2, page.States[1]) {
			t.Errorf("expected first entity %#v, got %#v", expect2, page.States[1])
		}
	}
}
Example #28
0
func TestExtractUnitPage(t *testing.T) {
	fr := registry.NewFakeRegistry()

	all := make([]job.Job, 103)
	for i := 0; i < 103; i++ {
		name := strconv.FormatInt(int64(i), 10)
		all[i] = job.Job{Name: name}
	}

	tests := []struct {
		token    PageToken
		idxStart int
		idxEnd   int
		next     *PageToken
	}{
		{PageToken{Page: 1, Limit: 60}, 0, 59, &PageToken{Page: 2, Limit: 60}},
		{PageToken{Page: 2, Limit: 60}, 60, 102, nil},
	}

	for i, tt := range tests {
		page, err := extractUnitPage(fr, all, tt.token)
		if err != nil {
			t.Errorf("case %d: call to extractUnitPage failed: %v", i, err)
			continue
		}
		expectCount := (tt.idxEnd - tt.idxStart + 1)
		if len(page.Units) != expectCount {
			t.Errorf("case %d: expected page of %d, got %d", i, expectCount, len(page.Units))
			continue
		}

		first := page.Units[0].Name
		if first != strconv.FormatInt(int64(tt.idxStart), 10) {
			t.Errorf("case %d: irst element in first page should have ID %d, got %d", i, tt.idxStart, first)
		}

		last := page.Units[len(page.Units)-1].Name
		if last != strconv.FormatInt(int64(tt.idxEnd), 10) {
			t.Errorf("case %d: first element in first page should have ID %d, got %d", i, tt.idxEnd, last)
		}

		if tt.next == nil && page.NextPageToken != "" {
			t.Errorf("case %d: did not expect NextPageToken", i)
			continue
		} else if page.NextPageToken == "" {
			if tt.next != nil {
				t.Errorf("case %d: did not receive expected NextPageToken", i)
			}
			continue
		}

		next, err := decodePageToken(page.NextPageToken)
		if err != nil {
			t.Errorf("case %d: unable to parse NextPageToken: %v", i, err)
			continue
		}

		if !reflect.DeepEqual(next, tt.next) {
			t.Errorf("case %d: expected PageToken %v, got %v", i, tt.next, next)
		}
	}
}
Example #29
0
func TestUnitsDestroy(t *testing.T) {
	tests := []struct {
		// initial state of registry
		init []job.Job
		// which Job to attempt to delete
		arg schema.DeletableUnit
		// expected HTTP status code
		code int
		// expected state of registry after deletion attempt
		remaining []string
	}{
		// Unsafe deletion of an existing unit should succeed
		{
			init:      []job.Job{job.Job{Name: "XXX", Unit: unit.Unit{Raw: "FOO"}}},
			arg:       schema.DeletableUnit{Name: "XXX"},
			code:      http.StatusNoContent,
			remaining: []string{},
		},
		// Safe deletion of an existing unit should succeed
		{
			init:      []job.Job{job.Job{Name: "XXX", Unit: unit.Unit{Raw: "FOO"}}},
			arg:       schema.DeletableUnit{Name: "XXX", FileContents: "Rk9P"},
			code:      http.StatusNoContent,
			remaining: []string{},
		},
		// Unsafe deletion of a nonexistent unit should fail
		{
			init:      []job.Job{job.Job{Name: "XXX", Unit: unit.Unit{Raw: "FOO"}}},
			arg:       schema.DeletableUnit{Name: "YYY"},
			code:      http.StatusNotFound,
			remaining: []string{"XXX"},
		},
		// Safe deletion of a nonexistent unit should fail
		{
			init:      []job.Job{},
			arg:       schema.DeletableUnit{Name: "XXX", FileContents: "Rk9P"},
			code:      http.StatusNotFound,
			remaining: []string{},
		},
		// Safe deletion of a unit with the wrong contents should fail
		{
			init:      []job.Job{job.Job{Name: "XXX", Unit: unit.Unit{Raw: "FOO"}}},
			arg:       schema.DeletableUnit{Name: "XXX", FileContents: "QkFS"},
			code:      http.StatusConflict,
			remaining: []string{"XXX"},
		},
		// Safe deletion of a unit with the malformed contents should fail
		{
			init:      []job.Job{job.Job{Name: "XXX", Unit: unit.Unit{Raw: "FOO"}}},
			arg:       schema.DeletableUnit{Name: "XXX", FileContents: "*"},
			code:      http.StatusBadRequest,
			remaining: []string{"XXX"},
		},
	}

	for i, tt := range tests {
		fr := registry.NewFakeRegistry()
		fr.SetJobs(tt.init)

		req, err := http.NewRequest("DELETE", fmt.Sprintf("http://example.com/units/%s", tt.arg.Name), nil)
		if err != nil {
			t.Errorf("case %d: failed creating http.Request: %v", i, err)
			continue
		}

		enc, err := json.Marshal(tt.arg)
		if err != nil {
			t.Errorf("case %d: unable to JSON-encode request: %v", i, err)
			continue
		}
		req.Body = ioutil.NopCloser(bytes.NewBuffer(enc))
		req.Header.Set("Content-Type", "application/json")

		resource := &unitsResource{fr, "/units"}
		rw := httptest.NewRecorder()
		resource.destroy(rw, req, tt.arg.Name)

		if tt.code/100 == 2 {
			if tt.code != rw.Code {
				t.Errorf("case %d: expected %d, got %d", i, tt.code, rw.Code)
			}
		} else {
			err = assertErrorResponse(rw, tt.code)
			if err != nil {
				t.Errorf("case %d: %v", i, err)
			}
		}

		jobs, err := fr.Jobs()
		if err != nil {
			t.Errorf("case %d: failed fetching Jobs after destruction: %v", i, err)
			continue
		}

		remaining := make([]string, len(jobs))
		for i, j := range jobs {
			remaining[i] = j.Name
		}

		if !reflect.DeepEqual(tt.remaining, remaining) {
			t.Errorf("case %d: expected Jobs %v, got %v", i, tt.remaining, remaining)
		}
	}
}
Example #30
0
func TestUnitsSetDesiredState(t *testing.T) {
	tests := []struct {
		// initial state of Registry
		initJobs   []job.Job
		initStates map[string]job.JobState
		// which Job to attempt to delete
		arg schema.DesiredUnitState
		// expected HTTP status code
		code int
		// expected state of registry after request
		finalStates map[string]job.JobState
	}{
		// Modify the DesiredState of an existing Job
		{
			initJobs:    []job.Job{job.Job{Name: "XXX", Unit: unit.Unit{Raw: "FOO"}}},
			initStates:  map[string]job.JobState{"XXX": "inactive"},
			arg:         schema.DesiredUnitState{Name: "XXX", DesiredState: "launched"},
			code:        http.StatusNoContent,
			finalStates: map[string]job.JobState{"XXX": "launched"},
		},
		// Create a new Job
		{
			initJobs:    []job.Job{},
			initStates:  map[string]job.JobState{},
			arg:         schema.DesiredUnitState{Name: "YYY", DesiredState: "loaded", FileContents: "cGVubnkNCg=="},
			code:        http.StatusNoContent,
			finalStates: map[string]job.JobState{"YYY": "loaded"},
		},
		{
			initJobs:    []job.Job{},
			initStates:  map[string]job.JobState{},
			arg:         schema.DesiredUnitState{Name: "YYY", DesiredState: "loaded", FileContents: "*"},
			code:        http.StatusBadRequest,
			finalStates: map[string]job.JobState{},
		},
		// Modifying a Job with garbage fileContents should fail
		{
			initJobs:    []job.Job{job.Job{Name: "XXX", Unit: unit.Unit{Raw: "FOO"}}},
			initStates:  map[string]job.JobState{"XXX": job.JobStateInactive},
			arg:         schema.DesiredUnitState{Name: "YYY", DesiredState: "loaded", FileContents: "*"},
			code:        http.StatusBadRequest,
			finalStates: map[string]job.JobState{"XXX": job.JobStateInactive},
		},
		// Modifying a nonexistent Job should fail
		{
			initJobs:    []job.Job{},
			initStates:  map[string]job.JobState{},
			arg:         schema.DesiredUnitState{Name: "YYY", DesiredState: "loaded"},
			code:        http.StatusConflict,
			finalStates: map[string]job.JobState{},
		},
		// Modifying a Job with the incorrect fileContents should fail
		{
			initJobs:    []job.Job{job.Job{Name: "XXX", Unit: unit.Unit{Raw: "FOO"}}},
			initStates:  map[string]job.JobState{"XXX": "inactive"},
			arg:         schema.DesiredUnitState{Name: "XXX", DesiredState: "loaded", FileContents: "ZWxyb3kNCg=="},
			code:        http.StatusConflict,
			finalStates: map[string]job.JobState{"XXX": "inactive"},
		},
	}

	for i, tt := range tests {
		fr := registry.NewFakeRegistry()
		fr.SetJobs(tt.initJobs)
		for j, s := range tt.initStates {
			err := fr.SetJobTargetState(j, s)
			if err != nil {
				t.Errorf("case %d: failed initializing Job target state: %v", i, err)
			}
		}

		req, err := http.NewRequest("PUT", fmt.Sprintf("http://example.com/units/%s", tt.arg.Name), nil)
		if err != nil {
			t.Errorf("case %d: failed creating http.Request: %v", i, err)
			continue
		}

		enc, err := json.Marshal(tt.arg)
		if err != nil {
			t.Errorf("case %d: unable to JSON-encode request: %v", i, err)
			continue
		}
		req.Body = ioutil.NopCloser(bytes.NewBuffer(enc))
		req.Header.Set("Content-Type", "application/json")

		resource := &unitsResource{fr, "/units"}
		rw := httptest.NewRecorder()
		resource.set(rw, req, tt.arg.Name)

		if tt.code/100 == 2 {
			if tt.code != rw.Code {
				t.Errorf("case %d: expected %d, got %d", i, tt.code, rw.Code)
			}
		} else {
			err = assertErrorResponse(rw, tt.code)
			if err != nil {
				t.Errorf("case %d: %v", i, err)
			}
		}

		for name, expect := range tt.finalStates {
			j, err := fr.Job(name)
			if err != nil {
				t.Errorf("case %d: failed fetching Job: %v", i, err)
			} else if j == nil {
				t.Errorf("case %d: fetched nil Job(%s), expected non-nil", i, name)
			}

			if j.TargetState != expect {
				t.Errorf("case %d: expect Job(%s) target state %q, got %q", i, name, expect, j.TargetState)
			}
		}
	}
}