Merge pull request #5729 from tlwr/master

Allow set_pipeline can be used across teams
This commit is contained in:
Alex Suraci 2020-06-23 11:12:43 -04:00 committed by GitHub
commit ba0f27a99c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 310 additions and 79 deletions

View File

@ -433,6 +433,8 @@ type PlanConfig struct {
// name of 'set_pipeline'
SetPipeline string `json:"set_pipeline,omitempty"`
VarFiles []string `json:"var_files,omitempty"`
// team of 'set_pipeline', only valid for main team
Team string `json:"team,omitempty"`
// config path, e.g. foo/build.yml. Multiple steps might have this field, e.g. Task step and SetPipeline step.
File string `json:"file,omitempty"`

View File

@ -130,7 +130,47 @@ func (step *SetPipelineStep) run(ctx context.Context, state RunState) error {
return nil
}
team := step.teamFactory.GetByID(step.metadata.TeamID)
var team db.Team
if step.plan.Team == "" {
team = step.teamFactory.GetByID(step.metadata.TeamID)
} else {
fmt.Fprintln(stderr, "\x1b[1;33mWARNING: specifying the team in a set_pipeline step is experimental and may be removed in the future!\x1b[0m")
fmt.Fprintln(stderr, "")
fmt.Fprintln(stderr, "\x1b[33mcontribute to discussion #5731 with feedback: https://github.com/concourse/concourse/discussions/5731\x1b[0m")
fmt.Fprintln(stderr, "")
currentTeam, found, err := step.teamFactory.FindTeam(step.metadata.TeamName)
if err != nil {
return err
}
if !found {
return fmt.Errorf("team %s not found", step.metadata.TeamName)
}
targetTeam, found, err := step.teamFactory.FindTeam(step.plan.Team)
if err != nil {
return err
}
if !found {
return fmt.Errorf("team %s not found", step.plan.Team)
}
permitted := false
if targetTeam.ID() == currentTeam.ID() {
permitted = true
}
if currentTeam.Admin() {
permitted = true
}
if !permitted {
return fmt.Errorf(
"only %s team can set another team's pipeline",
atc.DefaultTeamName,
)
}
team = targetTeam
}
fromVersion := db.ConfigVersion(0)
pipeline, found, err := team.Pipeline(step.plan.Name)

View File

@ -94,14 +94,7 @@ jobs:
credVarsTracker vars.CredVarsTracker
stepMetadata = exec.StepMetadata{
TeamID: 123,
TeamName: "some-team",
BuildID: 42,
BuildName: "some-build",
PipelineID: 4567,
PipelineName: "some-pipeline",
}
stepMetadata exec.StepMetadata
stdout, stderr *gbytes.Buffer
@ -135,7 +128,18 @@ jobs:
fakeTeam = new(dbfakes.FakeTeam)
fakePipeline = new(dbfakes.FakePipeline)
fakeTeam.NameReturns("some-team")
stepMetadata = exec.StepMetadata{
TeamID: 123,
TeamName: "some-team",
BuildID: 42,
BuildName: "some-build",
PipelineID: 4567,
PipelineName: "some-pipeline",
}
fakeTeam.IDReturns(stepMetadata.TeamID)
fakeTeam.NameReturns(stepMetadata.TeamName)
fakePipeline.NameReturns("some-pipeline")
fakeTeamFactory.GetByIDReturns(fakeTeam)
@ -305,6 +309,117 @@ jobs:
Expect(succeeded).To(BeTrue())
})
})
Context("when team is configured", func() {
var (
fakeUserCurrentTeam *dbfakes.FakeTeam
)
BeforeEach(func() {
fakeUserCurrentTeam = new(dbfakes.FakeTeam)
fakeUserCurrentTeam.IDReturns(111)
fakeUserCurrentTeam.NameReturns("main")
fakeUserCurrentTeam.AdminReturns(false)
stepMetadata.TeamID = fakeUserCurrentTeam.ID()
stepMetadata.TeamName = fakeUserCurrentTeam.Name()
fakeTeamFactory.FindTeamReturnsOnCall(
0,
fakeUserCurrentTeam, true, nil,
)
})
Context("when team is set to the empty string", func() {
BeforeEach(func() {
fakeTeam.PipelineReturns(fakePipeline, true, nil)
fakeTeam.SavePipelineReturns(fakePipeline, false, nil)
spPlan.Team = ""
})
It("should finish successfully", func() {
Expect(fakeDelegate.FinishedCallCount()).To(Equal(1))
_, succeeded := fakeDelegate.FinishedArgsForCall(0)
Expect(succeeded).To(BeTrue())
})
})
Context("when team does not exist", func() {
BeforeEach(func() {
spPlan.Team = "not-found"
fakeTeamFactory.FindTeamReturnsOnCall(
1,
nil, false, nil,
)
})
It("should return error", func() {
Expect(stepErr).To(HaveOccurred())
Expect(stepErr.Error()).To(Equal("team not-found not found"))
})
})
Context("when team exists", func() {
Context("when the target team is the current team", func() {
BeforeEach(func() {
spPlan.Team = fakeUserCurrentTeam.Name()
fakeTeamFactory.FindTeamReturnsOnCall(
1,
fakeUserCurrentTeam, true, nil,
)
fakeUserCurrentTeam.PipelineReturns(fakePipeline, true, nil)
fakeUserCurrentTeam.SavePipelineReturns(fakePipeline, false, nil)
})
It("should finish successfully", func() {
Expect(fakeDelegate.FinishedCallCount()).To(Equal(1))
_, succeeded := fakeDelegate.FinishedArgsForCall(0)
Expect(succeeded).To(BeTrue())
})
It("should print an experimental message", func() {
Expect(stderr).To(gbytes.Say("WARNING: specifying the team"))
Expect(stderr).To(gbytes.Say("contribute to discussion #5731"))
Expect(stderr).To(gbytes.Say("discussions/5731"))
})
})
Context("when the team is not the current team", func() {
BeforeEach(func() {
spPlan.Team = fakeTeam.Name()
fakeTeamFactory.FindTeamReturnsOnCall(
1,
fakeTeam, true, nil,
)
})
Context("when the current team is an admin team", func() {
BeforeEach(func() {
fakeUserCurrentTeam.AdminReturns(true)
fakeTeam.PipelineReturns(fakePipeline, true, nil)
fakeTeam.SavePipelineReturns(fakePipeline, false, nil)
})
It("should finish successfully", func() {
Expect(fakeDelegate.FinishedCallCount()).To(Equal(1))
_, succeeded := fakeDelegate.FinishedArgsForCall(0)
Expect(succeeded).To(BeTrue())
})
})
Context("when the current team is not an admin team", func() {
It("should return error", func() {
Expect(stepErr).To(HaveOccurred())
Expect(stepErr.Error()).To(Equal(
"only main team can set another team's pipeline",
))
})
})
})
})
})
})
})
})

