// Determines if either there is no currently built version of the agent, // or if the currently built version is out of date. // Returns whether a new version needs to be built, and an error if any // of the checks within the function throw an error. func (self *AgentBasedHostGateway) AgentNeedsBuild() (bool, error) { // compute and cache the current hash of the agent package agentHash, err := util.CurrentGitHash(self.AgentPackageDir) if err != nil { return false, fmt.Errorf("error getting git hash of agent package "+ "at %v: %v", self.AgentPackageDir, err) } self.currentAgentHash = agentHash // if the local directory where the executables live does not exist, then we // certainly need to build the agent exists, err := util.FileExists(self.ExecutablesDir) if err != nil { return false, err } if !exists { return true, nil } // if the most recently built agent hash is different from the current // mci hash, then the agent needs to be rebuilt lastBuiltRevision, err := db.GetLastAgentBuild() if err != nil { return false, fmt.Errorf("error getting last agent build hash: %v", err) } return self.currentAgentHash != lastBuiltRevision, nil }
func TestAgentBasedHostGateway(t *testing.T) { var hostGateway *AgentBasedHostGateway SkipConvey("When checking if the agent needs to be built", t, func() { hostGateway = &AgentBasedHostGateway{} Convey("a non-existent executables directory should cause the agent"+ " to need to be built", func() { hostGateway.ExecutablesDir = "/foo/bar/baz/foo/bar" needsBuild, err := hostGateway.AgentNeedsBuild() So(err, ShouldBeNil) So(needsBuild, ShouldBeTrue) }) Convey("an out-of-date last-built version of the agent should cause"+ " the agent to need to be built", func() { hostGateway.ExecutablesDir = "/" // just needs to exist // store the "last built hash" as something different than the // current one So(db.StoreLastAgentBuild("fedcba"), ShouldBeNil) needsBuild, err := hostGateway.AgentNeedsBuild() So(err, ShouldBeNil) So(needsBuild, ShouldBeTrue) }) Convey("an up-to-date last-built version of the agent should cause"+ " the agent to not need to be built", func() { hostGateway.ExecutablesDir = "/" // just needs to exist // compute and cache the current hash of the agent package agentHash, err := util.CurrentGitHash(hostGateway.AgentPackageDir) So(err, ShouldBeNil) // store the "last built hash" as the same as the current one So(db.StoreLastAgentBuild(agentHash), ShouldBeNil) needsBuild, err := hostGateway.AgentNeedsBuild() So(err, ShouldBeNil) So(needsBuild, ShouldBeFalse) }) }) SkipConvey("When building the agent", t, func() { hostGateway = &AgentBasedHostGateway{} So(db.StoreLastAgentBuild("abcdef"), ShouldBeNil) Convey("a nil AgentCompiler should cause a panic", func() { So(func() { hostGateway.buildAgent() }, ShouldPanic) }) Convey("a failed compilation should error and cause the build to not"+ " be recorded as successful", func() { // attempt to compile hostGateway.currentAgentHash = "fedcba" hostGateway.Compiler = &FailingAgentCompiler{} So(hostGateway.buildAgent(), ShouldNotBeNil) // make sure the last built hash was not updated lastBuiltHash, err := db.GetLastAgentBuild() So(err, ShouldBeNil) So(lastBuiltHash, ShouldEqual, "abcdef") }) Convey("a successful compilation should cause the build to be"+ " recorded as successful", func() { // attempt to compile hostGateway.currentAgentHash = "fedcba" hostGateway.Compiler = &SucceedingAgentCompiler{} So(hostGateway.buildAgent(), ShouldBeNil) // make sure the last built hash was updated lastBuiltHash, err := db.GetLastAgentBuild() So(err, ShouldBeNil) So(lastBuiltHash, ShouldEqual, "fedcba") }) }) Convey("When prepping the remote machine", t, func() { Convey("the remote shell should be created, and the config directory"+ " and agent binaries should be copied over to it", func() { hostGateway = &AgentBasedHostGateway{ Compiler: &SucceedingAgentCompiler{}, } // create a mock config directory, mock executables // directory, and mock remote shell evgHome := evergreen.FindEvergreenHome() tmpBase := filepath.Join(evgHome, "taskrunner/testdata/tmp") mockConfigDir := filepath.Join(tmpBase, "mock_config_dir") hostGatewayTestConf.ConfigDir = mockConfigDir mockExecutablesDir := filepath.Join(tmpBase, "mock_executables_dir") hostGateway.ExecutablesDir = mockExecutablesDir mockExecutable := filepath.Join(mockExecutablesDir, "main") mockRemoteShell := filepath.Join(tmpBase, "mock_remote_shell") evergreen.RemoteShell = mockRemoteShell // prevent permissions issues syscall.Umask(0000) // remove the directories, if they exist (start clean) exists, err := util.FileExists(tmpBase) So(err, ShouldBeNil) if exists { So(os.RemoveAll(tmpBase), ShouldBeNil) } So(os.MkdirAll(tmpBase, 0777), ShouldBeNil) // create the config and executables directories, as well as a // mock executable, to copy over So(os.Mkdir(mockConfigDir, 0777), ShouldBeNil) So(os.Mkdir(mockExecutablesDir, 0777), ShouldBeNil) So(ioutil.WriteFile(mockExecutable, []byte("run me"), 0777), ShouldBeNil) // mock up a host for localhost localhost := host.Host{ Host: command.TestRemote, User: command.TestRemoteUser, } // prep the "remote" host _, err = hostGateway.prepRemoteHost(hostGatewayTestConf, localhost, []string{"-i", command.TestRemoteKey}, "") So(err, ShouldBeNil) // make sure the correct files were created and copied over exists, err = util.FileExists(filepath.Join(mockRemoteShell, "mock_config_dir")) So(err, ShouldBeNil) So(exists, ShouldBeTrue) exists, err = util.FileExists(filepath.Join(mockRemoteShell, "main")) So(err, ShouldBeNil) So(exists, ShouldBeTrue) }) }) }