コード例 #1
0
ファイル: run_errand_test.go プロジェクト: jianqiu/bosh-agent
					_, err := action.Run()
					Expect(err).To(HaveOccurred())
					Expect(err.Error()).To(Equal("At least one job template is required to run an errand"))
				})

				It("does not run errand script", func() {
					_, err := action.Run()
					Expect(err).To(HaveOccurred())
					Expect(len(cmdRunner.RunComplexCommands)).To(Equal(0))
				})
			})
		})

		Context("when apply spec could not be retrieved", func() {
			BeforeEach(func() {
				specService.GetErr = errors.New("fake-get-error")
			})

			It("returns error stating that job template is required", func() {
				_, err := action.Run()
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("fake-get-error"))
			})

			It("does not run errand script", func() {
				_, err := action.Run()
				Expect(err).To(HaveOccurred())
				Expect(len(cmdRunner.RunComplexCommands)).To(Equal(0))
			})
		})
	})
コード例 #2
0
ファイル: agent_test.go プロジェクト: nimbus-cloud/bosh-agent
func init() {
	Describe("Agent", func() {
		var (
			logger           boshlog.Logger
			handler          *fakembus.FakeHandler
			platform         *fakeplatform.FakePlatform
			actionDispatcher *fakeagent.FakeActionDispatcher
			jobSupervisor    *fakejobsuper.FakeJobSupervisor
			specService      *fakeas.FakeV1Service
			syslogServer     *fakesyslog.FakeServer
			settingsService  *fakesettings.FakeSettingsService
			uuidGenerator    *fakeuuid.FakeGenerator
			timeService      *fakeclock.FakeClock
			agent            Agent
		)

		BeforeEach(func() {
			logger = boshlog.NewLogger(boshlog.LevelNone)
			handler = &fakembus.FakeHandler{}
			platform = fakeplatform.NewFakePlatform()
			actionDispatcher = &fakeagent.FakeActionDispatcher{}
			jobSupervisor = fakejobsuper.NewFakeJobSupervisor()
			specService = fakeas.NewFakeV1Service()
			syslogServer = &fakesyslog.FakeServer{}
			settingsService = &fakesettings.FakeSettingsService{}
			uuidGenerator = &fakeuuid.FakeGenerator{}
			timeService = fakeclock.NewFakeClock(time.Now())
			agent = New(
				logger,
				handler,
				platform,
				actionDispatcher,
				jobSupervisor,
				specService,
				syslogServer,
				5*time.Millisecond,
				settingsService,
				uuidGenerator,
				timeService,
			)
		})

		Describe("Run", func() {
			It("lets dispatcher handle requests arriving via handler", func() {
				err := agent.Run()
				Expect(err).ToNot(HaveOccurred())

				expectedResp := boshhandler.NewValueResponse("pong")
				actionDispatcher.DispatchResp = expectedResp

				req := boshhandler.NewRequest("fake-reply", "fake-action", []byte("fake-payload"))
				resp := handler.RunFunc(req)

				Expect(actionDispatcher.DispatchReq).To(Equal(req))
				Expect(resp).To(Equal(expectedResp))
			})

			It("resumes persistent actions *before* dispatching new requests", func() {
				resumedBeforeStartingToDispatch := false
				handler.RunCallBack = func() {
					resumedBeforeStartingToDispatch = actionDispatcher.ResumedPreviouslyDispatchedTasks
				}

				err := agent.Run()
				Expect(err).ToNot(HaveOccurred())
				Expect(resumedBeforeStartingToDispatch).To(BeTrue())
			})

			Context("when heartbeats can be sent", func() {
				BeforeEach(func() {
					handler.KeepOnRunning()
				})

				BeforeEach(func() {
					jobName := "fake-job"
					nodeID := "node-id"
					jobIndex := 1
					specService.Spec = boshas.V1ApplySpec{
						JobSpec: boshas.JobSpec{Name: &jobName},
						Index:   &jobIndex,
						NodeID:  nodeID,
					}

					jobSupervisor.StatusStatus = "fake-state"

					platform.FakeVitalsService.GetVitals = boshvitals.Vitals{
						Load: []string{"a", "b", "c"},
					}
				})

				expectedJobName := "fake-job"
				expectedJobIndex := 1
				expectedNodeID := "node-id"
				expectedHb := Heartbeat{
					Job:      &expectedJobName,
					Index:    &expectedJobIndex,
					JobState: "fake-state",
					NodeID:   expectedNodeID,
					Vitals:   boshvitals.Vitals{Load: []string{"a", "b", "c"}},
				}

				It("sends initial heartbeat", func() {
					// Configure periodic heartbeat every 5 hours
					// so that we are sure that we will not receive it
					agent = New(
						logger,
						handler,
						platform,
						actionDispatcher,
						jobSupervisor,
						specService,
						syslogServer,
						5*time.Hour,
						settingsService,
						uuidGenerator,
						timeService,
					)

					// Immediately exit after sending initial heartbeat
					handler.SendErr = errors.New("stop")

					err := agent.Run()
					Expect(err).To(HaveOccurred())
					Expect(err.Error()).To(ContainSubstring("stop"))

					Expect(handler.SendInputs()).To(Equal([]fakembus.SendInput{
						{
							Target:  boshhandler.HealthMonitor,
							Topic:   boshhandler.Heartbeat,
							Message: expectedHb,
						},
					}))
				})

				It("sends periodic heartbeats", func() {
					sentRequests := 0
					handler.SendCallback = func(_ fakembus.SendInput) {
						sentRequests++
						if sentRequests == 3 {
							handler.SendErr = errors.New("stop")
						}
					}

					err := agent.Run()
					Expect(err).To(HaveOccurred())
					Expect(err.Error()).To(ContainSubstring("stop"))

					Expect(handler.SendInputs()).To(Equal([]fakembus.SendInput{
						{
							Target:  boshhandler.HealthMonitor,
							Topic:   boshhandler.Heartbeat,
							Message: expectedHb,
						},
						{
							Target:  boshhandler.HealthMonitor,
							Topic:   boshhandler.Heartbeat,
							Message: expectedHb,
						},
						{
							Target:  boshhandler.HealthMonitor,
							Topic:   boshhandler.Heartbeat,
							Message: expectedHb,
						},
					}))
				})
			})

			Context("when the agent fails to get job spec for a heartbeat", func() {
				BeforeEach(func() {
					specService.GetErr = errors.New("fake-spec-service-error")
					handler.KeepOnRunning()
				})

				It("returns the error", func() {
					err := agent.Run()
					Expect(err).To(HaveOccurred())
					Expect(err.Error()).To(ContainSubstring("fake-spec-service-error"))
				})
			})

			Context("when the agent fails to get vitals for a heartbeat", func() {
				BeforeEach(func() {
					platform.FakeVitalsService.GetErr = errors.New("fake-vitals-service-error")
					handler.KeepOnRunning()
				})

				It("returns the error", func() {
					err := agent.Run()
					Expect(err).To(HaveOccurred())
					Expect(err.Error()).To(ContainSubstring("fake-vitals-service-error"))
				})
			})

			It("sends job monitoring alerts to health manager", func() {
				handler.KeepOnRunning()

				monitAlert := boshalert.MonitAlert{
					ID:          "fake-monit-alert",
					Service:     "fake-service",
					Event:       "fake-event",
					Action:      "fake-action",
					Date:        "Sun, 22 May 2011 20:07:41 +0500",
					Description: "fake-description",
				}
				jobSupervisor.JobFailureAlert = &monitAlert

				// Fail the first time handler.Send is called for an alert (ignore heartbeats)
				handler.SendCallback = func(input fakembus.SendInput) {
					if input.Topic == boshhandler.Alert {
						handler.SendErr = errors.New("stop")
					}
				}

				err := agent.Run()
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("stop"))

				expectedAlert := boshalert.Alert{
					ID:        "fake-monit-alert",
					Severity:  boshalert.SeverityDefault,
					Title:     "fake-service - fake-event - fake-action",
					Summary:   "fake-description",
					CreatedAt: int64(1306076861),
				}

				Expect(handler.SendInputs()).To(ContainElement(fakembus.SendInput{
					Target:  boshhandler.HealthMonitor,
					Topic:   boshhandler.Alert,
					Message: expectedAlert,
				}))
			})

			It("sends ssh alerts to health manager", func() {
				handler.KeepOnRunning()

				syslogMsg := boshsyslog.Msg{Content: "disconnected by user"}
				syslogServer.StartFirstSyslogMsg = &syslogMsg

				uuidGenerator.GeneratedUUID = "fake-uuid"

				// Fail the first time handler.Send is called for an alert (ignore heartbeats)
				handler.SendCallback = func(input fakembus.SendInput) {
					if input.Topic == boshhandler.Alert {
						handler.SendErr = errors.New("stop")
					}
				}

				err := agent.Run()
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("stop"))

				expectedAlert := boshalert.Alert{
					ID:        "fake-uuid",
					Severity:  boshalert.SeverityWarning,
					Title:     "SSH Logout",
					Summary:   "disconnected by user",
					CreatedAt: timeService.Now().Unix(),
				}

				Expect(handler.SendInputs()).To(ContainElement(fakembus.SendInput{
					Target:  boshhandler.HealthMonitor,
					Topic:   boshhandler.Alert,
					Message: expectedAlert,
				}))
			})
		})
	})
}
コード例 #3
0
ファイル: apply_test.go プロジェクト: jianqiu/bosh-agent
func init() {
	Describe("ApplyAction", func() {
		var (
			applier         *fakeappl.FakeApplier
			specService     *fakeas.FakeV1Service
			settingsService *fakesettings.FakeSettingsService
			dirProvider     boshdir.Provider
			action          ApplyAction
			fs              boshsys.FileSystem
		)

		BeforeEach(func() {
			applier = fakeappl.NewFakeApplier()
			specService = fakeas.NewFakeV1Service()
			settingsService = &fakesettings.FakeSettingsService{}
			dirProvider = boshdir.NewProvider("/var/vcap")
			fs = fakesys.NewFakeFileSystem()
			action = NewApply(applier, specService, settingsService, dirProvider.InstanceDir(), fs)
		})

		It("apply should be asynchronous", func() {
			Expect(action.IsAsynchronous()).To(BeTrue())
		})

		It("is not persistent", func() {
			Expect(action.IsPersistent()).To(BeFalse())
		})

		Describe("Run", func() {
			settings := boshsettings.Settings{AgentID: "fake-agent-id"}

			BeforeEach(func() {
				settingsService.Settings = settings
			})

			Context("when desired spec has configuration hash", func() {
				currentApplySpec := boshas.V1ApplySpec{ConfigurationHash: "fake-current-config-hash"}
				desiredApplySpec := boshas.V1ApplySpec{ConfigurationHash: "fake-desired-config-hash"}
				populatedDesiredApplySpec := boshas.V1ApplySpec{
					ConfigurationHash: "fake-populated-desired-config-hash",
				}

				Context("when current spec can be retrieved", func() {
					BeforeEach(func() {
						specService.Spec = currentApplySpec
					})

					It("populates dynamic networks in desired spec", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).ToNot(HaveOccurred())
						Expect(specService.PopulateDHCPNetworksSpec).To(Equal(desiredApplySpec))
						Expect(specService.PopulateDHCPNetworksSettings).To(Equal(settings))
					})

					Context("when resolving dynamic networks succeeds", func() {
						BeforeEach(func() {
							specService.PopulateDHCPNetworksResultSpec = populatedDesiredApplySpec
						})

						It("runs applier with populated desired spec", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).ToNot(HaveOccurred())
							Expect(applier.Applied).To(BeTrue())
							Expect(applier.ApplyCurrentApplySpec).To(Equal(currentApplySpec))
							Expect(applier.ApplyDesiredApplySpec).To(Equal(populatedDesiredApplySpec))
						})

						Context("when applier succeeds applying desired spec", func() {
							Context("when saving desires spec as current spec succeeds", func() {
								It("returns 'applied' after setting populated desired spec as current spec", func() {
									value, err := action.Run(desiredApplySpec)
									Expect(err).ToNot(HaveOccurred())
									Expect(value).To(Equal("applied"))

									Expect(specService.Spec).To(Equal(populatedDesiredApplySpec))
								})

								Context("desired spec has id, instance name, deployment name, and az", func() {

									BeforeEach(func() {
										desiredApplySpec = boshas.V1ApplySpec{ConfigurationHash: "fake-desired-config-hash", NodeID: "node-id01-123f-r2344", AvailabilityZone: "ex-az", Deployment: "deployment-name", Name: "instance-name"}
										specService.PopulateDHCPNetworksResultSpec = desiredApplySpec
									})

									It("returns 'applied' and writes the id, instance name, deployment name, and az to files in the instance directory", func() {
										value, err := action.Run(desiredApplySpec)
										Expect(err).ToNot(HaveOccurred())
										Expect(value).To(Equal("applied"))

										instanceDir := dirProvider.InstanceDir()

										id, err := fs.ReadFileString(path.Join(instanceDir, "id"))
										Expect(err).ToNot(HaveOccurred())
										Expect(id).To(Equal(desiredApplySpec.NodeID))

										az, err := fs.ReadFileString(path.Join(instanceDir, "az"))
										Expect(err).ToNot(HaveOccurred())
										Expect(az).To(Equal(desiredApplySpec.AvailabilityZone))

										instanceName, err := fs.ReadFileString(path.Join(instanceDir, "name"))
										Expect(err).ToNot(HaveOccurred())
										Expect(instanceName).To(Equal(desiredApplySpec.Name))

										deploymentName, err := fs.ReadFileString(path.Join(instanceDir, "deployment"))
										Expect(err).ToNot(HaveOccurred())
										Expect(deploymentName).To(Equal(desiredApplySpec.Deployment))
									})
								})
							})

							Context("when saving populated desires spec as current spec fails", func() {
								It("returns error because agent was not able to remember that is converged to desired spec", func() {
									specService.SetErr = errors.New("fake-set-error")

									_, err := action.Run(desiredApplySpec)
									Expect(err).To(HaveOccurred())
									Expect(err.Error()).To(ContainSubstring("fake-set-error"))
								})
							})
						})

						Context("when applier fails applying desired spec", func() {
							BeforeEach(func() {
								applier.ApplyError = errors.New("fake-apply-error")
							})

							It("returns error", func() {
								_, err := action.Run(desiredApplySpec)
								Expect(err).To(HaveOccurred())
								Expect(err.Error()).To(ContainSubstring("fake-apply-error"))
							})

							It("does not save desired spec as current spec", func() {
								_, err := action.Run(desiredApplySpec)
								Expect(err).To(HaveOccurred())
								Expect(specService.Spec).To(Equal(currentApplySpec))
							})
						})
					})

					Context("when resolving dynamic networks fails", func() {
						BeforeEach(func() {
							specService.PopulateDHCPNetworksErr = errors.New("fake-populate-dynamic-networks-err")
						})

						It("returns error", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).To(HaveOccurred())
							Expect(err.Error()).To(ContainSubstring("fake-populate-dynamic-networks-err"))
						})

						It("does not apply desired spec as current spec", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).To(HaveOccurred())
							Expect(applier.Applied).To(BeFalse())
						})

						It("does not save desired spec as current spec", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).To(HaveOccurred())
							Expect(specService.Spec).To(Equal(currentApplySpec))
						})
					})
				})

				Context("when current spec cannot be retrieved", func() {
					BeforeEach(func() {
						specService.Spec = currentApplySpec
						specService.GetErr = errors.New("fake-get-error")
					})

					It("returns error and does not apply desired spec", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).To(HaveOccurred())
						Expect(err.Error()).To(ContainSubstring("fake-get-error"))
					})

					It("does not run applier with desired spec", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).To(HaveOccurred())
						Expect(applier.Applied).To(BeFalse())
					})

					It("does not save desired spec as current spec", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).To(HaveOccurred())
						Expect(specService.Spec).To(Equal(currentApplySpec))
					})
				})
			})

			Context("when desired spec does not have a configuration hash", func() {
				desiredApplySpec := boshas.V1ApplySpec{
					JobSpec: boshas.JobSpec{
						Template: "fake-job-template",
					},
				}

				populatedDesiredApplySpec := boshas.V1ApplySpec{
					JobSpec: boshas.JobSpec{
						Template: "fake-populated-job-template",
					},
				}

				It("populates dynamic networks in desired spec", func() {
					_, err := action.Run(desiredApplySpec)
					Expect(err).ToNot(HaveOccurred())
					Expect(specService.PopulateDHCPNetworksSpec).To(Equal(desiredApplySpec))
					Expect(specService.PopulateDHCPNetworksSettings).To(Equal(settings))
				})

				Context("when resolving dynamic networks succeeds", func() {
					BeforeEach(func() {
						specService.PopulateDHCPNetworksResultSpec = populatedDesiredApplySpec
					})

					Context("when saving desires spec as current spec succeeds", func() {
						It("returns 'applied' after setting desired spec as current spec", func() {
							value, err := action.Run(desiredApplySpec)
							Expect(err).ToNot(HaveOccurred())
							Expect(value).To(Equal("applied"))

							Expect(specService.Spec).To(Equal(populatedDesiredApplySpec))
						})

						It("does not try to apply desired spec since it does not have jobs and packages", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).ToNot(HaveOccurred())
							Expect(applier.Applied).To(BeFalse())
						})
					})

					Context("when saving desires spec as current spec fails", func() {
						BeforeEach(func() {
							specService.SetErr = errors.New("fake-set-error")
						})

						It("returns error because agent was not able to remember that is converged to desired spec", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).To(HaveOccurred())
							Expect(err.Error()).To(ContainSubstring("fake-set-error"))
						})

						It("does not try to apply desired spec since it does not have jobs and packages", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).To(HaveOccurred())
							Expect(applier.Applied).To(BeFalse())
						})
					})
				})

				Context("when resolving dynamic networks fails", func() {
					BeforeEach(func() {
						specService.PopulateDHCPNetworksErr = errors.New("fake-populate-dynamic-networks-err")
					})

					It("returns error", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).To(HaveOccurred())
						Expect(err.Error()).To(ContainSubstring("fake-populate-dynamic-networks-err"))
					})

					It("does not apply desired spec as current spec", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).To(HaveOccurred())
						Expect(applier.Applied).To(BeFalse())
					})

					It("does not save desired spec as current spec", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).To(HaveOccurred())
						Expect(specService.Spec).ToNot(Equal(desiredApplySpec))
					})
				})
			})
		})
	})
}
コード例 #4
0
ファイル: apply_test.go プロジェクト: keaty/bosh-provisioner
func init() {
	Describe("ApplyAction", func() {
		var (
			applier         *fakeappl.FakeApplier
			specService     *fakeas.FakeV1Service
			settingsService *fakesettings.FakeSettingsService
			action          ApplyAction
		)

		BeforeEach(func() {
			applier = fakeappl.NewFakeApplier()
			specService = fakeas.NewFakeV1Service()
			settingsService = &fakesettings.FakeSettingsService{}
			action = NewApply(applier, specService, settingsService)
		})

		It("apply should be asynchronous", func() {
			Expect(action.IsAsynchronous()).To(BeTrue())
		})

		It("is not persistent", func() {
			Expect(action.IsPersistent()).To(BeFalse())
		})

		Describe("Run", func() {
			settings := boshsettings.Settings{AgentID: "fake-agent-id"}

			BeforeEach(func() {
				settingsService.Settings = settings
			})

			Context("when desired spec has configuration hash", func() {
				currentApplySpec := boshas.V1ApplySpec{ConfigurationHash: "fake-current-config-hash"}
				desiredApplySpec := boshas.V1ApplySpec{ConfigurationHash: "fake-desired-config-hash"}
				populatedDesiredApplySpec := boshas.V1ApplySpec{
					ConfigurationHash: "fake-populated-desired-config-hash",
				}

				Context("when current spec can be retrieved", func() {
					BeforeEach(func() {
						specService.Spec = currentApplySpec
					})

					It("populates dynamic networks in desired spec", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).ToNot(HaveOccurred())
						Expect(specService.PopulateDynamicNetworksSpec).To(Equal(desiredApplySpec))
						Expect(specService.PopulateDynamicNetworksSettings).To(Equal(settings))
					})

					Context("when resolving dynamic networks succeeds", func() {
						BeforeEach(func() {
							specService.PopulateDynamicNetworksResultSpec = populatedDesiredApplySpec
						})

						It("runs applier with populated desired spec", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).ToNot(HaveOccurred())
							Expect(applier.Applied).To(BeTrue())
							Expect(applier.ApplyCurrentApplySpec).To(Equal(currentApplySpec))
							Expect(applier.ApplyDesiredApplySpec).To(Equal(populatedDesiredApplySpec))
						})

						Context("when applier succeeds applying desired spec", func() {
							Context("when saving desires spec as current spec succeeds", func() {
								It("returns 'applied' after setting populated desired spec as current spec", func() {
									value, err := action.Run(desiredApplySpec)
									Expect(err).ToNot(HaveOccurred())
									Expect(value).To(Equal("applied"))

									Expect(specService.Spec).To(Equal(populatedDesiredApplySpec))
								})
							})

							Context("when saving populated desires spec as current spec fails", func() {
								It("returns error because agent was not able to remember that is converged to desired spec", func() {
									specService.SetErr = errors.New("fake-set-error")

									_, err := action.Run(desiredApplySpec)
									Expect(err).To(HaveOccurred())
									Expect(err.Error()).To(ContainSubstring("fake-set-error"))
								})
							})
						})

						Context("when applier fails applying desired spec", func() {
							BeforeEach(func() {
								applier.ApplyError = errors.New("fake-apply-error")
							})

							It("returns error", func() {
								_, err := action.Run(desiredApplySpec)
								Expect(err).To(HaveOccurred())
								Expect(err.Error()).To(ContainSubstring("fake-apply-error"))
							})

							It("does not save desired spec as current spec", func() {
								_, err := action.Run(desiredApplySpec)
								Expect(err).To(HaveOccurred())
								Expect(specService.Spec).To(Equal(currentApplySpec))
							})
						})
					})

					Context("when resolving dynamic networks fails", func() {
						BeforeEach(func() {
							specService.PopulateDynamicNetworksErr = errors.New("fake-populate-dynamic-networks-err")
						})

						It("returns error", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).To(HaveOccurred())
							Expect(err.Error()).To(ContainSubstring("fake-populate-dynamic-networks-err"))
						})

						It("does not apply desired spec as current spec", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).To(HaveOccurred())
							Expect(applier.Applied).To(BeFalse())
						})

						It("does not save desired spec as current spec", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).To(HaveOccurred())
							Expect(specService.Spec).To(Equal(currentApplySpec))
						})
					})
				})

				Context("when current spec cannot be retrieved", func() {
					BeforeEach(func() {
						specService.Spec = currentApplySpec
						specService.GetErr = errors.New("fake-get-error")
					})

					It("returns error and does not apply desired spec", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).To(HaveOccurred())
						Expect(err.Error()).To(ContainSubstring("fake-get-error"))
					})

					It("does not run applier with desired spec", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).To(HaveOccurred())
						Expect(applier.Applied).To(BeFalse())
					})

					It("does not save desired spec as current spec", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).To(HaveOccurred())
						Expect(specService.Spec).To(Equal(currentApplySpec))
					})
				})
			})

			Context("when desired spec does not have a configuration hash", func() {
				desiredApplySpec := boshas.V1ApplySpec{
					JobSpec: boshas.JobSpec{
						Template: "fake-job-template",
					},
				}

				populatedDesiredApplySpec := boshas.V1ApplySpec{
					JobSpec: boshas.JobSpec{
						Template: "fake-populated-job-template",
					},
				}

				It("populates dynamic networks in desired spec", func() {
					_, err := action.Run(desiredApplySpec)
					Expect(err).ToNot(HaveOccurred())
					Expect(specService.PopulateDynamicNetworksSpec).To(Equal(desiredApplySpec))
					Expect(specService.PopulateDynamicNetworksSettings).To(Equal(settings))
				})

				Context("when resolving dynamic networks succeeds", func() {
					BeforeEach(func() {
						specService.PopulateDynamicNetworksResultSpec = populatedDesiredApplySpec
					})

					Context("when saving desires spec as current spec succeeds", func() {
						It("returns 'applied' after setting desired spec as current spec", func() {
							value, err := action.Run(desiredApplySpec)
							Expect(err).ToNot(HaveOccurred())
							Expect(value).To(Equal("applied"))

							Expect(specService.Spec).To(Equal(populatedDesiredApplySpec))
						})

						It("does not try to apply desired spec since it does not have jobs and packages", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).ToNot(HaveOccurred())
							Expect(applier.Applied).To(BeFalse())
						})
					})

					Context("when saving desires spec as current spec fails", func() {
						BeforeEach(func() {
							specService.SetErr = errors.New("fake-set-error")
						})

						It("returns error because agent was not able to remember that is converged to desired spec", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).To(HaveOccurred())
							Expect(err.Error()).To(ContainSubstring("fake-set-error"))
						})

						It("does not try to apply desired spec since it does not have jobs and packages", func() {
							_, err := action.Run(desiredApplySpec)
							Expect(err).To(HaveOccurred())
							Expect(applier.Applied).To(BeFalse())
						})
					})
				})

				Context("when resolving dynamic networks fails", func() {
					BeforeEach(func() {
						specService.PopulateDynamicNetworksErr = errors.New("fake-populate-dynamic-networks-err")
					})

					It("returns error", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).To(HaveOccurred())
						Expect(err.Error()).To(ContainSubstring("fake-populate-dynamic-networks-err"))
					})

					It("does not apply desired spec as current spec", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).To(HaveOccurred())
						Expect(applier.Applied).To(BeFalse())
					})

					It("does not save desired spec as current spec", func() {
						_, err := action.Run(desiredApplySpec)
						Expect(err).To(HaveOccurred())
						Expect(specService.Spec).ToNot(Equal(desiredApplySpec))
					})
				})
			})
		})
	})
}