Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SUP-1307 Implement Pipeline Team Resource #351

Merged
merged 74 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
62fa08c
Add empty pipeline resource using framework
jradtilbrook Jul 31, 2023
e4a58ac
Implement config, import and schema for pipeline
jradtilbrook Jul 31, 2023
1ef2ceb
Start implementing pipeline read
jradtilbrook Jul 31, 2023
ddede28
Start using pipeline through interface
jradtilbrook Jul 31, 2023
27bcc37
Finish pipeline read
jradtilbrook Jul 31, 2023
9fca758
Implement delete pipeline
jradtilbrook Jul 31, 2023
db01555
Implement remaining pipeline crud
jradtilbrook Jul 31, 2023
0829334
Some changes after running tests
jradtilbrook Jul 31, 2023
e1c6227
Add pipeline resource to provider
jradtilbrook Aug 1, 2023
ac6fc65
Fix bug with tags
jradtilbrook Aug 1, 2023
c707f48
Refactor pipeline REST requests
jradtilbrook Aug 1, 2023
22c07fb
WIP remove test
jradtilbrook Aug 2, 2023
9ec92c5
Leave tags null if empty
jradtilbrook Aug 2, 2023
4405730
Fix some tests
jradtilbrook Aug 2, 2023
8b1156c
Some refactoring to pass tests
jradtilbrook Aug 2, 2023
b4450ee
Fix formatting
jradtilbrook Aug 2, 2023
dc72700
Change test for pipeline disappearance
jradtilbrook Aug 2, 2023
e143061
Fix teams on update
jradtilbrook Aug 2, 2023
a7b18ba
Fix case where pipeline is deleted outside terraform
jradtilbrook Aug 2, 2023
ded4048
Deprecate deletion_protection
jradtilbrook Aug 3, 2023
1acc4d6
Add changelog entry
jradtilbrook Aug 3, 2023
902d4ae
Add test against old provider version
jradtilbrook Aug 3, 2023
fa1e014
Formatting
jradtilbrook Aug 3, 2023
776fb50
Test against cluster_id removal
jradtilbrook Aug 3, 2023
ac1a8d0
Add test condition for provider_settings
jradtilbrook Aug 3, 2023
6f52441
Initial Team Pipeline Implementation
lizrabuya Aug 3, 2023
8b8574b
Fix PipelineAccessLevel validations and Test errors
lizrabuya Aug 3, 2023
b6a0295
Add team pipeline resource
lizrabuya Aug 3, 2023
1a81c35
Fix resource name
lizrabuya Aug 3, 2023
dc6e852
Merge remote-tracking branch 'origin/main' into sup-1068-migrate-pipe…
jradtilbrook Aug 4, 2023
05d0aa6
Update tests to assert changed behaviour can be migrated
jradtilbrook Aug 4, 2023
f066db4
Update function names and object names to team_pipeline for consistency
lizrabuya Aug 4, 2023
d53570c
Remove tests for updating resource temporarily
lizrabuya Aug 4, 2023
ec9c14a
Update test check
jradtilbrook Aug 4, 2023
f85762f
Merge remote-tracking branch 'origin/main' into sup-1068-migrate-pipe…
jradtilbrook Aug 4, 2023
33a9390
Rename provider source
jradtilbrook Aug 4, 2023
9b4fcff
Merge branch 'main' into SUP-1307-pipeline_team_resource and updated …
lizrabuya Aug 7, 2023
2857d48
Merge branch 'main' into SUP-1307-pipeline_team_resource and updated …
lizrabuya Aug 7, 2023
1903890
Fix update resource logic to use create resource
lizrabuya Aug 7, 2023
8a468cd
Fix Update operations on pipeline team resource but keep update test …
lizrabuya Aug 8, 2023
cce2cf7
updated docs, examples and changelog
lizrabuya Aug 8, 2023
69e5bdd
Improvements from code review
jradtilbrook Aug 9, 2023
85c981d
Merge remote-tracking branch 'origin/main' into sup-1068-migrate-pipe…
jradtilbrook Aug 9, 2023
16f96f2
Try different source url
jradtilbrook Aug 9, 2023
cbe4f4b
Remove last test step
jradtilbrook Aug 9, 2023
9475016
Add plan modifications and validation
jradtilbrook Aug 9, 2023
29c9738
Remove plan modifier for slug
jradtilbrook Aug 9, 2023
02280e6
Save organization ID on test init
jradtilbrook Aug 10, 2023
758007a
Add tests for team functionality
jradtilbrook Aug 10, 2023
9f4ab10
Remove extra test check
jradtilbrook Aug 10, 2023
4d33867
Add custom logic for handling pipeline teams
jradtilbrook Aug 10, 2023
75d5de3
Revert "Add custom logic for handling pipeline teams"
jradtilbrook Aug 11, 2023
80c0af7
Change maximum timeout in minutes to pointer
jradtilbrook Aug 11, 2023
39079b1
Stop finding team in Update
jradtilbrook Aug 11, 2023
378ca7c
Merge branch 'sup-1068-migrate-pipeline-resource-to-framework' into p…
jradtilbrook Aug 11, 2023
c4f7374
Remove skipped test
jradtilbrook Aug 11, 2023
5c8d688
Update pipeline to ignore added teams
jradtilbrook Aug 11, 2023
8cf9f08
Fix: Changes to align with pipeline team is fully removed from pipel…
lizrabuya Aug 13, 2023
49392f0
Fix: Remove skipped test
lizrabuya Aug 13, 2023
d0a4daf
Fix: Updates base on review comments
lizrabuya Aug 14, 2023
c882e43
Merge branch 'main' into SUP-1307-pipeline_team_resource
lizrabuya Aug 14, 2023
2855d2a
Merge remote-tracking branch 'origin/SUP-1307-pipeline_team_resource'…
jradtilbrook Aug 14, 2023
4d9cd24
Fix: Missing } on setPipelineModel and removed unused function blocks
lizrabuya Aug 14, 2023
6b361b3
Fix: Create all teams returned from api in state
lizrabuya Aug 14, 2023
e24ecb2
Fix: Pipeline Teams Tests
lizrabuya Aug 14, 2023
32f76b5
Fix: Ignore slug attribute in test (remove team)
lizrabuya Aug 15, 2023
630b883
Merge branch 'main' into SUP-1307-pipeline_team_resource
lizrabuya Aug 15, 2023
b21005d
docs: Move out this PR from v0.24.0 release
lizrabuya Aug 15, 2023
9dc84ba
Fix: Added code that were replaced in a past merge
lizrabuya Aug 16, 2023
fa4ba43
Merge remote-tracking branch 'origin/main' into SUP-1307-pipeline_tea…
jradtilbrook Aug 21, 2023
963d82b
SUP-1307 Add deprecation message
jradtilbrook Aug 21, 2023
aa60b7e
SUP-1307 Update tests to framework
jradtilbrook Aug 21, 2023
e45fdac
SUP-1307 Refactor tests to framework
jradtilbrook Aug 21, 2023
99866fd
SUP-1307 Add check to confirm pipeline team block is not updated
jradtilbrook Aug 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.

