From c292309030290adace895d188b752033299ec799 Mon Sep 17 00:00:00 2001 From: Aidan Oldershaw Date: Tue, 24 Mar 2020 15:22:45 -0400 Subject: [PATCH] fly: behaviour: add archive-pipeline command concourse/concourse#5320 Adds an archive-pipeline command that functions nearly identically to pause-pipeline Signed-off-by: Aidan Oldershaw Co-authored-by: James Thomson --- fly/commands/archive_pipeline.go | 76 +++++++++ fly/commands/fly.go | 1 + fly/integration/archive_pipeline_test.go | 188 +++++++++++++++++++++++ 3 files changed, 265 insertions(+) create mode 100644 fly/commands/archive_pipeline.go create mode 100644 fly/integration/archive_pipeline_test.go diff --git a/fly/commands/archive_pipeline.go b/fly/commands/archive_pipeline.go new file mode 100644 index 00000000000..fb0accdc7da --- /dev/null +++ b/fly/commands/archive_pipeline.go @@ -0,0 +1,76 @@ +package commands + +import ( + "fmt" + + "github.com/concourse/concourse/fly/commands/internal/displayhelpers" + "github.com/concourse/concourse/fly/commands/internal/flaghelpers" + "github.com/concourse/concourse/fly/rc" +) + +type ArchivePipelineCommand struct { + Pipeline flaghelpers.PipelineFlag `short:"p" long:"pipeline" description:"Pipeline to archive"` + All bool `short:"a" long:"all" description:"Archive all pipelines"` +} + +func (command *ArchivePipelineCommand) Validate() error { + return command.Pipeline.Validate() +} + +func (command *ArchivePipelineCommand) Execute(args []string) error { + if string(command.Pipeline) == "" && !command.All { + displayhelpers.Failf("Either a pipeline name or --all are required") + } + + if string(command.Pipeline) != "" && command.All { + displayhelpers.Failf("A pipeline and --all can not both be specified") + } + + err := command.Validate() + if err != nil { + return err + } + + target, err := rc.LoadTarget(Fly.Target, Fly.Verbose) + if err != nil { + return err + } + + err = target.Validate() + if err != nil { + return err + } + + var pipelineNames []string + if string(command.Pipeline) != "" { + pipelineNames = []string{string(command.Pipeline)} + } + + if command.All { + pipelines, err := target.Team().ListPipelines() + if err != nil { + return err + } + + for _, pipeline := range pipelines { + if !pipeline.Archived { + pipelineNames = append(pipelineNames, pipeline.Name) + } + } + } + + for _, pipelineName := range pipelineNames { + found, err := target.Team().ArchivePipeline(pipelineName) + if err != nil { + return err + } + + if found { + fmt.Printf("archived '%s'\n", pipelineName) + } else { + displayhelpers.Failf("pipeline '%s' not found\n", pipelineName) + } + } + + return nil +} diff --git a/fly/commands/fly.go b/fly/commands/fly.go index 5290e8d159a..da0e891effc 100644 --- a/fly/commands/fly.go +++ b/fly/commands/fly.go @@ -50,6 +50,7 @@ type FlyCommand struct { GetPipeline GetPipelineCommand `command:"get-pipeline" alias:"gp" description:"Get a pipeline's current configuration"` SetPipeline SetPipelineCommand `command:"set-pipeline" alias:"sp" description:"Create or update a pipeline's configuration"` PausePipeline PausePipelineCommand `command:"pause-pipeline" alias:"pp" description:"Pause a pipeline"` + ArchivePipeline ArchivePipelineCommand `command:"archive-pipeline" alias:"ap" description:"Archive a pipeline"` UnpausePipeline UnpausePipelineCommand `command:"unpause-pipeline" alias:"up" description:"Un-pause a pipeline"` ExposePipeline ExposePipelineCommand `command:"expose-pipeline" alias:"ep" description:"Make a pipeline publicly viewable"` HidePipeline HidePipelineCommand `command:"hide-pipeline" alias:"hp" description:"Hide a pipeline from the public"` diff --git a/fly/integration/archive_pipeline_test.go b/fly/integration/archive_pipeline_test.go new file mode 100644 index 00000000000..dff25b4efc6 --- /dev/null +++ b/fly/integration/archive_pipeline_test.go @@ -0,0 +1,188 @@ +package integration_test + +import ( + "net/http" + "os/exec" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/concourse/concourse/atc" + "github.com/onsi/gomega/gbytes" + "github.com/onsi/gomega/gexec" + "github.com/onsi/gomega/ghttp" + "github.com/tedsuo/rata" +) + +var _ = Describe("Fly CLI", func() { + Describe("archive-pipeline", func() { + Context("when the pipeline name is specified", func() { + var ( + path string + err error + ) + BeforeEach(func() { + path, err = atc.Routes.CreatePathForRoute(atc.ArchivePipeline, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) + Expect(err).NotTo(HaveOccurred()) + }) + + Context("when the pipeline exists", func() { + BeforeEach(func() { + atcServer.AppendHandlers( + ghttp.CombineHandlers( + ghttp.VerifyRequest("PUT", path), + ghttp.RespondWith(http.StatusOK, nil), + ), + ) + }) + + It("archives the pipeline", func() { + Expect(func() { + flyCmd := exec.Command(flyPath, "-t", targetName, "archive-pipeline", "-p", "awesome-pipeline") + + sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + + Eventually(sess).Should(gbytes.Say(`archived 'awesome-pipeline'`)) + + <-sess.Exited + Expect(sess.ExitCode()).To(Equal(0)) + }).To(Change(func() int { + return len(atcServer.ReceivedRequests()) + }).By(2)) + }) + }) + + Context("when the pipeline doesn't exist", func() { + BeforeEach(func() { + atcServer.AppendHandlers( + ghttp.CombineHandlers( + ghttp.VerifyRequest("PUT", path), + ghttp.RespondWith(http.StatusNotFound, nil), + ), + ) + }) + + It("prints helpful message", func() { + Expect(func() { + flyCmd := exec.Command(flyPath, "-t", targetName, "archive-pipeline", "-p", "awesome-pipeline") + + sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + + Eventually(sess.Err).Should(gbytes.Say(`pipeline 'awesome-pipeline' not found`)) + + <-sess.Exited + Expect(sess.ExitCode()).To(Equal(1)) + }).To(Change(func() int { + return len(atcServer.ReceivedRequests()) + }).By(2)) + }) + }) + }) + + Context("when the pipeline name or --all is not specified", func() { + It("errors", func() { + Expect(func() { + flyCmd := exec.Command(flyPath, "-t", targetName, "archive-pipeline") + + sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + + Eventually(sess.Err).Should(gbytes.Say(`Either a pipeline name or --all are required`)) + + <-sess.Exited + Expect(sess.ExitCode()).To(Equal(1)) + }).To(Change(func() int { + return len(atcServer.ReceivedRequests()) + }).By(0)) + }) + }) + + Context("when both the pipeline name and --all are specified", func() { + It("errors", func() { + Expect(func() { + flyCmd := exec.Command(flyPath, "-t", targetName, "archive-pipeline", "-p", "awesome-pipeline", "--all") + + sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + + Eventually(sess.Err).Should(gbytes.Say(`A pipeline and --all can not both be specified`)) + + <-sess.Exited + Expect(sess.ExitCode()).To(Equal(1)) + }).To(Change(func() int { + return len(atcServer.ReceivedRequests()) + }).By(0)) + }) + }) + + Context("when specifying a pipeline name with a '/' character in it", func() { + It("fails and says '/' characters are not allowed", func() { + flyCmd := exec.Command(flyPath, "-t", targetName, "archive-pipeline", "-p", "forbidden/pipelinename") + + sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + + <-sess.Exited + Expect(sess.ExitCode()).To(Equal(1)) + + Expect(sess.Err).To(gbytes.Say("error: pipeline name cannot contain '/'")) + }) + }) + + }) + + Context("when the --all flag is passed", func() { + var ( + somePath string + someOtherPath string + err error + ) + + BeforeEach(func() { + somePath, err = atc.Routes.CreatePathForRoute(atc.ArchivePipeline, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) + Expect(err).NotTo(HaveOccurred()) + + someOtherPath, err = atc.Routes.CreatePathForRoute(atc.ArchivePipeline, rata.Params{"pipeline_name": "more-awesome-pipeline", "team_name": "main"}) + Expect(err).NotTo(HaveOccurred()) + + atcServer.AppendHandlers( + ghttp.CombineHandlers( + ghttp.VerifyRequest("GET", "/api/v1/teams/main/pipelines"), + ghttp.RespondWithJSONEncoded(200, []atc.Pipeline{ + {Name: "awesome-pipeline", Archived: false, Public: false}, + {Name: "more-awesome-pipeline", Archived: false, Public: false}, + {Name: "already-archived", Archived: true, Public: false}, + }), + ), + ghttp.CombineHandlers( + ghttp.VerifyRequest("PUT", somePath), + ghttp.RespondWith(http.StatusOK, nil), + ), + ghttp.CombineHandlers( + ghttp.VerifyRequest("PUT", someOtherPath), + ghttp.RespondWith(http.StatusOK, nil), + ), + ) + }) + + It("archives every currently unarchived pipeline", func() { + Expect(func() { + flyCmd := exec.Command(flyPath, "-t", targetName, "archive-pipeline", "--all") + + sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + + Eventually(sess).Should(gbytes.Say(`archived 'awesome-pipeline'`)) + Eventually(sess).Should(gbytes.Say(`archived 'more-awesome-pipeline'`)) + Consistently(sess).ShouldNot(gbytes.Say(`archived 'already-archived'`)) + + <-sess.Exited + Expect(sess.ExitCode()).To(Equal(0)) + }).To(Change(func() int { + return len(atcServer.ReceivedRequests()) + }).By(4)) + }) + }) +})