View File

@ -211,6 +211,7 @@ type TaskPlan struct {
type SetPipelinePlan struct {
Name string `json:"name"`
File string `json:"file"`
Team string `json:"team,omitempty"`
Vars map[string]interface{} `json:"vars,omitempty"`
VarFiles []string `json:"var_files,omitempty"`
}

View File

@ -262,8 +262,10 @@ func (plan TaskPlan) Public() *json.RawMessage {
func (plan SetPipelinePlan) Public() *json.RawMessage {
return enc(struct {
Name string `json:"name"`
Team string `json:"team"`
}{
Name: plan.Name,
Team: plan.Team,
})
}

View File

@ -354,6 +354,7 @@ var _ = Describe("Plan", func() {
ID: "37",
SetPipeline: &atc.SetPipelinePlan{
Name: "some-pipeline",
Team: "some-team",
File: "some-file",
VarFiles: []string{"vf"},
Vars: map[string]interface{}{"k1": "v1"},
@ -621,7 +622,8 @@ var _ = Describe("Plan", func() {
{
"id": "37",
"set_pipeline": {
"name": "some-pipeline"
"name": "some-pipeline",
"team": "some-team"
}
}
]

View File

@ -281,6 +281,7 @@ func (factory *buildFactory) basePlan(
plan = factory.planFactory.NewPlan(atc.SetPipelinePlan{
Name: name,
File: planConfig.File,
Team: planConfig.Team,
Vars: planConfig.Vars,
VarFiles: planConfig.VarFiles,
})

View File

@ -0,0 +1,37 @@
---
resources:
- name: some-resource
type: mock
source:
create_files:
pipeline.yml: |
---
jobs:
- name: normal-job
public: true
plan:
- task: a-task
config:
platform: linux
image_resource:
type: mock
source: {mirror_self: true}
run:
path: echo
args: ["hello world"]
name.yml: |
---
name: somebody
jobs:
- name: sp
public: true
plan:
- get: some-resource
- set_pipeline: ((pipeline_name))
team: ((team_name))
file: some-resource/pipeline.yml
var_files:
- some-resource/name.yml
vars:
greetings: hello

View File

@ -1,103 +1,134 @@
package testflight_test
import (
"fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/gexec"
"io/ioutil"
"os"
"path/filepath"
)
var _ = Describe("set-pipeline Step", func() {
const pipeline_content = `---
resources:
- name: some-resource
type: mock
source:
create_files:
pipeline.yml: |
---
jobs:
- name: normal-job
public: true
plan:
- task: a-task
config:
platform: linux
image_resource:
type: mock
source: {mirror_self: true}
run:
path: echo
args: ["hello world"]
name.yml: |
---
name: somebody
jobs:
- name: sp
public: true
plan:
- get: some-resource
- set_pipeline: %s
file: some-resource/pipeline.yml
var_files:
- some-resource/name.yml
vars:
greetings: hello
`
var fixture string
var (
createdPipelineName string
currentTeamName string
currentFlyTarget string
)
BeforeEach(func() {
var err error
fixture = filepath.Join(tmp, "fixture")
err = os.MkdirAll(fixture, 0755)
Expect(err).NotTo(HaveOccurred())
createdPipelineName = randomPipelineName()
})
Context("set other pipeline", func() {
var secondPipelineName string
BeforeEach(func() {
pipelineName = "first-sp"
secondPipelineName = "second-sp"
err := ioutil.WriteFile(
filepath.Join(fixture, pipelineName+".yml"),
[]byte(fmt.Sprintf(pipeline_content, secondPipelineName)),
0755,
JustBeforeEach(func() {
withFlyTarget(currentFlyTarget, func() {
setAndUnpausePipeline(
"fixtures/set-pipeline.yml",
"-v", "team_name="+currentTeamName,
"-v", "pipeline_name="+createdPipelineName,
)
Expect(err).NotTo(HaveOccurred())
})
})
fly("set-pipeline", "-n", "-p", pipelineName, "-c", fixture+"/"+pipelineName+".yml")
fly("unpause-pipeline", "-p", pipelineName)
AfterEach(func() {
withFlyTarget(currentFlyTarget, func() {
fly("destroy-pipeline", "-n", "-p", pipelineName)
})
})
Context("when setting the current team's pipeline", func() {
BeforeEach(func() {
currentFlyTarget = testflightFlyTarget
currentTeamName = ""
})
AfterEach(func() {
fly("destroy-pipeline", "-n", "-p", secondPipelineName)
fly("destroy-pipeline", "-n", "-p", createdPipelineName)
})
It("set the other pipeline", func() {
It("sets the other pipeline", func() {
By("second pipeline should initially not exist")
execS := spawnFly("get-pipeline", "-p", secondPipelineName)
execS := spawnFly("get-pipeline", "-p", createdPipelineName)
<-execS.Exited
Expect(execS).To(gexec.Exit(1))
Expect(execS.Err).To(gbytes.Say("pipeline not found"))
By("set-pipeline step should succeed")
execS = fly("trigger-job", "-w", "-j", pipelineName+"/sp")
Expect(execS.Out).To(gbytes.Say("setting pipeline: second-sp"))
Expect(execS.Out).To(gbytes.Say("setting pipeline: " + createdPipelineName))
Expect(execS.Out).To(gbytes.Say("done"))
By("should trigger the second pipeline job successfully")
execS = fly("trigger-job", "-w", "-j", secondPipelineName+"/normal-job")
execS = fly("trigger-job", "-w", "-j", createdPipelineName+"/normal-job")
Expect(execS.Out).To(gbytes.Say("hello world"))
})
})
Context("when setting another team's pipeline from the main team", func() {
BeforeEach(func() {
currentFlyTarget = adminFlyTarget
currentTeamName = teamName
})
It("sets the other pipeline", func() {
By("second pipeline should initially not exist")
withFlyTarget(testflightFlyTarget, func() {
execS := spawnFly("get-pipeline", "-p", createdPipelineName)
<-execS.Exited
Expect(execS).To(gexec.Exit(1))
Expect(execS.Err).To(gbytes.Say("pipeline not found"))
})
By("set-pipeline step should succeed")
withFlyTarget(adminFlyTarget, func() {
execS := fly("trigger-job", "-w", "-j", pipelineName+"/sp")
Expect(execS.Out).To(gbytes.Say("setting pipeline: " + createdPipelineName))
Expect(execS.Out).To(gbytes.Say("done"))
})
By("should trigger the second pipeline job successfully")
withFlyTarget(testflightFlyTarget, func() {
execS := fly("trigger-job", "-w", "-j", createdPipelineName+"/normal-job")
Expect(execS.Out).To(gbytes.Say("hello world"))
})
})
AfterEach(func() {
withFlyTarget(testflightFlyTarget, func() {
fly("destroy-pipeline", "-n", "-p", createdPipelineName)
})
})
})
Context("when setting the main team's pipeline from a normal team", func() {
BeforeEach(func() {
currentFlyTarget = testflightFlyTarget
currentTeamName = "main"
})
It("fails to set the other pipeline", func() {
By("second pipeline should initially not exist")
withFlyTarget(adminFlyTarget, func() {
execS := spawnFly("get-pipeline", "-p", createdPipelineName)
<-execS.Exited
Expect(execS).To(gexec.Exit(1))
Expect(execS.Err).To(gbytes.Say("pipeline not found"))
})
By("set-pipeline step should fail")
withFlyTarget(testflightFlyTarget, func() {
execS := spawnFly("trigger-job", "-w", "-j", pipelineName+"/sp")
<-execS.Exited
Expect(execS).To(gexec.Exit(2))
Expect(execS.Out).To(gbytes.Say("only main team can set another team's pipeline"))
Expect(execS.Out).To(gbytes.Say("errored"))
})
By("second pipeline should still not exist")
withFlyTarget(adminFlyTarget, func() {
execS := spawnFly("get-pipeline", "-p", createdPipelineName)
<-execS.Exited
Expect(execS).To(gexec.Exit(1))
Expect(execS.Err).To(gbytes.Say("pipeline not found"))
})
})
})
})