## Unreleased
- refactor 🧹: Refactor templates to use Conventional Commits[[PR #348](https://github.com/buildkite/terraform-provider-buildkite/pull/348)] @mcncl
- SUP-1307: Implement Pipeline Team Resource[[PR #351](https://github.com/buildkite/terraform-provider-buildkite/pull/351)]


### Forthcoming Changes
`deletion_protection` is being deprecated and will be removed in a future release (`v1`). This feature offers similar
Expand Down
598 changes: 595 additions & 3 deletions buildkite/generated.go

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions buildkite/graphql/node.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@ query getNode(
... on TeamSuite {
...TeamSuiteFields
}
... on TeamPipeline {
... TeamPipelineFields
}
}
}
55 changes: 55 additions & 0 deletions buildkite/graphql/team_pipeline.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
fragment TeamPipelineFields on TeamPipeline {
id
uuid
pipelineAccessLevel: accessLevel
team {
id
}
pipeline {
id
}
}

mutation createTeamPipeline(
$teamID: ID!,
$pipelineID: ID!,
# @genqlient(omitempty: true)
$accessLevel: PipelineAccessLevels
) {
teamPipelineCreate(input:{
teamID: $teamID,
pipelineID: $pipelineID,
accessLevel: $accessLevel
}) {
teamPipelineEdge {
node {
...TeamPipelineFields
}
}
}
}

mutation updateTeamPipeline(
$id: ID!,
$accessLevel: PipelineAccessLevels!
) {
teamPipelineUpdate(input:{
id: $id,
accessLevel: $accessLevel
}) {
teamPipeline {
...TeamPipelineFields
}
}
}

mutation deleteTeamPipeline(
$id: ID!,
) {
teamPipelineDelete(input:{
id: $id
}) {
deletedTeamPipelineID
clientMutationId
}
}
1 change: 1 addition & 0 deletions buildkite/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ func (*terraformProvider) Resources(context.Context) []func() resource.Resource
newTeamMemberResource,
newTeamResource,
newTestSuiteResource,
newPipelineTeamResource,
newTestSuiteTeamResource,
}
}
Expand Down
250 changes: 250 additions & 0 deletions buildkite/resource_pipeline_team.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
package buildkite

import (
"context"
"fmt"

"github.com/Khan/genqlient/graphql"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
resource_schema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)

type pipelineTeamResourceModel struct {
Id types.String `tfsdk:"id"`
Uuid types.String `tfsdk:"uuid"`
PipelineId types.String `tfsdk:"pipeline_id"`
TeamId types.String `tfsdk:"team_id"`
AccessLevel types.String `tfsdk:"access_level"`
}

type pipelineTeamResource struct {
client *Client
}

func newPipelineTeamResource() resource.Resource {
return &pipelineTeamResource{}
}

func (pipelineTeamResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_pipeline_team"
}

func (tp *pipelineTeamResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

tp.client = req.ProviderData.(*Client)
}

func (pipelineTeamResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = resource_schema.Schema{
MarkdownDescription: "A team pipeline resource sets a team's access for the pipeline.",
Attributes: map[string]resource_schema.Attribute{
"id": resource_schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"uuid": resource_schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"team_id": resource_schema.StringAttribute{
Required: true,
MarkdownDescription: "The GraphQL ID of the team.",
},
"pipeline_id": resource_schema.StringAttribute{
Required: true,
MarkdownDescription: "The GraphQL ID of the pipeline.",
},
"access_level": resource_schema.StringAttribute{
Required: true,
jradtilbrook marked this conversation as resolved.
Show resolved Hide resolved
MarkdownDescription: "The access level for the team. Either READ_ONLY, BUILD_AND_READ or MANAGE_BUILD_AND_READ.",
Validators: []validator.String{
stringvalidator.OneOf("READ_ONLY", "BUILD_AND_READ", "MANAGE_BUILD_AND_READ"),
lizrabuya marked this conversation as resolved.
Show resolved Hide resolved
},
},
},
}
}

func (tp *pipelineTeamResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var state pipelineTeamResourceModel

resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...)

if resp.Diagnostics.HasError() {
return
}

apiResponse, err := createTeamPipeline(
tp.client.genqlient,
state.TeamId.ValueString(),
state.PipelineId.ValueString(),
PipelineAccessLevels(state.AccessLevel.ValueString()),
)

if err != nil {
resp.Diagnostics.AddError(
"Unable to create team pipeline",
fmt.Sprintf("Unable to create team pipeline: %s", err.Error()),
)
return
}

// Update state with values from API response/plan
state.Id = types.StringValue(apiResponse.TeamPipelineCreate.TeamPipelineEdge.Node.Id)
state.Uuid = types.StringValue(apiResponse.TeamPipelineCreate.TeamPipelineEdge.Node.Uuid)
state.PipelineId = types.StringValue(apiResponse.TeamPipelineCreate.TeamPipelineEdge.Node.Pipeline.Id)
state.TeamId = types.StringValue(apiResponse.TeamPipelineCreate.TeamPipelineEdge.Node.Team.Id)
state.AccessLevel = types.StringValue(string(apiResponse.TeamPipelineCreate.TeamPipelineEdge.Node.PipelineAccessLevel))
lizrabuya marked this conversation as resolved.
Show resolved Hide resolved

resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (tp *pipelineTeamResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state pipelineTeamResourceModel

resp.Diagnostics.Append(req.State.Get(ctx, &state)...)

if resp.Diagnostics.HasError() {
return
}

apiResponse, err := getNode(
tp.client.genqlient,
state.Id.ValueString(),
)

if err != nil {
resp.Diagnostics.AddError(
"Unable to read team pipeline",
fmt.Sprintf("Unable to read team pipeline: %s", err.Error()),
)
}

// Convert from Node to getNodeNodeTeamPipeline type
if teamPipelineNode, ok := apiResponse.GetNode().(*getNodeNodeTeamPipeline); ok {
if teamPipelineNode == nil {
resp.Diagnostics.AddError(
"Unable to get team pipeline",
"Error getting team pipeline: nil response",
)
return
}
updateTeamPipelineResourceState(&state, *teamPipelineNode)
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
} else {
// Resource not found, remove from state
resp.Diagnostics.AddWarning("Team pipeline resource not found", "Removing team pipeline from state")
resp.State.RemoveResource(ctx)
}

}

func (tp *pipelineTeamResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}

func (tp *pipelineTeamResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var state pipelineTeamResourceModel
var accessLevel string

resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("access_level"), &accessLevel)...)

