diff --git a/.github/workflows/kind.yml b/.github/workflows/kind.yml index b0866b5..a1071af 100644 --- a/.github/workflows/kind.yml +++ b/.github/workflows/kind.yml @@ -63,7 +63,7 @@ jobs: - name: Deploy vcsim run: | - kubectl create deployment vcsim --image=docker.io/vmware/vcsim + make deploy-vsphere-simulator kubectl wait --for=condition=Ready pods --all --timeout=240s kubectl port-forward --address 0.0.0.0 deploy/vcsim 8989:8989 & diff --git a/Makefile b/Makefile index 27e116c..c86b1f9 100644 --- a/Makefile +++ b/Makefile @@ -114,6 +114,9 @@ push-containers: build-containers $(PODMAN) push $(MIGRATION_PLANNER_AGENT_IMAGE):latest $(PODMAN) push $(MIGRATION_PLANNER_AGENT_IMAGE):$(REGISTRY_TAG) +deploy-vsphere-simulator: + $(KUBECTL) apply -f 'deploy/k8s/vcsim.yaml' + deploy-on-kind: sed "s|@MIGRATION_PLANNER_AGENT_IMAGE@|$(MIGRATION_PLANNER_AGENT_IMAGE)|g; \ s|@INSECURE_REGISTRY@|$(INSECURE_REGISTRY)|g; \ diff --git a/deploy/k8s/migration-planner.yaml b/deploy/k8s/migration-planner.yaml index dfec1f4..00c99cf 100644 --- a/deploy/k8s/migration-planner.yaml +++ b/deploy/k8s/migration-planner.yaml @@ -45,7 +45,6 @@ spec: value: quay.io/kubev2v/migration-planner-agent - name: INSECURE_REGISTRY value: "true" - volumeMounts: volumeMounts: - name: migration-planner-config mountPath: "/.migration-planner/config.yaml" diff --git a/deploy/k8s/vcsim.yaml b/deploy/k8s/vcsim.yaml new file mode 100644 index 0000000..b663590 --- /dev/null +++ b/deploy/k8s/vcsim.yaml @@ -0,0 +1,41 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + deployment.kubernetes.io/revision: "1" + generation: 1 + labels: + app: vcsim + name: vcsim + namespace: default +spec: + progressDeadlineSeconds: 600 + replicas: 1 + revisionHistoryLimit: 10 + selector: + matchLabels: + app: vcsim + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + creationTimestamp: null + labels: + app: vcsim + spec: + containers: + - name: vcsim + image: docker.io/vmware/vcsim + imagePullPolicy: Always + args: ["-l", "0.0.0.0:8989", "-username", "core", "-password", "123456"] + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 diff --git a/internal/agent/rest.go b/internal/agent/rest.go index c5fbc58..c761eed 100644 --- a/internal/agent/rest.go +++ b/internal/agent/rest.go @@ -132,7 +132,7 @@ func testVmwareConnection(requestCtx context.Context, credentials *config.Creden err = client.Login(ctx, u.User) if err != nil { err = liberr.Wrap(err) - if strings.Contains(err.Error(), "incorrect") && strings.Contains(err.Error(), "password") { + if strings.Contains(err.Error(), "Login failure") { return http.StatusUnauthorized, err } return http.StatusBadRequest, err diff --git a/test/e2e/e2e_agent_test.go b/test/e2e/e2e_agent_test.go index 0be085c..3db47e0 100644 --- a/test/e2e/e2e_agent_test.go +++ b/test/e2e/e2e_agent_test.go @@ -42,8 +42,9 @@ type PlannerAgent interface { type PlannerService interface { RemoveSources() error + RemoveAgent(UUID string) error GetSource() (*api.Source, error) - GetAgent() (*api.Agent, error) + GetAgent(credentialUrl string) (*api.Agent, error) } type plannerService struct { @@ -260,7 +261,7 @@ func NewPlannerService(configPath string) (*plannerService, error) { return &plannerService{c: c}, nil } -func (s *plannerService) GetAgent() (*api.Agent, error) { +func (s *plannerService) GetAgent(credentialUrl string) (*api.Agent, error) { ctx := context.TODO() res, err := s.c.ListAgentsWithResponse(ctx) if err != nil || res.HTTPResponse.StatusCode != 200 { @@ -271,9 +272,8 @@ func (s *plannerService) GetAgent() (*api.Agent, error) { return nil, fmt.Errorf("No agents found") } - nullUuid := uuid.UUID{} for _, agent := range *res.JSON200 { - if agent.Id != nullUuid.String() { + if agent.CredentialUrl == credentialUrl { return &agent, nil } } @@ -302,6 +302,18 @@ func (s *plannerService) GetSource() (*api.Source, error) { return nil, fmt.Errorf("No sources found") } +func (s *plannerService) RemoveAgent(UUID string) error { + parsedUUID, err1 := uuid.Parse(UUID) + if err1 != nil { + return err1 + } + _, err2 := s.c.DeleteAgentWithResponse(context.TODO(), parsedUUID) + if err2 != nil { + return err2 + } + return nil +} + func (s *plannerService) RemoveSources() error { _, err := s.c.DeleteSourcesWithResponse(context.TODO()) return err diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 92d1521..9536d7f 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -22,11 +22,11 @@ var _ = Describe("e2e", func() { BeforeEach(func() { svc, err = NewPlannerService(defaultConfigPath) - Expect(err).To(BeNil()) + Expect(err).To(BeNil(), "Failed to create PlannerService") agent, err = NewPlannerAgent(defaultConfigPath, vmName) - Expect(err).To(BeNil()) + Expect(err).To(BeNil(), "Failed to create PlannerAgent") err = agent.Run() - Expect(err).To(BeNil()) + Expect(err).To(BeNil(), "Failed to run PlannerAgent") Eventually(func() string { agentIP, err = agent.GetIp() if err != nil { @@ -40,7 +40,7 @@ var _ = Describe("e2e", func() { }, "3m", "2s").Should(BeTrue()) Eventually(func() string { - s, err := svc.GetAgent() + s, err := svc.GetAgent(fmt.Sprintf("http://%s:3333", agentIP)) if err != nil { return "" } @@ -50,9 +50,19 @@ var _ = Describe("e2e", func() { return "" }, "3m", "2s").Should(Equal(fmt.Sprintf("http://%s:3333", agentIP))) + + // Check that planner-agent service is running + r := agent.IsServiceRunning(agentIP, "planner-agent") + Expect(r).To(BeTrue()) }) AfterEach(func() { + s, err := svc.GetAgent(fmt.Sprintf("http://%s:3333", agentIP)) + if err != nil { + s = nil + } + _ = svc.RemoveAgent(s.Id) // remove the agent created within the 'BeforeEach' block from the DB + _ = svc.RemoveSources() _ = agent.Remove() }) @@ -61,18 +71,55 @@ var _ = Describe("e2e", func() { agent.DumpLogs(agentIP) }) + Context("Check Vcenter login behavior", func() { + + It("should successfully login with valid credentials", func() { + + res, err := agent.Login(fmt.Sprintf("https://%s:8989/sdk", systemIP), "core", "123456") + Expect(err).To(BeNil()) + Expect(res.StatusCode).To(Equal(http.StatusNoContent)) + + }) + + It("Two test combined: should return BadRequest due to an empty username"+ + " and BadRequest due to an empty password", func() { + + res1, err1 := agent.Login(fmt.Sprintf("https://%s:8989/sdk", systemIP), "", "pass") + Expect(err1).To(BeNil()) + Expect(res1.StatusCode).To(Equal(http.StatusBadRequest)) + + res2, err2 := agent.Login(fmt.Sprintf("https://%s:8989/sdk", systemIP), "user", "") + Expect(err2).To(BeNil()) + Expect(res2.StatusCode).To(Equal(http.StatusBadRequest)) + + }) + + It("should return Unauthorized due to invalid credentials", func() { + + res, err := agent.Login(fmt.Sprintf("https://%s:8989/sdk", systemIP), "invalid", "cred") + Expect(err).To(BeNil()) + Expect(res.StatusCode).To(Equal(http.StatusUnauthorized)) + + }) + + It("should return badRequest due to an invalid URL", func() { + + res, err := agent.Login(fmt.Sprintf("https://%s", systemIP), "user", "pass") // bad link to Vcenter environment + Expect(err).To(BeNil()) + Expect(res.StatusCode).To(Equal(http.StatusBadRequest)) + + }) + + }) + Context("Flow", func() { It("Up to date", func() { - // Check that planner-agent service is running - r := agent.IsServiceRunning(agentIP, "planner-agent") - Expect(r).To(BeTrue()) - // Put the vCenter credentials and check that source is up to date eventually - res, err := agent.Login(fmt.Sprintf("https://%s:8989/sdk", systemIP), "user", "pass") + res, err := agent.Login(fmt.Sprintf("https://%s:8989/sdk", systemIP), "core", "123456") Expect(err).To(BeNil()) Expect(res.StatusCode).To(Equal(http.StatusNoContent)) Eventually(func() bool { - apiAgent, err := svc.GetAgent() + apiAgent, err := svc.GetAgent(fmt.Sprintf("http://%s:3333", agentIP)) if err != nil { return false }