Skip to content

Commit

Permalink
Adds resource BitbucketServerConfig to CloudBuild. (GoogleCloudPlatfo…
Browse files Browse the repository at this point in the history
…rm#7083)

* Adds resource BitbucketServerConfig to CloudBuild.

* Adds example bitbucket config with peered_network and ssl_ca fields being used

* Minor code refactor. Removing unnecessary code for Cloud Build BitbucketServerConfig

* Update doc description mmv1/products/cloudbuild/api.yaml

Co-authored-by: Shuya Ma <[email protected]>

* Update doc description mmv1/products/cloudbuild/api.yaml

Co-authored-by: Shuya Ma <[email protected]>

---------

Co-authored-by: Mario Machado <[email protected]>
Co-authored-by: Shuya Ma <[email protected]>
  • Loading branch information
3 people authored and kubalaguna committed Feb 27, 2023
1 parent 6194061 commit aacacd3
Show file tree
Hide file tree
Showing 9 changed files with 362 additions and 0 deletions.
124 changes: 124 additions & 0 deletions mmv1/products/cloudbuild/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,130 @@ apis_required:
name: Cloud Build API
url: https://console.cloud.google.com/apis/library/cloudbuild.googleapis.com/
objects:
- !ruby/object:Api::Resource
name: 'BitbucketServerConfig'
base_url: projects/{{project}}/locations/{{location}}/bitbucketServerConfigs
self_link: projects/{{project}}/locations/{{location}}/bitbucketServerConfigs/{{bitbucketServerConfigId}}
create_url: projects/{{project}}/locations/{{location}}/bitbucketServerConfigs?bitbucketServerConfigId={{bitbucketServerConfigId}}
update_verb: :PATCH
update_mask: true
references: !ruby/object:Api::Resource::ReferenceLinks
guides:
'Connect to a Bitbucket Server host': 'https://cloud.google.com/build/docs/automating-builds/bitbucket/connect-host-bitbucket-server'
api: 'https://cloud.google.com/build/docs/api/reference/rest/v1/projects.locations.bitbucketServerConfigs'
description: |
BitbucketServerConfig represents the configuration for a Bitbucket Server.
async: !ruby/object:Api::OpAsync
operation: !ruby/object:Api::OpAsync::Operation
path: 'name'
base_url: '{{op_id}}'
wait_ms: 1000
result: !ruby/object:Api::OpAsync::Result
path: 'response'
resource_inside_response: true
status: !ruby/object:Api::OpAsync::Status
path: 'done'
complete: true
allowed:
- true
- false
error: !ruby/object:Api::OpAsync::Error
path: 'error'
message: 'message'
parameters:
- !ruby/object:Api::Type::String
name: 'bitbucketServerConfigId'
required: true
url_param_only: true
input: true
description: |
The ID to use for the BitbucketServerConfig, which will become the final component of the BitbucketServerConfig's resource name.
- !ruby/object:Api::Type::String
name: 'location'
url_param_only: true
input: true
required: true
description: |
The location of this bitbucket server config.
properties:
- !ruby/object:Api::Type::String
name: 'name'
output: true
description: |
The resource name for the config.
- !ruby/object:Api::Type::String
name: 'hostUri'
required: true
description: |
Immutable. The URI of the Bitbucket Server host. Once this field has been set, it cannot be changed.
If you need to change it, please create another BitbucketServerConfig.
- !ruby/object:Api::Type::NestedObject
name: 'secrets'
required: true
description: |
Secret Manager secrets needed by the config.
properties:
- !ruby/object:Api::Type::String
name: 'adminAccessTokenVersionName'
required: true
description: |
The resource name for the admin access token's secret version.
- !ruby/object:Api::Type::String
name: 'readAccessTokenVersionName'
required: true
description: |
The resource name for the read access token's secret version.
- !ruby/object:Api::Type::String
name: 'webhookSecretVersionName'
required: true
input: true
description: |
Immutable. The resource name for the webhook secret's secret version. Once this field has been set, it cannot be changed.
Changing this field will result in deleting/ recreating the resource.
- !ruby/object:Api::Type::String
name: 'username'
required: true
description: |
Username of the account Cloud Build will use on Bitbucket Server.
- !ruby/object:Api::Type::String
name: 'webhookKey'
output: true
description: |
Output only. UUID included in webhook requests. The UUID is used to look up the corresponding config.
- !ruby/object:Api::Type::String
name: 'apiKey'
required: true
input: true
description: |
Immutable. API Key that will be attached to webhook. Once this field has been set, it cannot be changed.
Changing this field will result in deleting/ recreating the resource.
- !ruby/object:Api::Type::Array
name: 'connectedRepositories'
description: |
Connected Bitbucket Server repositories for this config.
item_type: !ruby/object:Api::Type::NestedObject
properties:
- !ruby/object:Api::Type::String
name: 'projectKey'
required: true
description: |
Identifier for the project storing the repository.
- !ruby/object:Api::Type::String
name: 'repoSlug'
required: true
description: |
Identifier for the repository.
- !ruby/object:Api::Type::String
name: 'peeredNetwork'
description: |
The network to be used when reaching out to the Bitbucket Server instance. The VPC network must be enabled for private service connection.
This should be set if the Bitbucket Server instance is hosted on-premises and not reachable by public internet. If this field is left empty,
no network peering will occur and calls to the Bitbucket Server instance will be made over the public internet. Must be in the format
projects/{project}/global/networks/{network}, where {project} is a project number or id and {network} is the name of a VPC network in the project.
- !ruby/object:Api::Type::String
name: 'sslCa'
description: |
SSL certificate to use for requests to Bitbucket Server. The format should be PEM format but the extension can be one of .pem, .cer, or .crt.
- !ruby/object:Api::Resource
name: 'Trigger'
base_url: projects/{{project}}/locations/{{location}}/triggers
Expand Down
32 changes: 32 additions & 0 deletions mmv1/products/cloudbuild/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,38 @@
--- !ruby/object:Provider::Terraform::Config
legacy_name: 'cloudbuild'
overrides: !ruby/object:Overrides::ResourceOverrides
BitbucketServerConfig: !ruby/object:Overrides::Terraform::ResourceOverride
autogen_async: true
import_format:
- "projects/{{project}}/locations/{{location}}/bitbucketServerConfigs/{{config_id}}"
id_format: 'projects/{{project}}/locations/{{location}}/bitbucketServerConfigs/{{config_id}}'
self_link: projects/{{project}}/locations/{{location}}/bitbucketServerConfigs/{{config_id}}
create_url: projects/{{project}}/locations/{{location}}/bitbucketServerConfigs?bitbucketServerConfigId={{config_id}}
examples:
- !ruby/object:Provider::Terraform::Examples
name: "cloudbuild_bitbucket_server_config"
primary_resource_id: "bbs-config"
- !ruby/object:Provider::Terraform::Examples
name: "cloudbuild_bitbucket_server_config_repositories"
primary_resource_id: "bbs-config-with-repos"
skip_test: true
- !ruby/object:Provider::Terraform::Examples
name: "cloudbuild_bitbucket_server_config_peered_network"
primary_resource_id: "bbs-config-with-peered-network"
vars:
network_name: "vpc-network"
test_vars_overrides:
network_name: 'BootstrapSharedTestNetwork(t, "peered-network")'
properties:
bitbucketServerConfigId: !ruby/object:Overrides::Terraform::PropertyOverride
name: 'config_id'
connectedRepositories: !ruby/object:Overrides::Terraform::PropertyOverride
is_set: true
custom_code: !ruby/object:Provider::Terraform::CustomCode
encoder: templates/terraform/encoders/cloudbuild_bitbucketserver_config.go.erb
post_create: templates/terraform/post_create/cloudbuild_bitbucketserver_config.go.erb
pre_update: templates/terraform/pre_update/cloudbuild_bitbucketserver_config.go.erb
post_update: templates/terraform/post_update/cloudbuild_bitbucketserver_config.go.erb
Trigger: !ruby/object:Overrides::Terraform::ResourceOverride
docs: !ruby/object:Provider::Terraform::Docs
note: |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// connectedRepositories is needed for batchCreate on the config after creation.
delete(obj, "connectedRepositories")
return obj, nil
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
resource "google_cloudbuild_bitbucket_server_config" "<%= ctx[:primary_resource_id] %>" {
config_id = "mybbsconfig"
location = "us-central1"
host_uri = "https://bbs.com"
secrets {
admin_access_token_version_name = "projects/myProject/secrets/mybbspat/versions/1"
read_access_token_version_name = "projects/myProject/secrets/mybbspat/versions/1"
webhook_secret_version_name = "projects/myProject/secrets/mybbspat/versions/1"
}
username = "test"
api_key = "<api-key>"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
data "google_project" "project" {}

resource "google_project_service" "servicenetworking" {
service = "servicenetworking.googleapis.com"
disable_on_destroy = false
}

data "google_compute_network" "vpc_network" {
name = "<%= ctx[:vars]['network_name'] %>"
depends_on = [google_project_service.servicenetworking]
}

resource "google_compute_global_address" "private_ip_alloc" {
name = "private-ip-alloc"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = 16
network = data.google_compute_network.vpc_network.id
}

resource "google_service_networking_connection" "default" {
network = data.google_compute_network.vpc_network.id
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.private_ip_alloc.name]
depends_on = [google_project_service.servicenetworking]
}

resource "google_cloudbuild_bitbucket_server_config" "<%= ctx[:primary_resource_id] %>" {
config_id = "mybbsconfig"
location = "us-central1"
host_uri = "https://bbs.com"
secrets {
admin_access_token_version_name = "projects/myProject/secrets/mybbspat/versions/1"
read_access_token_version_name = "projects/myProject/secrets/mybbspat/versions/1"
webhook_secret_version_name = "projects/myProject/secrets/mybbspat/versions/1"
}
username = "test"
api_key = "<api-key>"
peered_network = replace(data.google_compute_network.vpc_network.id, data.google_project.project.name, data.google_project.project.number)
ssl_ca = "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n"
depends_on = [google_service_networking_connection.default]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
resource "google_cloudbuild_bitbucket_server_config" "<%= ctx[:primary_resource_id] %>" {
config_id = "mybbsconfig"
location = "us-central1"
host_uri = "https://bbs.com"
secrets {
admin_access_token_version_name = "projects/myProject/secrets/mybbspat/versions/1"
read_access_token_version_name = "projects/myProject/secrets/mybbspat/versions/1"
webhook_secret_version_name = "projects/myProject/secrets/mybbspat/versions/1"
}
username = "test"
api_key = "<api-key>"

connected_repositories {
project_key = "DEV"
repo_slug = "repo1"
}
connected_repositories {
project_key = "PROD"
repo_slug = "repo1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
log.Printf("[DEBUG] Finished creating BitbucketServerConfig without connected repos: %q: %#v", d.Id(), res)

if v, ok := d.GetOkExists("connected_repositories"); !isEmptyValue(reflect.ValueOf(connectedRepositoriesProp)) && (ok || !reflect.DeepEqual(v, connectedRepositoriesProp)) {
connectedReposPropArray, ok := connectedRepositoriesProp.([]interface{})
if !ok {
return fmt.Errorf("Error reading connected_repositories")
}

requests := make([]interface{}, len(connectedReposPropArray))
for i := 0; i < len(connectedReposPropArray); i++ {
connectedRepo := make(map[string]interface{})
connectedRepo["parent"] = id
connectedRepo["repo"] = connectedReposPropArray[i]

connectedRepoRequest := make(map[string]interface{})
connectedRepoRequest["parent"] = id
connectedRepoRequest["bitbucketServerConnectedRepository"] = connectedRepo

requests[i] = connectedRepoRequest
}
obj = make(map[string]interface{})
obj["requests"] = requests

url, err = replaceVars(d, config, "{{CloudBuildBasePath}}projects/{{project}}/locations/{{location}}/bitbucketServerConfigs/{{config_id}}/connectedRepositories:batchCreate")
if err != nil {
return err
}

res, err = sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate))
if err != nil {
return fmt.Errorf("Error creating connected_repositories: %s", err)
}

err = cloudBuildOperationWaitTime(
config, res, project, "Creating connected_repositories on BitbucketServerConfig", userAgent,
d.Timeout(schema.TimeoutCreate))
if err != nil {
return fmt.Errorf("Error waiting to create connected_repositories: %s", err)
}
} else {
log.Printf("[DEBUG] No connected repositories found to create: %#v", connectedRepositoriesProp)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
if d.HasChange("connected_repositories") {
o, n := d.GetChange("connected_repositories")
oReposSet, ok := o.(*schema.Set)
if !ok {
return fmt.Errorf("Error reading old connected repositories")
}
nReposSet, ok := n.(*schema.Set)
if !ok {
return fmt.Errorf("Error reading new connected repositories")
}

removeRepos := oReposSet.Difference(nReposSet).List()
createRepos := nReposSet.Difference(oReposSet).List()

url, err = replaceVars(d, config, "{{CloudBuildBasePath}}projects/{{project}}/locations/{{location}}/bitbucketServerConfigs/{{config_id}}:removeBitbucketServerConnectedRepository")
if err != nil {
return err
}

// send remove repo requests.
for _, repo := range removeRepos {
obj := make(map[string]interface{})
obj["connectedRepository"] = repo
res, err = sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate))
if err != nil {
return fmt.Errorf("Error removing connected_repositories: %s", err)
}
}

// if repos to create, prepare and send batchCreate request
if len(createRepos) > 0 {
parent, err := replaceVars(d, config, "projects/{{project}}/locations/{{location}}/bitbucketServerConfigs/{{config_id}}")
if err != nil {
return fmt.Errorf("Error constructing id: %s", err)
}
var requests []interface{}
for _, repo := range createRepos {
connectedRepo := make(map[string]interface{})
connectedRepo["parent"] = parent
connectedRepo["repo"] = repo

connectedRepoRequest := make(map[string]interface{})
connectedRepoRequest["parent"] = parent
connectedRepoRequest["bitbucketServerConnectedRepository"] = connectedRepo

requests = append(requests, connectedRepoRequest)
}
obj = make(map[string]interface{})
obj["requests"] = requests

url, err = replaceVars(d, config, "{{CloudBuildBasePath}}projects/{{project}}/locations/{{location}}/bitbucketServerConfigs/{{config_id}}/connectedRepositories:batchCreate")
if err != nil {
return err
}

res, err = sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate))
if err != nil {
return fmt.Errorf("Error creating connected_repositories: %s", err)
}

err = cloudBuildOperationWaitTime(
config, res, project, "Updating connected_repositories on BitbucketServerConfig", userAgent,
d.Timeout(schema.TimeoutUpdate))
if err != nil {
return fmt.Errorf("Error waiting to create connected_repositories: %s", err)
}
}
} else {
log.Printf("[DEBUG] connected_repositories have no changes")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// remove connectedRepositories from updateMask
for i, field := range updateMask {
if field == "connectedRepositories" {
updateMask = append(updateMask[:i], updateMask[i+1:]...)
break
}
}
// reconstruct url
url, err = replaceVars(d, config, "{{CloudBuildBasePath}}projects/{{project}}/locations/{{location}}/bitbucketServerConfigs/{{config_id}}")
if err != nil {
return err
}
url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
if err != nil {
return err
}

0 comments on commit aacacd3

Please sign in to comment.