Пример #1
0
// Assert that jobs and their peers are properly indexed
func TestGetJobsByPeer(t *testing.T) {
	state := NewState()

	u1 := unit.NewUnit(`[X-Fleet]
X-ConditionMachineOf=b
X-ConditionMachineOf=c
`)
	j1 := job.NewJob("a", *u1)
	state.TrackJob(j1)

	u2 := unit.NewUnit(`[X-Fleet]
X-ConditionMachineOf=c
`)
	j2 := job.NewJob("d", *u2)
	state.TrackJob(j2)

	peers := state.GetJobsByPeer("b")
	if len(peers) != 1 || peers[0] != "a" {
		t.Fatalf("Unexpected index of job peers %v", peers)
	}

	peers = state.GetJobsByPeer("c")
	if len(peers) != 2 || peers[0] != "a" || peers[1] != "d" {
		t.Fatalf("Unexpected index of job peers %v", peers)
	}
}
Пример #2
0
func TestSignJob(t *testing.T) {
	c, _ := initSign(t)

	u := unit.NewUnit("Echo")
	j := job.NewJob("echo.service", *u)

	data, err := marshal(u)
	if err != nil {
		t.Fatal("marshal error:", err)
	}

	expectedSig, err := c.keyring.Sign(testPublicKeys["rsa"], data)
	if err != nil {
		t.Fatal("sign error:", err)
	}

	s, err := c.SignJob(j)
	if err != nil {
		t.Fatal("sign payload error:", err)
	}
	if s.Tag != TagForJob("echo.service") {
		t.Fatal("sign tag error:", err)
	}

	if len(s.Signatures) != 1 {
		t.Fatal("expect 1 signature instead of", len(s.Signatures))
	}
	if bytes.Compare(s.Signatures[0].Blob, expectedSig.Blob) != 0 {
		t.Fatal("wrong signature")
	}
}
Пример #3
0
func mapUnitToJob(entity *schema.Unit, mm map[string]*machine.MachineState) (*job.Job, error) {
	contents, err := base64.StdEncoding.DecodeString(entity.FileContents)
	if err != nil {
		return nil, err
	}
	u, err := unit.NewUnit(string(contents))
	if err != nil {
		return nil, err
	}

	js := job.JobState(entity.CurrentState)
	j := job.Job{
		Name:  entity.Name,
		State: &js,
		Unit:  *u,
	}

	// populate a UnitState object only if the entity
	// is actually reporting relevant data
	if entity.Systemd != nil {
		j.UnitState = &unit.UnitState{
			LoadState:   entity.Systemd.LoadState,
			ActiveState: entity.Systemd.ActiveState,
			SubState:    entity.Systemd.SubState,
		}
		if len(entity.Systemd.MachineID) > 0 {
			j.UnitState.MachineID = entity.Systemd.MachineID
		}
	}

	return &j, nil
}
Пример #4
0
// getUnitByHash retrieves from the Registry the Unit associated with the given Hash
func (r *EtcdRegistry) getUnitByHash(hash unit.Hash) *unit.Unit {
	req := etcd.Get{
		Key:       r.hashedUnitPath(hash),
		Recursive: true,
	}
	resp, err := r.etcd.Do(&req)
	if err != nil {
		if isKeyNotFound(err) {
			err = nil
		}
		return nil
	}
	var um unitModel
	if err := unmarshal(resp.Node.Value, &um); err != nil {
		log.Errorf("error unmarshaling Unit(%s): %v", hash, err)
		return nil
	}

	u, err := unit.NewUnit(um.Raw)
	if err != nil {
		log.Errorf("error parsing Unit(%s): %v", hash, err)
		return nil
	}

	return u
}
Пример #5
0
func TestMarshaling(t *testing.T) {
	units := []string{
		``,

		`[Service]
		ExecStart=/bin/sleep 1`,

		`[Unit]
		Description=Foo

		[Service]
		ExecStart=echo "foo"`,

		`[Path]
		PathExists=/foo`,
	}

	for _, contents := range units {
		u := unit.NewUnit(contents)
		json, err := marshal(u)
		if err != nil {
			t.Error("Error marshaling unit:", err)
		}
		var um unit.Unit
		err = unmarshal(json, &um)
		if err != nil {
			t.Error("Error unmarshaling unit:", err)
		}
		if !reflect.DeepEqual(*u, um) {
			t.Errorf("Unmarshaled unit does not match original!\nOriginal:\n%s\nUnmarshaled:\n%s", *u, um)
		}
	}

}
Пример #6
0
func newUnit(t *testing.T, str string) *unit.Unit {
	u, err := unit.NewUnit(str)
	if err != nil {
		t.Fatalf("Unexpected error creating unit from %q: %v", str, err)
	}
	return u
}
Пример #7
0
func decodeUnitContents(c string) (*unit.Unit, error) {
	dec, err := base64.StdEncoding.DecodeString(c)
	if err != nil {
		return nil, err
	}

	return unit.NewUnit(string(dec))
}
Пример #8
0
func TestFakeRegistryJobLifecycle(t *testing.T) {
	reg := NewFakeRegistry()

	jobs, err := reg.Jobs()
	if err != nil {
		t.Fatalf("Received error while calling Jobs: %v", err)
	}
	if !reflect.DeepEqual([]job.Job{}, jobs) {
		t.Fatalf("Expected no jobs, got %v", jobs)
	}

	u, _ := unit.NewUnit("")
	j1 := job.NewJob("job1.service", *u)
	err = reg.CreateJob(j1)
	if err != nil {
		t.Fatalf("Received error while calling CreateJob: %v", err)
	}

	jobs, err = reg.Jobs()
	if err != nil {
		t.Fatalf("Received error while calling Jobs: %v", err)
	}
	if len(jobs) != 1 {
		t.Fatalf("Expected 1 Job, got %v", jobs)
	}
	if jobs[0].Name != "job1.service" {
		t.Fatalf("Expected Job with name \"job1.service\", got %q", jobs[0].Name)
	}

	err = reg.ScheduleJob("job1.service", "XXX")
	if err != nil {
		t.Fatalf("Received error while calling ScheduleJob: %v", err)
	}

	j, err := reg.Job("job1.service")
	if err != nil {
		t.Fatalf("Received error while calling JobTarget: %v", err)
	}
	if j.TargetMachineID != "XXX" {
		t.Fatalf("Job should be scheduled to XXX, got %v", j.TargetMachineID)
	}

	err = reg.DestroyJob("job1.service")
	if err != nil {
		t.Fatalf("Received error while calling DestroyJob: %v", err)
	}

	jobs, err = reg.Jobs()
	if err != nil {
		t.Fatalf("Received error while calling Jobs: %v", err)
	}
	if !reflect.DeepEqual([]job.Job{}, jobs) {
		t.Fatalf("Expected no jobs, got %v", jobs)
	}
}
Пример #9
0
func newNamedTestJobFromUnitContents(t *testing.T, name, contents string) *job.Job {
	u, err := unit.NewUnit(contents)
	if err != nil {
		t.Fatalf("error creating Unit from %q: %v", contents, err)
	}
	j := job.NewJob(name, *u)
	if j == nil {
		t.Fatalf("error creating Job %q from %q", name, u)
	}
	return j
}
Пример #10
0
// getUnitFromFile attempts to load a Job from a given filename
// It returns the Job or nil, and any error encountered
func getUnitFromFile(file string) (*unit.Unit, error) {
	out, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	unitName := path.Base(file)
	log.V(1).Infof("Unit(%s) found in local filesystem", unitName)

	return unit.NewUnit(string(out)), nil
}
Пример #11
0
func TestAbleToRunConditionMachineIDMatch(t *testing.T) {
	u := unit.NewUnit(`[X-Fleet]
X-ConditionMachineID=XYZ
`)
	job := job.NewJob("example.service", *u)

	mach := &machine.FakeMachine{machine.MachineState{ID: "XYZ"}}
	agent := Agent{Machine: mach, state: NewState()}
	if !agent.ableToRun(job) {
		t.Fatalf("Agent should be able to run job")
	}
}
Пример #12
0
func TestAbleToRunConditionMachineIDMismatch(t *testing.T) {
	u := unit.NewUnit(`[X-Fleet]
X-ConditionMachineID=XYZ
`)
	job := job.NewJob("example.service", *u)

	mach := machine.New(machine.MachineState{ID: "123"})
	agent := Agent{machine: mach, state: NewState()}
	if agent.AbleToRun(job) {
		t.Fatalf("Agent should not be able to run job")
	}
}
Пример #13
0
func fleetUnit(t *testing.T, opts ...string) unit.Unit {
	contents := "[X-Fleet]"
	for _, v := range opts {
		contents = fmt.Sprintf("%s\n%s", contents, v)
	}

	u, err := unit.NewUnit(contents)
	if u == nil || err != nil {
		t.Fatalf("Failed creating test unit: unit=%v, err=%v", u, err)
	}

	return *u
}
Пример #14
0
// Assert that no jobs are returned for unknown peers
func TestGetJobsByPeerUnknown(t *testing.T) {
	u := unit.NewUnit(`[X-Fleet]
X-ConditionMachineOf=b
`)
	j := job.NewJob("a", *u)

	state := NewState()
	state.TrackJob(j)

	peers := state.GetJobsByPeer("c")
	if len(peers) != 0 {
		t.Fatalf("Unexpected index of job peers %v", peers)
	}
}
Пример #15
0
// Assert that existing jobs and potential jobs that do not conflict do not
// trigger a match
func TestHasConflictNoMatch(t *testing.T) {
	state := NewState()

	u := unit.NewUnit(`[X-Fleet]`)
	j := job.NewJob("example.service", *u)
	state.TrackJob(j)
	state.SetTargetState(j.Name, job.JobStateLoaded)

	agent := Agent{state: state}

	matched, name := agent.HasConflict("other.service", []string{})
	if matched {
		t.Errorf("Expected no match, but got conflict with %s", name)
	}
}
Пример #16
0
func TestLegacyPayload(t *testing.T) {
	unitContents := `
[Service]
ExecStart=/bin/sleep 30000
`[1:]
	legacyPayloadContents := `{"Name":"sleep.service","Unit":{"Contents":{"Service":{"ExecStart":"/bin/sleep 30000"}},"Raw":"[Service]\nExecStart=/bin/sleep 30000\n"}}`
	want := unit.NewUnit(unitContents)
	var ljp LegacyJobPayload
	err := unmarshal(legacyPayloadContents, &ljp)
	if err != nil {
		t.Error("Error unmarshaling legacy payload:", err)
	}
	got := ljp.Unit
	if !reflect.DeepEqual(*want, got) {
		t.Errorf("Unit from legacy payload does not match expected!\nwant:\n%s\ngot:\n%s", *want, got)
	}
}
Пример #17
0
// Assert that a potential conflict is triggered against the existing job name
func TestHasConflictPotentialMatch(t *testing.T) {
	state := NewState()

	u := unit.NewUnit(`[X-Fleet]`)
	j := job.NewJob("example.service", *u)
	state.TrackJob(j)
	state.SetTargetState(j.Name, job.JobStateLoaded)

	agent := Agent{state: state}

	matched, name := agent.HasConflict("other.service", []string{"example.service"})
	if !matched {
		t.Errorf("Expected conflict with 'example.service', no conflict reported")
	} else if name != "example.service" {
		t.Errorf("Expected conflict with 'example.service', but conflict found with %s", name)
	}
}
Пример #18
0
func TestHasConflictIgnoresBids(t *testing.T) {
	state := NewState()

	u := unit.NewUnit(`[X-Fleet]
X-Conflicts=other.service
`)
	j := job.NewJob("example.service", *u)
	state.TrackJob(j)
	state.TrackBid(j.Name)

	agent := Agent{state: state}

	matched, name := agent.HasConflict("other.service", []string{})
	if matched {
		t.Errorf("Expected no conflict, but got conflict with %s", name)
	}
}
Пример #19
0
func TestVerifyJob(t *testing.T) {
	c, v := initSign(t)

	u, err := unit.NewUnit("Echo")
	if err != nil {
		t.Fatalf("unexpected error creating new unit: %v", err)
	}
	j := job.NewJob("echo.service", *u)

	data, err := marshal(u)
	if err != nil {
		t.Fatal("marshal error:", err)
	}

	v.pubkeys = append(v.pubkeys, testPublicKeys["rsa"])
	signature, err := c.keyring.Sign(testPublicKeys["rsa"], data)
	if err != nil {
		t.Fatal("sign error:", err)
	}

	ss := &SignatureSet{TagForJob("echo.service"), []*gossh.Signature{signature}}

	ok, err := v.VerifyJob(j, ss)
	if err != nil {
		t.Fatal("error verifying job:", err)
	}
	if !ok {
		t.Fatal("job verification failed")
	}

	ss.Tag = ""
	ok, err = v.VerifyJob(j, ss)
	if err == nil || ok == true {
		t.Fatal("should fail on job verification")
	}

	ok, err = v.VerifyJob(j, nil)
	if err == nil || ok == true {
		t.Fatal("should fail on job verification")
	}
}
Пример #20
0
func (ljp *LegacyJobPayload) UnmarshalJSON(data []byte) error {
	var ljpm legacyJobPayloadModel
	err := json.Unmarshal(data, &ljpm)
	if err != nil {
		return fmt.Errorf("unable to JSON-deserialize object: %s", err)
	}

	var u *unit.Unit
	if len(ljpm.Unit.Raw) > 0 {
		u, err = unit.NewUnit(ljpm.Unit.Raw)
	} else {
		u, err = unit.NewUnitFromLegacyContents(ljpm.Unit.Contents)
	}
	if err != nil {
		return err
	}

	ljp.Unit = *u
	ljp.Name = ljpm.Name

	return nil
}
Пример #21
0
func TestAgentLoadStartStopJob(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)
	}

	u, err := unit.NewUnit("")
	if err != nil {
		t.Fatalf("Failed creating Unit: %v", err)
	}

	j := job.NewJob("foo.service", *u)

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

	a.startJob("foo.service")

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

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

			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.stopJob("foo.service")

	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)
	}
}
Пример #22
0
func TestSystemdUnitFlow(t *testing.T) {
	uDir, err := ioutil.TempDir("", "fleet-")
	if err != nil {
		t.Fatalf("Failed creating tempdir: %v", err)
	}
	defer os.RemoveAll(uDir)

	mgr, err := systemd.NewSystemdUnitManager(uDir)
	if err != nil {
		t.Fatalf("Failed initializing SystemdUnitManager: %v", err)
	}

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

	if len(units) > 0 {
		t.Fatalf("Expected no units to be returned, got %v", units)
	}

	contents := `[Service]
ExecStart=/usr/bin/sleep 3000
`
	name := fmt.Sprintf("fleet-unit-%d.service", rand.Int63())
	uf, err := unit.NewUnit(contents)
	if err != nil {
		t.Fatalf("Invalid unit file: %v", err)
	}
	hash := uf.Hash().String()
	j := job.NewJob(name, *uf)

	if err := mgr.Load(j.Name, j.Unit); err != nil {
		t.Fatalf("Failed loading job: %v", err)
	}

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

	if !reflect.DeepEqual([]string{name}, units) {
		t.Fatalf("Expected [hello.service], got %v", units)
	}

	us, err := mgr.GetUnitState(name)
	if err == nil {
		expect := unit.UnitState{"loaded", "inactive", "dead", "", hash}
		if !reflect.DeepEqual(expect, *us) {
			t.Errorf("Expected UnitState %v, got %v", expect, *us)
		}
	} else {
		t.Errorf("Failed determining unit state: %v", err)
	}

	mgr.Start(name)

	us, err = mgr.GetUnitState(name)
	if err == nil {
		expect := unit.UnitState{"loaded", "active", "running", "", hash}
		if !reflect.DeepEqual(expect, *us) {
			t.Errorf("Expected UnitState %v, got %v", expect, *us)
		}
	} else {
		t.Errorf("Failed determining unit state: %v", err)
	}

	mgr.Stop(name)

	us, err = mgr.GetUnitState(name)
	if err == nil {
		expect := unit.UnitState{"loaded", "inactive", "dead", "", hash}
		if !reflect.DeepEqual(expect, *us) {
			t.Errorf("Expected UnitState %v, got %v", expect, *us)
		}
	} else {
		t.Errorf("Failed determining unit state: %v", err)
	}

	mgr.Unload(name)

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

	if len(units) > 0 {
		t.Fatalf("Expected no units to be returned, got %v", units)
	}
}