if resp.Diagnostics.HasError() {
return
}

found := findTeamInPipelineNode(&tp.client.genqlient, &state)
if !found {
_, err := createTeamPipeline(
tp.client.genqlient,
state.TeamId.ValueString(),
state.PipelineId.ValueString(),
PipelineAccessLevels(accessLevel),
)

if err != nil {
resp.Diagnostics.AddError(
"Unable to create team pipeline",
fmt.Sprintf("Unable to create team pipeline: %s", err.Error()),
)
return
}
jradtilbrook marked this conversation as resolved.
Show resolved Hide resolved
} else {
_, err := updateTeamPipeline(tp.client.genqlient,
state.Id.ValueString(),
PipelineAccessLevels(accessLevel),
)

if err != nil {
resp.Diagnostics.AddError(
"Unable to update team pipeline",
fmt.Sprintf("Unable to update team pipeline: %s", err.Error()),
)
return
}
}

// Update state with values from API response/plan
state.AccessLevel = types.StringValue(accessLevel)
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (tp *pipelineTeamResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state pipelineTeamResourceModel

resp.Diagnostics.Append(req.State.Get(ctx, &state)...)

if resp.Diagnostics.HasError() {
return
}

_, err := deleteTeamPipeline(tp.client.genqlient, state.Id.ValueString())

if err != nil {
resp.Diagnostics.AddError(
"Unable to delete team pipeline",
fmt.Sprintf("Unable to delete team pipeline: %s", err.Error()),
)
return
}
}

func updateTeamPipelineResourceState(tpState *pipelineTeamResourceModel, tpNode getNodeNodeTeamPipeline) {
tpState.Id = types.StringValue(tpNode.Id)
tpState.Uuid = types.StringValue(tpNode.Uuid)
tpState.TeamId = types.StringValue(tpNode.Team.Id)
tpState.PipelineId = types.StringValue(tpNode.Pipeline.Id)
tpState.AccessLevel = types.StringValue(string(tpNode.PipelineAccessLevel))
}

func findTeamInPipelineNode(client *graphql.Client, tpState *pipelineTeamResourceModel) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks unused now. Can it be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the issue in line 74 comment, the unused function got reverted in this commit Fix: Missing } on setPipelineModel and removed unused function blocks. I will make sure it got reverted to that.


apiResponse, err := getNode(*client, tpState.PipelineId.ValueString())
if err != nil {
return false
}

teams := apiResponse.GetNode().(*getNodeNodePipeline).Teams.Edges
found := false
for _, team := range teams {
if team.Node.Id == tpState.TeamId.ValueString() {
found = true
break
}
}
return found
}
Loading