-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Adds resource BitbucketServerConfig to CloudBuild. #7083
Changes from all commits
8bc9469
d58b7b9
feda46b
48e8427
e9c40e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand that we probably not able to add tests for this field in our CI. Got a quick question regarding your local test:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did. I had to be sure to add custom code in the preupdate to remove this field from the field mask, as well as to then update it with custom code. Then made sure to test edge cases, 0 -> 1, many as well as update many, 1 -> 0, as well as a few other permutations. |
||
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' | ||
mariomachado94 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
|
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 | ||
} | ||
shuyama1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) | ||
if err != nil { | ||
return err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just want to confirm the behavior of this field. If we don't specify this field explicitly, will the API return a value for this field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it needs this field from the user, it will return an error if not specified (it is the bitbucket server user name to be used by our api).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I missed
required: true
setting somehow and thought it was an optional field, and that's why I asked the question. Thanks for confirming though!