diff --git a/CHANGELOG.md b/CHANGELOG.md index 0157890d..9d18b5d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,9 @@ All notable changes to this project will be documented in this file. ## Unreleased -* Support option to archive on delete [[PR #296](https://github.com/buildkite/terraform-provider-buildkite/pull/296)] -* SUP-270 Fix branch_configuration updating to empty string [[PR #298](https://github.com/buildkite/terraform-provider-buildkite/pull/298)] +* Support option to archive on delete [[PR #296](https://github.com/buildkite/terraform-provider-buildkite/pull/296)] @mcncl +* SUP-1085: Cluster Queue resource implementation [[PR #297](https://github.com/buildkite/terraform-provider-buildkite/pull/297)] @james2791 +* SUP-270 Fix branch_configuration updating to empty string [[PR #298](https://github.com/buildkite/terraform-provider-buildkite/pull/298)] @jradtilbrook ## [v0.19.2](https://github.com/buildkite/terraform-provider-buildkite/compare/v0.19.1...v0.19.2) * Consistent naming for environment variables [[PR #290](https://github.com/buildkite/terraform-provider-buildkite/pull/290)] @mcncl diff --git a/buildkite/generated.go b/buildkite/generated.go index c158eba1..cbaf8693 100644 --- a/buildkite/generated.go +++ b/buildkite/generated.go @@ -3,6 +3,8 @@ package buildkite import ( + "encoding/json" + "github.com/Khan/genqlient/graphql" ) @@ -26,6 +28,44 @@ const ( BuildRetentionPeriodsYears2 BuildRetentionPeriods = "YEARS_2" ) +// ClusterQueueValues includes the GraphQL fields of ClusterQueue requested by the fragment ClusterQueueValues. +type ClusterQueueValues struct { + Id string `json:"id"` + // The public UUID for this cluster queue + Uuid string `json:"uuid"` + Key string `json:"key"` + Description *string `json:"description"` + Cluster ClusterQueueValuesCluster `json:"cluster"` +} + +// GetId returns ClusterQueueValues.Id, and is useful for accessing the field via an interface. +func (v *ClusterQueueValues) GetId() string { return v.Id } + +// GetUuid returns ClusterQueueValues.Uuid, and is useful for accessing the field via an interface. +func (v *ClusterQueueValues) GetUuid() string { return v.Uuid } + +// GetKey returns ClusterQueueValues.Key, and is useful for accessing the field via an interface. +func (v *ClusterQueueValues) GetKey() string { return v.Key } + +// GetDescription returns ClusterQueueValues.Description, and is useful for accessing the field via an interface. +func (v *ClusterQueueValues) GetDescription() *string { return v.Description } + +// GetCluster returns ClusterQueueValues.Cluster, and is useful for accessing the field via an interface. +func (v *ClusterQueueValues) GetCluster() ClusterQueueValuesCluster { return v.Cluster } + +// ClusterQueueValuesCluster includes the requested fields of the GraphQL type Cluster. +type ClusterQueueValuesCluster struct { + Id string `json:"id"` + // The public UUID for this cluster + Uuid string `json:"uuid"` +} + +// GetId returns ClusterQueueValuesCluster.Id, and is useful for accessing the field via an interface. +func (v *ClusterQueueValuesCluster) GetId() string { return v.Id } + +// GetUuid returns ClusterQueueValuesCluster.Uuid, and is useful for accessing the field via an interface. +func (v *ClusterQueueValuesCluster) GetUuid() string { return v.Uuid } + // The roles a user can be within a team type GTeamMemberRole string @@ -270,6 +310,38 @@ func (v *__createAgentTokenInput) GetOrganizationId() string { return v.Organiza // GetDescription returns __createAgentTokenInput.Description, and is useful for accessing the field via an interface. func (v *__createAgentTokenInput) GetDescription() *string { return v.Description } +// __createClusterQueueInput is used internally by genqlient +type __createClusterQueueInput struct { + OrganizationId string `json:"organizationId"` + ClusterId string `json:"clusterId"` + Key string `json:"key"` + Description *string `json:"description"` +} + +// GetOrganizationId returns __createClusterQueueInput.OrganizationId, and is useful for accessing the field via an interface. +func (v *__createClusterQueueInput) GetOrganizationId() string { return v.OrganizationId } + +// GetClusterId returns __createClusterQueueInput.ClusterId, and is useful for accessing the field via an interface. +func (v *__createClusterQueueInput) GetClusterId() string { return v.ClusterId } + +// GetKey returns __createClusterQueueInput.Key, and is useful for accessing the field via an interface. +func (v *__createClusterQueueInput) GetKey() string { return v.Key } + +// GetDescription returns __createClusterQueueInput.Description, and is useful for accessing the field via an interface. +func (v *__createClusterQueueInput) GetDescription() *string { return v.Description } + +// __deleteClusterQueueInput is used internally by genqlient +type __deleteClusterQueueInput struct { + OrganizationId string `json:"organizationId"` + Id string `json:"id"` +} + +// GetOrganizationId returns __deleteClusterQueueInput.OrganizationId, and is useful for accessing the field via an interface. +func (v *__deleteClusterQueueInput) GetOrganizationId() string { return v.OrganizationId } + +// GetId returns __deleteClusterQueueInput.Id, and is useful for accessing the field via an interface. +func (v *__deleteClusterQueueInput) GetId() string { return v.Id } + // __getAgentTokenInput is used internally by genqlient type __getAgentTokenInput struct { Slug string `json:"slug"` @@ -278,6 +350,18 @@ type __getAgentTokenInput struct { // GetSlug returns __getAgentTokenInput.Slug, and is useful for accessing the field via an interface. func (v *__getAgentTokenInput) GetSlug() string { return v.Slug } +// __getClusterQueuesInput is used internally by genqlient +type __getClusterQueuesInput struct { + OrgSlug string `json:"orgSlug"` + Id string `json:"id"` +} + +// GetOrgSlug returns __getClusterQueuesInput.OrgSlug, and is useful for accessing the field via an interface. +func (v *__getClusterQueuesInput) GetOrgSlug() string { return v.OrgSlug } + +// GetId returns __getClusterQueuesInput.Id, and is useful for accessing the field via an interface. +func (v *__getClusterQueuesInput) GetId() string { return v.Id } + // __getOrganizationInput is used internally by genqlient type __getOrganizationInput struct { Slug string `json:"slug"` @@ -326,6 +410,22 @@ func (v *__setApiIpAddressesInput) GetOrganizationID() string { return v.Organiz // GetIpAddresses returns __setApiIpAddressesInput.IpAddresses, and is useful for accessing the field via an interface. func (v *__setApiIpAddressesInput) GetIpAddresses() string { return v.IpAddresses } +// __updateClusterQueueInput is used internally by genqlient +type __updateClusterQueueInput struct { + OrganizationId string `json:"organizationId"` + Id string `json:"id"` + Description *string `json:"description"` +} + +// GetOrganizationId returns __updateClusterQueueInput.OrganizationId, and is useful for accessing the field via an interface. +func (v *__updateClusterQueueInput) GetOrganizationId() string { return v.OrganizationId } + +// GetId returns __updateClusterQueueInput.Id, and is useful for accessing the field via an interface. +func (v *__updateClusterQueueInput) GetId() string { return v.Id } + +// GetDescription returns __updateClusterQueueInput.Description, and is useful for accessing the field via an interface. +func (v *__updateClusterQueueInput) GetDescription() *string { return v.Description } + // __updatePipelineInput is used internally by genqlient type __updatePipelineInput struct { Input PipelineUpdateInput `json:"input"` @@ -427,6 +527,141 @@ func (v *createAgentTokenResponse) GetAgentTokenCreate() createAgentTokenAgentTo return v.AgentTokenCreate } +// createClusterQueueClusterQueueCreateClusterQueueCreatePayload includes the requested fields of the GraphQL type ClusterQueueCreatePayload. +// The GraphQL type's documentation follows. +// +// Autogenerated return type of ClusterQueueCreate. +type createClusterQueueClusterQueueCreateClusterQueueCreatePayload struct { + ClusterQueue createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue `json:"clusterQueue"` +} + +// GetClusterQueue returns createClusterQueueClusterQueueCreateClusterQueueCreatePayload.ClusterQueue, and is useful for accessing the field via an interface. +func (v *createClusterQueueClusterQueueCreateClusterQueueCreatePayload) GetClusterQueue() createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue { + return v.ClusterQueue +} + +// createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue includes the requested fields of the GraphQL type ClusterQueue. +type createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue struct { + ClusterQueueValues `json:"-"` +} + +// GetId returns createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue.Id, and is useful for accessing the field via an interface. +func (v *createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue) GetId() string { + return v.ClusterQueueValues.Id +} + +// GetUuid returns createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue.Uuid, and is useful for accessing the field via an interface. +func (v *createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue) GetUuid() string { + return v.ClusterQueueValues.Uuid +} + +// GetKey returns createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue.Key, and is useful for accessing the field via an interface. +func (v *createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue) GetKey() string { + return v.ClusterQueueValues.Key +} + +// GetDescription returns createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue.Description, and is useful for accessing the field via an interface. +func (v *createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue) GetDescription() *string { + return v.ClusterQueueValues.Description +} + +// GetCluster returns createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue.Cluster, and is useful for accessing the field via an interface. +func (v *createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue) GetCluster() ClusterQueueValuesCluster { + return v.ClusterQueueValues.Cluster +} + +func (v *createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue + graphql.NoUnmarshalJSON + } + firstPass.createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.ClusterQueueValues) + if err != nil { + return err + } + return nil +} + +type __premarshalcreateClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue struct { + Id string `json:"id"` + + Uuid string `json:"uuid"` + + Key string `json:"key"` + + Description *string `json:"description"` + + Cluster ClusterQueueValuesCluster `json:"cluster"` +} + +func (v *createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *createClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue) __premarshalJSON() (*__premarshalcreateClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue, error) { + var retval __premarshalcreateClusterQueueClusterQueueCreateClusterQueueCreatePayloadClusterQueue + + retval.Id = v.ClusterQueueValues.Id + retval.Uuid = v.ClusterQueueValues.Uuid + retval.Key = v.ClusterQueueValues.Key + retval.Description = v.ClusterQueueValues.Description + retval.Cluster = v.ClusterQueueValues.Cluster + return &retval, nil +} + +// createClusterQueueResponse is returned by createClusterQueue on success. +type createClusterQueueResponse struct { + // Create a cluster queue. + ClusterQueueCreate createClusterQueueClusterQueueCreateClusterQueueCreatePayload `json:"clusterQueueCreate"` +} + +// GetClusterQueueCreate returns createClusterQueueResponse.ClusterQueueCreate, and is useful for accessing the field via an interface. +func (v *createClusterQueueResponse) GetClusterQueueCreate() createClusterQueueClusterQueueCreateClusterQueueCreatePayload { + return v.ClusterQueueCreate +} + +// deleteClusterQueueClusterQueueDeleteClusterQueueDeletePayload includes the requested fields of the GraphQL type ClusterQueueDeletePayload. +// The GraphQL type's documentation follows. +// +// Autogenerated return type of ClusterQueueDelete. +type deleteClusterQueueClusterQueueDeleteClusterQueueDeletePayload struct { + // A unique identifier for the client performing the mutation. + ClientMutationId string `json:"clientMutationId"` +} + +// GetClientMutationId returns deleteClusterQueueClusterQueueDeleteClusterQueueDeletePayload.ClientMutationId, and is useful for accessing the field via an interface. +func (v *deleteClusterQueueClusterQueueDeleteClusterQueueDeletePayload) GetClientMutationId() string { + return v.ClientMutationId +} + +// deleteClusterQueueResponse is returned by deleteClusterQueue on success. +type deleteClusterQueueResponse struct { + // Delete a cluster queue. + ClusterQueueDelete deleteClusterQueueClusterQueueDeleteClusterQueueDeletePayload `json:"clusterQueueDelete"` +} + +// GetClusterQueueDelete returns deleteClusterQueueResponse.ClusterQueueDelete, and is useful for accessing the field via an interface. +func (v *deleteClusterQueueResponse) GetClusterQueueDelete() deleteClusterQueueClusterQueueDeleteClusterQueueDeletePayload { + return v.ClusterQueueDelete +} + // getAgentTokenAgentToken includes the requested fields of the GraphQL type AgentToken. // The GraphQL type's documentation follows. // @@ -457,6 +692,147 @@ type getAgentTokenResponse struct { // GetAgentToken returns getAgentTokenResponse.AgentToken, and is useful for accessing the field via an interface. func (v *getAgentTokenResponse) GetAgentToken() getAgentTokenAgentToken { return v.AgentToken } +// getClusterQueuesOrganization includes the requested fields of the GraphQL type Organization. +// The GraphQL type's documentation follows. +// +// An organization +type getClusterQueuesOrganization struct { + // Return cluster in the Organization by UUID + Cluster getClusterQueuesOrganizationCluster `json:"cluster"` +} + +// GetCluster returns getClusterQueuesOrganization.Cluster, and is useful for accessing the field via an interface. +func (v *getClusterQueuesOrganization) GetCluster() getClusterQueuesOrganizationCluster { + return v.Cluster +} + +// getClusterQueuesOrganizationCluster includes the requested fields of the GraphQL type Cluster. +type getClusterQueuesOrganizationCluster struct { + Queues getClusterQueuesOrganizationClusterQueuesClusterQueueConnection `json:"queues"` +} + +// GetQueues returns getClusterQueuesOrganizationCluster.Queues, and is useful for accessing the field via an interface. +func (v *getClusterQueuesOrganizationCluster) GetQueues() getClusterQueuesOrganizationClusterQueuesClusterQueueConnection { + return v.Queues +} + +// getClusterQueuesOrganizationClusterQueuesClusterQueueConnection includes the requested fields of the GraphQL type ClusterQueueConnection. +type getClusterQueuesOrganizationClusterQueuesClusterQueueConnection struct { + Edges []getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdge `json:"edges"` +} + +// GetEdges returns getClusterQueuesOrganizationClusterQueuesClusterQueueConnection.Edges, and is useful for accessing the field via an interface. +func (v *getClusterQueuesOrganizationClusterQueuesClusterQueueConnection) GetEdges() []getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdge { + return v.Edges +} + +// getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdge includes the requested fields of the GraphQL type ClusterQueueEdge. +type getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdge struct { + Node getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue `json:"node"` +} + +// GetNode returns getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdge.Node, and is useful for accessing the field via an interface. +func (v *getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdge) GetNode() getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue { + return v.Node +} + +// getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue includes the requested fields of the GraphQL type ClusterQueue. +type getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue struct { + ClusterQueueValues `json:"-"` +} + +// GetId returns getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue.Id, and is useful for accessing the field via an interface. +func (v *getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue) GetId() string { + return v.ClusterQueueValues.Id +} + +// GetUuid returns getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue.Uuid, and is useful for accessing the field via an interface. +func (v *getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue) GetUuid() string { + return v.ClusterQueueValues.Uuid +} + +// GetKey returns getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue.Key, and is useful for accessing the field via an interface. +func (v *getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue) GetKey() string { + return v.ClusterQueueValues.Key +} + +// GetDescription returns getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue.Description, and is useful for accessing the field via an interface. +func (v *getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue) GetDescription() *string { + return v.ClusterQueueValues.Description +} + +// GetCluster returns getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue.Cluster, and is useful for accessing the field via an interface. +func (v *getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue) GetCluster() ClusterQueueValuesCluster { + return v.ClusterQueueValues.Cluster +} + +func (v *getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue + graphql.NoUnmarshalJSON + } + firstPass.getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.ClusterQueueValues) + if err != nil { + return err + } + return nil +} + +type __premarshalgetClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue struct { + Id string `json:"id"` + + Uuid string `json:"uuid"` + + Key string `json:"key"` + + Description *string `json:"description"` + + Cluster ClusterQueueValuesCluster `json:"cluster"` +} + +func (v *getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue) __premarshalJSON() (*__premarshalgetClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue, error) { + var retval __premarshalgetClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue + + retval.Id = v.ClusterQueueValues.Id + retval.Uuid = v.ClusterQueueValues.Uuid + retval.Key = v.ClusterQueueValues.Key + retval.Description = v.ClusterQueueValues.Description + retval.Cluster = v.ClusterQueueValues.Cluster + return &retval, nil +} + +// getClusterQueuesResponse is returned by getClusterQueues on success. +type getClusterQueuesResponse struct { + // Find an organization + Organization getClusterQueuesOrganization `json:"organization"` +} + +// GetOrganization returns getClusterQueuesResponse.Organization, and is useful for accessing the field via an interface. +func (v *getClusterQueuesResponse) GetOrganization() getClusterQueuesOrganization { + return v.Organization +} + // getOrganizationOrganization includes the requested fields of the GraphQL type Organization. // The GraphQL type's documentation follows. // @@ -702,6 +1078,116 @@ func (v *setApiIpAddressesResponse) GetOrganizationApiIpAllowlistUpdate() setApi return v.OrganizationApiIpAllowlistUpdate } +// updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayload includes the requested fields of the GraphQL type ClusterQueueUpdatePayload. +// The GraphQL type's documentation follows. +// +// Autogenerated return type of ClusterQueueUpdate. +type updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayload struct { + ClusterQueue updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue `json:"clusterQueue"` +} + +// GetClusterQueue returns updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayload.ClusterQueue, and is useful for accessing the field via an interface. +func (v *updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayload) GetClusterQueue() updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue { + return v.ClusterQueue +} + +// updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue includes the requested fields of the GraphQL type ClusterQueue. +type updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue struct { + ClusterQueueValues `json:"-"` +} + +// GetId returns updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue.Id, and is useful for accessing the field via an interface. +func (v *updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue) GetId() string { + return v.ClusterQueueValues.Id +} + +// GetUuid returns updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue.Uuid, and is useful for accessing the field via an interface. +func (v *updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue) GetUuid() string { + return v.ClusterQueueValues.Uuid +} + +// GetKey returns updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue.Key, and is useful for accessing the field via an interface. +func (v *updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue) GetKey() string { + return v.ClusterQueueValues.Key +} + +// GetDescription returns updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue.Description, and is useful for accessing the field via an interface. +func (v *updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue) GetDescription() *string { + return v.ClusterQueueValues.Description +} + +// GetCluster returns updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue.Cluster, and is useful for accessing the field via an interface. +func (v *updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue) GetCluster() ClusterQueueValuesCluster { + return v.ClusterQueueValues.Cluster +} + +func (v *updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue + graphql.NoUnmarshalJSON + } + firstPass.updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.ClusterQueueValues) + if err != nil { + return err + } + return nil +} + +type __premarshalupdateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue struct { + Id string `json:"id"` + + Uuid string `json:"uuid"` + + Key string `json:"key"` + + Description *string `json:"description"` + + Cluster ClusterQueueValuesCluster `json:"cluster"` +} + +func (v *updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue) __premarshalJSON() (*__premarshalupdateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue, error) { + var retval __premarshalupdateClusterQueueClusterQueueUpdateClusterQueueUpdatePayloadClusterQueue + + retval.Id = v.ClusterQueueValues.Id + retval.Uuid = v.ClusterQueueValues.Uuid + retval.Key = v.ClusterQueueValues.Key + retval.Description = v.ClusterQueueValues.Description + retval.Cluster = v.ClusterQueueValues.Cluster + return &retval, nil +} + +// updateClusterQueueResponse is returned by updateClusterQueue on success. +type updateClusterQueueResponse struct { + // Updates a cluster queue. + ClusterQueueUpdate updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayload `json:"clusterQueueUpdate"` +} + +// GetClusterQueueUpdate returns updateClusterQueueResponse.ClusterQueueUpdate, and is useful for accessing the field via an interface. +func (v *updateClusterQueueResponse) GetClusterQueueUpdate() updateClusterQueueClusterQueueUpdateClusterQueueUpdatePayload { + return v.ClusterQueueUpdate +} + // updatePipelinePipelineUpdatePipelineUpdatePayload includes the requested fields of the GraphQL type PipelineUpdatePayload. // The GraphQL type's documentation follows. // @@ -1105,6 +1591,94 @@ func createAgentToken( return &data, err } +// The query or mutation executed by createClusterQueue. +const createClusterQueue_Operation = ` +mutation createClusterQueue ($organizationId: ID!, $clusterId: ID!, $key: String!, $description: String) { + clusterQueueCreate(input: {organizationId:$organizationId,clusterId:$clusterId,key:$key,description:$description}) { + clusterQueue { + ... ClusterQueueValues + } + } +} +fragment ClusterQueueValues on ClusterQueue { + id + uuid + key + description + cluster { + id + uuid + } +} +` + +func createClusterQueue( + client graphql.Client, + organizationId string, + clusterId string, + key string, + description *string, +) (*createClusterQueueResponse, error) { + req := &graphql.Request{ + OpName: "createClusterQueue", + Query: createClusterQueue_Operation, + Variables: &__createClusterQueueInput{ + OrganizationId: organizationId, + ClusterId: clusterId, + Key: key, + Description: description, + }, + } + var err error + + var data createClusterQueueResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + nil, + req, + resp, + ) + + return &data, err +} + +// The query or mutation executed by deleteClusterQueue. +const deleteClusterQueue_Operation = ` +mutation deleteClusterQueue ($organizationId: ID!, $id: ID!) { + clusterQueueDelete(input: {organizationId:$organizationId,id:$id}) { + clientMutationId + } +} +` + +func deleteClusterQueue( + client graphql.Client, + organizationId string, + id string, +) (*deleteClusterQueueResponse, error) { + req := &graphql.Request{ + OpName: "deleteClusterQueue", + Query: deleteClusterQueue_Operation, + Variables: &__deleteClusterQueueInput{ + OrganizationId: organizationId, + Id: id, + }, + } + var err error + + var data deleteClusterQueueResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + nil, + req, + resp, + ) + + return &data, err +} + // The query or mutation executed by getAgentToken. const getAgentToken_Operation = ` query getAgentToken ($slug: ID!) { @@ -1141,6 +1715,60 @@ func getAgentToken( return &data, err } +// The query or mutation executed by getClusterQueues. +const getClusterQueues_Operation = ` +query getClusterQueues ($orgSlug: ID!, $id: ID!) { + organization(slug: $orgSlug) { + cluster(id: $id) { + queues(first: 50) { + edges { + node { + ... ClusterQueueValues + } + } + } + } + } +} +fragment ClusterQueueValues on ClusterQueue { + id + uuid + key + description + cluster { + id + uuid + } +} +` + +func getClusterQueues( + client graphql.Client, + orgSlug string, + id string, +) (*getClusterQueuesResponse, error) { + req := &graphql.Request{ + OpName: "getClusterQueues", + Query: getClusterQueues_Operation, + Variables: &__getClusterQueuesInput{ + OrgSlug: orgSlug, + Id: id, + }, + } + var err error + + var data getClusterQueuesResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + nil, + req, + resp, + ) + + return &data, err +} + // The query or mutation executed by getOrganization. const getOrganization_Operation = ` query getOrganization ($slug: ID!) { @@ -1339,6 +1967,56 @@ func setApiIpAddresses( return &data, err } +// The query or mutation executed by updateClusterQueue. +const updateClusterQueue_Operation = ` +mutation updateClusterQueue ($organizationId: ID!, $id: ID!, $description: String) { + clusterQueueUpdate(input: {organizationId:$organizationId,id:$id,description:$description}) { + clusterQueue { + ... ClusterQueueValues + } + } +} +fragment ClusterQueueValues on ClusterQueue { + id + uuid + key + description + cluster { + id + uuid + } +} +` + +func updateClusterQueue( + client graphql.Client, + organizationId string, + id string, + description *string, +) (*updateClusterQueueResponse, error) { + req := &graphql.Request{ + OpName: "updateClusterQueue", + Query: updateClusterQueue_Operation, + Variables: &__updateClusterQueueInput{ + OrganizationId: organizationId, + Id: id, + Description: description, + }, + } + var err error + + var data updateClusterQueueResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + nil, + req, + resp, + ) + + return &data, err +} + // The query or mutation executed by updatePipeline. const updatePipeline_Operation = ` mutation updatePipeline ($input: PipelineUpdateInput!) { diff --git a/buildkite/graphql/cluster_queue.graphql b/buildkite/graphql/cluster_queue.graphql new file mode 100644 index 00000000..67388cfc --- /dev/null +++ b/buildkite/graphql/cluster_queue.graphql @@ -0,0 +1,79 @@ +fragment ClusterQueueValues on ClusterQueue { + id + uuid + key + # @genqlient(pointer: true) + description + cluster { + id + uuid + } +} + +query getClusterQueues($orgSlug: ID!, $id: ID!) { + organization(slug: $orgSlug) { + cluster(id: $id) { + queues(first: 50) { + edges { + node { + ...ClusterQueueValues + } + } + } + } + } +} + +mutation createClusterQueue( + $organizationId: ID!, + $clusterId: ID!, + $key: String!, + # @genqlient(pointer: true) + $description: String +) { + clusterQueueCreate( + input: { + organizationId: $organizationId + clusterId: $clusterId, + key: $key + description: $description + } + ) { + clusterQueue { + ...ClusterQueueValues + } + } +} + +mutation updateClusterQueue( + $organizationId: ID!, + $id: ID!, + # @genqlient(pointer: true) + $description: String +) { + clusterQueueUpdate( + input: { + organizationId: $organizationId + id: $id + description: $description + } + ) { + clusterQueue { + ...ClusterQueueValues + } + } +} + +mutation deleteClusterQueue( + $organizationId: ID!, + $id: ID!, +){ + clusterQueueDelete( + input: { + organizationId: $organizationId + id: $id + } + ) { + clientMutationId + } +} \ No newline at end of file diff --git a/buildkite/provider.go b/buildkite/provider.go index 54beb2dc..c3cc2273 100644 --- a/buildkite/provider.go +++ b/buildkite/provider.go @@ -92,6 +92,7 @@ func (tf *terraformProvider) Metadata(ctx context.Context, req provider.Metadata func (*terraformProvider) Resources(context.Context) []func() resource.Resource { return []func() resource.Resource{ NewAgentTokenResource, + NewClusterQueueResource, } } diff --git a/buildkite/resource_cluster_queue.go b/buildkite/resource_cluster_queue.go new file mode 100644 index 00000000..59e6adcc --- /dev/null +++ b/buildkite/resource_cluster_queue.go @@ -0,0 +1,242 @@ +package buildkite + +import ( + "context" + "fmt" + "log" + "strings" + + "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/types" +) + +type ClusterQueueResourceModel struct { + Id types.String `tfsdk:"id"` + Uuid types.String `tfsdk:"uuid"` + ClusterId types.String `tfsdk:"cluster_id"` + ClusterUuid types.String `tfsdk:"cluster_uuid"` + Key types.String `tfsdk:"key"` + Description types.String `tfsdk:"description"` +} + +type ClusterQueueResource struct { + client *Client +} + +func NewClusterQueueResource() resource.Resource { + return &ClusterQueueResource{} +} + +func (ClusterQueueResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_cluster_queue" +} + +func (cq *ClusterQueueResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + cq.client = req.ProviderData.(*Client) +} + +func (ClusterQueueResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = resource_schema.Schema{ + MarkdownDescription: "A Cluster Queue is a queue belonging to a specific Cluster for its Agents to target builds on. ", + 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(), + }, + }, + "cluster_uuid": resource_schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "cluster_id": resource_schema.StringAttribute{ + Required: true, + MarkdownDescription: "The ID of the Cluster that this Cluster Queue belongs to.", + }, + "key": resource_schema.StringAttribute{ + Required: true, + MarkdownDescription: "The key of the Cluster Queue.", + }, + "description": resource_schema.StringAttribute{ + Optional: true, + MarkdownDescription: "A description for the Cluster Queue. ", + }, + }, + } +} + +func (cq *ClusterQueueResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan, state ClusterQueueResourceModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + + if resp.Diagnostics.HasError() { + return + } + + log.Printf("Creating cluster queue with key %s into cluster %s ...", plan.Key.ValueString(), plan.ClusterId.ValueString()) + apiResponse, err := createClusterQueue( + cq.client.genqlient, + cq.client.organizationId, + plan.ClusterId.ValueString(), + plan.Key.ValueString(), + plan.Description.ValueStringPointer(), + ) + + if err != nil { + resp.Diagnostics.AddError( + "Unable to create Cluster Queue", + fmt.Sprintf("Unable to create Cluster Queue: %s", err.Error()), + ) + return + } + + state.Id = types.StringValue(apiResponse.ClusterQueueCreate.ClusterQueue.Id) + state.Uuid = types.StringValue(apiResponse.ClusterQueueCreate.ClusterQueue.Uuid) + state.ClusterId = plan.ClusterId + state.ClusterUuid = types.StringValue(apiResponse.ClusterQueueCreate.ClusterQueue.Cluster.Uuid) + state.Key = types.StringValue(apiResponse.ClusterQueueCreate.ClusterQueue.Key) + state.Description = types.StringPointerValue(apiResponse.ClusterQueueCreate.ClusterQueue.Description) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (cq *ClusterQueueResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ClusterQueueResourceModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + + if resp.Diagnostics.HasError() { + return + } + + log.Printf("Getting cluster queues for cluster %s ...", state.ClusterUuid.ValueString()) + queues, err := getClusterQueues(cq.client.genqlient, cq.client.organization, state.ClusterUuid.ValueString()) + + if err != nil { + resp.Diagnostics.AddError( + "Unable to read Cluster Queues", + fmt.Sprintf("Unable to read Cluster Queues: %s", err.Error()), + ) + return + } + + // Find the cluster queue from the returned queues to update state + for _, edge := range queues.Organization.Cluster.Queues.Edges { + if edge.Node.Id == state.Id.ValueString() { + log.Printf("Found cluster queue with ID %s in cluster %s", edge.Node.Id, state.ClusterUuid.ValueString()) + // Update ClusterQueueResourceModel with Node values and append + updateClusterQueueResource(edge.Node, &state) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + return + } + } + + // If not returned by this point, the cluster queue could not be found + // This is a tradeoff of the current getClusterQueues Genqlient query (searches for 50 queues via the cluster UUID in state) + resp.Diagnostics.AddError( + "Unable to find Cluster Queue", + fmt.Sprintf("Unable to find Cluster Queue: %s", err.Error()), + ) + return +} + +func (cq *ClusterQueueResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + importComponents := strings.Split(req.ID, ",") + + if len(importComponents) != 2 || importComponents[0] == "" || importComponents[1] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: id,cluster_uuid. Got: %q", req.ID), + ) + return + } + + // Adding the cluster queue ID/cluster UUID to state for Read + log.Printf("Importing cluster queue %s ...", importComponents[0]) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), importComponents[0])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("cluster_uuid"), importComponents[1])...) +} + +func (cq *ClusterQueueResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var state ClusterQueueResourceModel + var description string + + //Load state and ontain description from plan (singularly) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("description"), &description)...) + + if resp.Diagnostics.HasError() { + return + } + + log.Printf("Updating cluster queue %s ...", state.Id.ValueString()) + apiResponse, err := updateClusterQueue( + cq.client.genqlient, + cq.client.organizationId, + state.Id.ValueString(), + &description, + ) + + if err != nil { + resp.Diagnostics.AddError( + "Unable to update Cluster Queue", + fmt.Sprintf("Unable to update Cluster Queue: %s", err.Error()), + ) + return + } + + state.Description = types.StringPointerValue(apiResponse.ClusterQueueUpdate.ClusterQueue.Description) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (cq *ClusterQueueResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var plan ClusterQueueResourceModel + + resp.Diagnostics.Append(req.State.Get(ctx, &plan)...) + + if resp.Diagnostics.HasError() { + return + } + + log.Printf("Deleting cluster queue %s ...", plan.Id.ValueString()) + _, err := deleteClusterQueue( + cq.client.genqlient, + cq.client.organizationId, + plan.Id.ValueString(), + ) + + if err != nil { + resp.Diagnostics.AddError( + "Unable to create Cluster Queue", + fmt.Sprintf("Unable to delete Cluster Queue: %s", err.Error()), + ) + return + } +} + +func updateClusterQueueResource(clusterQueueNode getClusterQueuesOrganizationClusterQueuesClusterQueueConnectionEdgesClusterQueueEdgeNodeClusterQueue, cq *ClusterQueueResourceModel) { + cq.Id = types.StringValue(clusterQueueNode.Id) + cq.Uuid = types.StringValue(clusterQueueNode.Uuid) + cq.Key = types.StringValue(clusterQueueNode.Key) + cq.Description = types.StringPointerValue(clusterQueueNode.Description) + cq.ClusterId = types.StringValue(clusterQueueNode.Cluster.Id) + cq.ClusterUuid = types.StringValue(clusterQueueNode.Cluster.Uuid) +} diff --git a/buildkite/resource_cluster_queue_test.go b/buildkite/resource_cluster_queue_test.go new file mode 100644 index 00000000..370bce7a --- /dev/null +++ b/buildkite/resource_cluster_queue_test.go @@ -0,0 +1,218 @@ +package buildkite + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func testAccClusterQueueConfigBasic(name string) string { + config := ` + + resource "buildkite_cluster_queue" "foobar" { + cluster_id = "Q2x1c3Rlci0tLTFkNmIxOTg5LTJmYjctNDRlMC04MWYyLTAxYjIxNzQ4MTVkMg==" + description = "Acceptance Test %s" + key = "foobar" + } + ` + + return fmt.Sprintf(config, name) +} + +// Confirm that we can create a new cluster queue, and then delete it without error +func TestAccClusterQueue_add_remove(t *testing.T) { + var cq ClusterQueueResourceModel + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: protoV6ProviderFactories(), + CheckDestroy: testAccCheckClusterQueueDestroy, + Steps: []resource.TestStep{ + { + Config: testAccClusterQueueConfigBasic("foo"), + Check: resource.ComposeAggregateTestCheckFunc( + // Confirm the cluster queue exists in the buildkite API + testAccCheckClusterQueueExists("buildkite_cluster_queue.foobar", &cq), + // Confirm the cluster queue has the correct values in Buildkite's system + testAccCheckClusterQueueRemoteValues(&cq, "Acceptance Test foo", "foobar"), + // Confirm the cluster queue has the correct values in terraform state + resource.TestCheckResourceAttr("buildkite_cluster_queue.foobar", "key", "foobar"), + resource.TestCheckResourceAttr("buildkite_cluster_queue.foobar", "description", "Acceptance Test foo"), + resource.TestCheckResourceAttrSet("buildkite_cluster_queue.foobar", "id"), + resource.TestCheckResourceAttrSet("buildkite_cluster_queue.foobar", "uuid"), + ), + }, + { + RefreshState: true, + PlanOnly: true, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("buildkite_cluster_queue.foobar", "key"), + resource.TestCheckResourceAttrSet("buildkite_cluster_queue.foobar", "description"), + ), + }, + }, + }) +} + +func TestAccClusterQueue_update(t *testing.T) { + var cq ClusterQueueResourceModel + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: protoV6ProviderFactories(), + CheckDestroy: testAccCheckClusterQueueDestroy, + Steps: []resource.TestStep{ + { + Config: testAccClusterQueueConfigBasic("foo"), + Check: resource.ComposeAggregateTestCheckFunc( + // Confirm the cluster queue exists in the buildkite API + testAccCheckClusterQueueExists("buildkite_cluster_queue.foobar", &cq), + // Confirm the cluster queue has the correct values in Buildkite's system + testAccCheckClusterQueueRemoteValues(&cq, "Acceptance Test foo", "foobar"), + // Confirm the cluster queue has the correct values in terraform state + resource.TestCheckResourceAttr("buildkite_cluster_queue.foobar", "description", "Acceptance Test foo"), + ), + }, + { + Config: testAccClusterQueueConfigBasic("bar"), + Check: resource.ComposeAggregateTestCheckFunc( + // Confirm the cluster queue exists in the buildkite API + testAccCheckClusterQueueExists("buildkite_cluster_queue.foobar", &cq), + // Confirm the cluster queue has the correct values in Buildkite's system + testAccCheckClusterQueueRemoteValues(&cq, "Acceptance Test bar", "foobar"), + // Confirm the cluster queue has the correct values in terraform state + resource.TestCheckResourceAttr("buildkite_cluster_queue.foobar", "description", "Acceptance Test bar"), + ), + }, + }, + }) +} + +func TestAccClusterQueue_import(t *testing.T) { + var cq ClusterQueueResourceModel + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: protoV6ProviderFactories(), + CheckDestroy: testAccCheckClusterQueueDestroy, + Steps: []resource.TestStep{ + { + Config: testAccClusterQueueConfigBasic("foo"), + Check: resource.ComposeAggregateTestCheckFunc( + // Confirm the cluster queue exists in the buildkite API + testAccCheckClusterQueueExists("buildkite_cluster_queue.foobar", &cq), + // Check to confirm the local state is correct before we re-import it + resource.TestCheckResourceAttr("buildkite_cluster_queue.foobar", "key", "foobar"), + resource.TestCheckResourceAttr("buildkite_cluster_queue.foobar", "description", "Acceptance Test foo"), + ), + }, + { + // re-import the resource (using the graphql token of the existing resource) and confirm they match + ResourceName: "buildkite_cluster_queue.foobar", + ImportStateIdFunc: testAccGetImportClusterQueueId(&cq), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckClusterQueueExists(resourceName string, clusterQueueResourceModel *ClusterQueueResourceModel) resource.TestCheckFunc { + return func(s *terraform.State) error { + resourceState, ok := s.RootModule().Resources[resourceName] + + if !ok { + return fmt.Errorf("Not found in state: %s", resourceName) + } + + if resourceState.Primary.ID == "" { + return fmt.Errorf("No ID is set in state") + } + + // Obtain queues of the queue's cluster from its cluster UUID + queues, err := getClusterQueues( + genqlientGraphql, + getenv("BUILDKITE_ORGANIZATION_SLUG"), + resourceState.Primary.Attributes["cluster_uuid"], + ) + + // If cluster queues were not able to be fetched by Genqlient + if err != nil { + return fmt.Errorf("Error fetching Cluster queues from graphql API: %v", err) + } + + // Obtain the ClusterQueueResourceModel from the queues slice + for _, edge := range queues.Organization.Cluster.Queues.Edges { + if edge.Node.Id == resourceState.Primary.ID { + updateClusterQueueResource(edge.Node, clusterQueueResourceModel) + break + } + } + + // If clusterQueueResourceModel isnt set from the queues slice + if clusterQueueResourceModel.Id.ValueString() == "" { + return fmt.Errorf("No Cluster queue found with graphql id: %s", resourceState.Primary.ID) + } + + return nil + } +} + +func testAccCheckClusterQueueRemoteValues(cq *ClusterQueueResourceModel, description, key string) resource.TestCheckFunc { + return func(s *terraform.State) error { + + if cq.Key.ValueString() != key { + return fmt.Errorf("Remote Cluster queue key (%s) doesn't match expected value (%s)", cq.Key, key) + } + + if cq.Description.ValueString() != description { + return fmt.Errorf("Remote Cluster queue description (%s) doesn't match expected value (%s)", cq.Description, description) + } + + return nil + } +} + +func testAccGetImportClusterQueueId(cq *ClusterQueueResourceModel) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + // Obtain trimmed cluster ID and cluster UUID + clusterUuid := strings.Trim(cq.Id.ValueString(), "\"") + clusterQueueID := strings.Trim(cq.ClusterUuid.ValueString(), "\"") + // Set ID for import + id := fmt.Sprintf("%s,%s", clusterUuid, clusterQueueID) + return id, nil + } +} + +func testAccCheckClusterQueueDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "buildkite_cluster_queue" { + continue + } + + // Obtain queues of the queue's cluster from its cluster UUID + queues, err := getClusterQueues( + genqlientGraphql, + getenv("BUILDKITE_ORGANIZATION_SLUG"), + rs.Primary.Attributes["cluster_uuid"], + ) + + // If cluster queues were not able to be fetched by Genqlient + if err != nil { + return fmt.Errorf("Error fetching Cluster queues from graphql API: %v", err) + } + + // Loop over the cluster's queues, error if the queue still exists + for _, edge := range queues.Organization.Cluster.Queues.Edges { + if edge.Node.Id == rs.Primary.ID { + return fmt.Errorf("Cluster queue still exists in cluster, expected not to find it") + } + } + + return nil + } + return nil +} diff --git a/docs/resources/cluster_queue.md b/docs/resources/cluster_queue.md new file mode 100644 index 00000000..8c2017d1 --- /dev/null +++ b/docs/resources/cluster_queue.md @@ -0,0 +1,74 @@ +# Resource: cluster_queue + +This resource allows you to create and manage cluster queues. + +Buildkite Documentation: https://buildkite.com/docs/clusters/manage-clusters#set-up-clusters-create-a-queue + +## Example Usage + +```hcl +provider "buildkite" {} + +resource "buildkite_cluster_queue" "queue1" { + cluster_id = "Q2x1c3Rlci0tLTMzMDc0ZDhiLTM4MjctNDRkNC05YTQ3LTkwN2E2NWZjODViNg==" + key = "prod-deploy" + description = "Prod deployment cluster queue" +} +``` + +## Argument Reference + +* `cluster_id` - (Required) The ID of the cluster that this cluster queue belongs to. +* `key` - (Required) The key of the cluster queue. +* `description` - (Optional) The description of the cluster queue. + +## Attribute Reference + +* `id` - The Graphql ID of the created cluster queue. +* `uuid` - The UUID of the created cluster queue. +* `cluster_uuid` - The UUID of the cluster that this cluster queue belongs to. + +## Import + +Cluster queues can be imported using its `GraphQL ID`, along with its respective cluster `UUID`, separated by a comma. e.g. + +``` +$ terraform import buildkite_cluster_queue.test Q2x1c3RlclF1ZXVlLS0tYjJiOGRhNTEtOWY5My00Y2MyLTkyMjktMGRiNzg3ZDQzOTAz,35498aaf-ad05-4fa5-9a07-91bf6cacd2bd +``` + +To find the cluster's `UUID` to utilize, you can use the below GraphQL query below. Alternatively, you can use this [pre-saved query](https://buildkite.com/user/graphql/console/3adf0389-02bd-45ef-adcd-4e8e5ae57f25), where you will need fo fill in the organization slug (ORGANIZATION_SLUG) for obtaining the relevant cluster name/`UUID` that the cluster queue is in. + +```graphql +query getClusters { + organization(slug: "ORGANIZATION_SLUG") { + clusters(first: 50) { + edges{ + node{ + name + uuid + } + } + } + } +} +``` + +After the cluster `UUID` has been found, you can use the below GraphQL query to find the cluster queue's `GraphQL ID`. Alternatively, this [pre-saved query](https://buildkite.com/user/graphql/console/1d913905-900e-40e7-8f46-651543487b5a) can be used, specifying the organization slug (ORGANIZATION_SLUG) and the cluster `UUID` from above (CLUSTER_UUID). + +```graphql +query getClusterQueues { + organization(slug: "ORGANIZATION_SLUG") { + cluster(id: "CLUSTER_UUID") { + queues(first: 50) { + edges { + node { + id + key + } + } + } + } + } +} +``` + diff --git a/examples/main.tf b/examples/main.tf index 81d0f366..5d34d3b9 100644 --- a/examples/main.tf +++ b/examples/main.tf @@ -25,6 +25,12 @@ resource "buildkite_agent_token" "fleet" { description = "token used by build fleet" } +resource "buildkite_cluster_queue" "queue1" { + cluster_id = "Q2x1c3Rlci0tLTMzMDc0ZDhiLTM4MjctNDRkNC05YTQ3LTkwN2E2NWZjODViNg==" + key = "dev" + description = "Dev cluster queue" +} + output "agent_token" { value = buildkite_agent_token.fleet.token } @@ -32,3 +38,4 @@ output "agent_token" { output "badge_url" { value = buildkite_pipeline.test.badge_url } +