Refactor project module, support per-file tags in tfdoc (#450)
* add support for in-doc tfdoc options overrides * clean up project module * add file description tags * only output module and resource columns in tfdoc file table if they exist * update fast READMEs * fix check docs
This commit is contained in:
parent
19c6e54298
commit
9a533180a0
|
@ -55,12 +55,7 @@ jobs:
|
|||
- name: Check documentation (fabric)
|
||||
id: documentation-fabric
|
||||
run: |
|
||||
python3 tools/check_documentation.py examples modules
|
||||
|
||||
- name: Check documentation (fast)
|
||||
id: documentation-fast
|
||||
run: |
|
||||
python3 tools/check_documentation.py --files --show-extra fast
|
||||
python3 tools/check_documentation.py examples modules fast
|
||||
|
||||
# markdown-link-check:
|
||||
# runs-on: ubuntu-latest
|
||||
|
|
|
@ -264,8 +264,7 @@ If a different convention is needed, identify names via search/grep (e.g. with `
|
|||
|
||||
Names used in internal references (e.g. `module.foo-prod.id`) are only used by Terraform and do not influence resource naming, so they are best left untouched to avoid having to debug complex errors.
|
||||
|
||||
|
||||
|
||||
<!-- TFDOC OPTS files:1 show_extra:1 -->
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Files
|
||||
|
@ -304,9 +303,3 @@ Names used in internal references (e.g. `module.foo-prod.id`) are only used by T
|
|||
| tfvars | Terraform variable files for the following stages. | ✓ | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -138,10 +138,7 @@ IAM roles can be easily edited in the relevant `branch-xxx.tf` file, following t
|
|||
|
||||
Due to its simplicity, this stage lends itself easily to customizations: adding a new top-level branch (e.g. for shared GKE clusters) is as easy as cloning one of the `branch-xxx.tf` files, and changing names.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- TFDOC OPTS files:1 show_extra:1 -->
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Files
|
||||
|
@ -185,12 +182,3 @@ Due to its simplicity, this stage lends itself easily to customizations: adding
|
|||
| tfvars | Terraform variable files for the following stages. | ✓ | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -282,6 +282,7 @@ VPN configuration also controls BGP advertisements, which requires the following
|
|||
|
||||
DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS resolution to Landing through DNS peering, and optionally define a private zone (e.g. `staging.gcp.example.com`) which the landing peers to. To configure DNS for a new environment, copy all the `prod-*` modules in the `dns.tf` file to `staging-*`, and update their content accordingly. Don't forget to add a peering zone from Landing to the newly created environment private zone.
|
||||
|
||||
<!-- TFDOC OPTS files:1 show_extra:1 -->
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Files
|
||||
|
|
|
@ -265,9 +265,7 @@ Some references that might be useful in setting up this stage:
|
|||
|
||||
- [VPC SC CSCC requirements](https://cloud.google.com/security-command-center/docs/troubleshooting).
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- TFDOC OPTS files:1 show_extra:1 -->
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Files
|
||||
|
@ -309,15 +307,3 @@ Some references that might be useful in setting up this stage:
|
|||
| stage_perimeter_projects | Security project numbers. They can be added to perimeter resources. | | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -94,17 +94,16 @@ terraform init
|
|||
terraform apply
|
||||
```
|
||||
|
||||
|
||||
|
||||
<!-- TFDOC OPTS files:1 show_extra:1 -->
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Files
|
||||
|
||||
| name | description | modules | resources |
|
||||
|---|---|---|---|
|
||||
| [main.tf](./main.tf) | Project factory. | <code>project-factory</code> | |
|
||||
| [outputs.tf](./outputs.tf) | Module outputs. | | |
|
||||
| [variables.tf](./variables.tf) | Module variables. | | |
|
||||
| name | description | modules |
|
||||
|---|---|---|
|
||||
| [main.tf](./main.tf) | Project factory. | <code>project-factory</code> |
|
||||
| [outputs.tf](./outputs.tf) | Module outputs. | |
|
||||
| [variables.tf](./variables.tf) | Module variables. | |
|
||||
|
||||
## Variables
|
||||
|
||||
|
@ -124,8 +123,3 @@ terraform apply
|
|||
| projects | Created projects and service accounts. | | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -150,6 +150,7 @@ module "project-host" {
|
|||
```
|
||||
|
||||
## Cloud KMS encryption keys
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./modules/project"
|
||||
|
@ -173,9 +174,24 @@ module "project" {
|
|||
# tftest:modules=1:resources=7
|
||||
```
|
||||
|
||||
|
||||
<!-- TFDOC OPTS files:1 -->
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Files
|
||||
|
||||
| name | description | resources |
|
||||
|---|---|---|
|
||||
| [iam.tf](./iam.tf) | Generic and OSLogin-specific IAM bindings and roles. | <code>google_project_iam_binding</code> · <code>google_project_iam_custom_role</code> · <code>google_project_iam_member</code> |
|
||||
| [logging.tf](./logging.tf) | Log sinks and supporting resources. | <code>google_bigquery_dataset_iam_member</code> · <code>google_logging_project_exclusion</code> · <code>google_logging_project_sink</code> · <code>google_project_iam_member</code> · <code>google_pubsub_topic_iam_member</code> · <code>google_storage_bucket_iam_member</code> |
|
||||
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_compute_project_metadata_item</code> · <code>google_essential_contacts_contact</code> · <code>google_monitoring_monitored_project</code> · <code>google_project</code> · <code>google_project_service</code> · <code>google_resource_manager_lien</code> |
|
||||
| [organization-policies.tf](./organization-policies.tf) | Project-level organization policies. | <code>google_project_organization_policy</code> |
|
||||
| [outputs.tf](./outputs.tf) | Module outputs. | |
|
||||
| [service_accounts.tf](./service_accounts.tf) | Service identities and supporting resources. | <code>google_kms_crypto_key_iam_member</code> · <code>google_project_service_identity</code> |
|
||||
| [shared_vpc.tf](./shared_vpc.tf) | Shared VPC project-level configuration. | <code>google_compute_shared_vpc_host_project</code> · <code>google_compute_shared_vpc_service_project</code> |
|
||||
| [variables.tf](./variables.tf) | Module variables. | |
|
||||
| [versions.tf](./versions.tf) | Version pins. | |
|
||||
| [vpc-sc.tf](./vpc-sc.tf) | VPC-SC project-level perimeter configuration. | <code>google_access_context_manager_service_perimeter_resource</code> |
|
||||
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|
@ -224,5 +240,3 @@ module "project" {
|
|||
| sink_writer_identities | Writer identities created for each sink. | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Generic and OSLogin-specific IAM bindings and roles.
|
||||
|
||||
# IAM notes:
|
||||
# - external users need to have accepted the invitation email to join
|
||||
# - oslogin roles also require role to list instances
|
||||
# - additive (non-authoritative) roles might fail due to dynamic values
|
||||
|
||||
locals {
|
||||
_group_iam_roles = distinct(flatten(values(var.group_iam)))
|
||||
_group_iam = {
|
||||
for r in local._group_iam_roles : r => [
|
||||
for k, v in var.group_iam : "group:${k}" if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
_iam_additive_pairs = flatten([
|
||||
for role, members in var.iam_additive : [
|
||||
for member in members : { role = role, member = member }
|
||||
]
|
||||
])
|
||||
_iam_additive_member_pairs = flatten([
|
||||
for member, roles in var.iam_additive_members : [
|
||||
for role in roles : { role = role, member = member }
|
||||
]
|
||||
])
|
||||
iam = {
|
||||
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
|
||||
role => concat(
|
||||
try(var.iam[role], []),
|
||||
try(local._group_iam[role], [])
|
||||
)
|
||||
}
|
||||
iam_additive = {
|
||||
for pair in concat(local._iam_additive_pairs, local._iam_additive_member_pairs) :
|
||||
"${pair.role}-${pair.member}" => pair
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_project_iam_custom_role" "roles" {
|
||||
for_each = var.custom_roles
|
||||
project = local.project.project_id
|
||||
role_id = each.key
|
||||
title = "Custom role ${each.key}"
|
||||
description = "Terraform-managed"
|
||||
permissions = each.value
|
||||
}
|
||||
|
||||
resource "google_project_iam_binding" "authoritative" {
|
||||
for_each = local.iam
|
||||
project = local.project.project_id
|
||||
role = each.key
|
||||
members = each.value
|
||||
depends_on = [
|
||||
google_project_service.project_services,
|
||||
google_project_iam_custom_role.roles
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "additive" {
|
||||
for_each = (
|
||||
length(var.iam_additive) + length(var.iam_additive_members) > 0
|
||||
? local.iam_additive
|
||||
: {}
|
||||
)
|
||||
project = local.project.project_id
|
||||
role = each.value.role
|
||||
member = each.value.member
|
||||
depends_on = [
|
||||
google_project_service.project_services,
|
||||
google_project_iam_custom_role.roles
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "oslogin_iam_serviceaccountuser" {
|
||||
for_each = var.oslogin ? toset(distinct(concat(var.oslogin_admins, var.oslogin_users))) : toset([])
|
||||
project = local.project.project_id
|
||||
role = "roles/iam.serviceAccountUser"
|
||||
member = each.value
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "oslogin_compute_viewer" {
|
||||
for_each = var.oslogin ? toset(distinct(concat(var.oslogin_admins, var.oslogin_users))) : toset([])
|
||||
project = local.project.project_id
|
||||
role = "roles/compute.viewer"
|
||||
member = each.value
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "oslogin_admins" {
|
||||
for_each = var.oslogin ? toset(var.oslogin_admins) : toset([])
|
||||
project = local.project.project_id
|
||||
role = "roles/compute.osAdminLogin"
|
||||
member = each.value
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "oslogin_users" {
|
||||
for_each = var.oslogin ? toset(var.oslogin_users) : toset([])
|
||||
project = local.project.project_id
|
||||
role = "roles/compute.osLogin"
|
||||
member = each.value
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Log sinks and supporting resources.
|
||||
|
||||
locals {
|
||||
logging_sinks = coalesce(var.logging_sinks, {})
|
||||
sink_bindings = {
|
||||
for type in ["gcs", "bigquery", "pubsub", "logging"] :
|
||||
type => {
|
||||
for name, sink in local.logging_sinks :
|
||||
name => sink if sink.iam && sink.type == type
|
||||
}
|
||||
}
|
||||
sink_type_destination = {
|
||||
gcs = "storage.googleapis.com"
|
||||
bigquery = "bigquery.googleapis.com"
|
||||
pubsub = "pubsub.googleapis.com"
|
||||
logging = "logging.googleapis.com"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_logging_project_sink" "sink" {
|
||||
for_each = local.logging_sinks
|
||||
name = each.key
|
||||
#description = "${each.key} (Terraform-managed)"
|
||||
project = local.project.project_id
|
||||
destination = "${local.sink_type_destination[each.value.type]}/${each.value.destination}"
|
||||
filter = each.value.filter
|
||||
unique_writer_identity = each.value.unique_writer
|
||||
|
||||
dynamic "exclusions" {
|
||||
for_each = each.value.exclusions
|
||||
iterator = exclusion
|
||||
content {
|
||||
name = exclusion.key
|
||||
filter = exclusion.value
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_project_iam_binding.authoritative,
|
||||
google_project_iam_member.additive
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_storage_bucket_iam_member" "gcs-sinks-binding" {
|
||||
for_each = local.sink_bindings["gcs"]
|
||||
bucket = each.value.destination
|
||||
role = "roles/storage.objectCreator"
|
||||
member = google_logging_project_sink.sink[each.key].writer_identity
|
||||
}
|
||||
|
||||
resource "google_bigquery_dataset_iam_member" "bq-sinks-binding" {
|
||||
for_each = local.sink_bindings["bigquery"]
|
||||
project = split("/", each.value.destination)[1]
|
||||
dataset_id = split("/", each.value.destination)[3]
|
||||
role = "roles/bigquery.dataEditor"
|
||||
member = google_logging_project_sink.sink[each.key].writer_identity
|
||||
}
|
||||
|
||||
resource "google_pubsub_topic_iam_member" "pubsub-sinks-binding" {
|
||||
for_each = local.sink_bindings["pubsub"]
|
||||
project = split("/", each.value.destination)[1]
|
||||
topic = split("/", each.value.destination)[3]
|
||||
role = "roles/pubsub.publisher"
|
||||
member = google_logging_project_sink.sink[each.key].writer_identity
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "bucket-sinks-binding" {
|
||||
for_each = local.sink_bindings["logging"]
|
||||
project = split("/", each.value.destination)[1]
|
||||
role = "roles/logging.bucketWriter"
|
||||
member = google_logging_project_sink.sink[each.key].writer_identity
|
||||
# TODO(jccb): use a condition to limit writer-identity only to this
|
||||
# bucket
|
||||
}
|
||||
|
||||
resource "google_logging_project_exclusion" "logging-exclusion" {
|
||||
for_each = coalesce(var.logging_exclusions, {})
|
||||
name = each.key
|
||||
project = local.project.project_id
|
||||
description = "${each.key} (Terraform-managed)"
|
||||
filter = each.value
|
||||
}
|
|
@ -15,44 +15,9 @@
|
|||
*/
|
||||
|
||||
locals {
|
||||
# service dependendencies for encryption keys
|
||||
_service_encryption_key_dependent_services = {
|
||||
"composer" : [
|
||||
"composer",
|
||||
"artifactregistry", "container-engine", "compute", "pubsub", "storage"
|
||||
]
|
||||
"dataflow" : ["dataflow", "compute"]
|
||||
}
|
||||
descriptive_name = (
|
||||
var.descriptive_name != null ? var.descriptive_name : "${local.prefix}${var.name}"
|
||||
)
|
||||
group_iam_roles = distinct(flatten(values(var.group_iam)))
|
||||
group_iam = {
|
||||
for r in local.group_iam_roles : r => [
|
||||
for k, v in var.group_iam : "group:${k}" if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
iam = {
|
||||
for role in distinct(concat(keys(var.iam), keys(local.group_iam))) :
|
||||
role => concat(
|
||||
try(var.iam[role], []),
|
||||
try(local.group_iam[role], [])
|
||||
)
|
||||
}
|
||||
iam_additive_pairs = flatten([
|
||||
for role, members in var.iam_additive : [
|
||||
for member in members : { role = role, member = member }
|
||||
]
|
||||
])
|
||||
iam_additive_member_pairs = flatten([
|
||||
for member, roles in var.iam_additive_members : [
|
||||
for role in roles : { role = role, member = member }
|
||||
]
|
||||
])
|
||||
iam_additive = {
|
||||
for pair in concat(local.iam_additive_pairs, local.iam_additive_member_pairs) :
|
||||
"${pair.role}-${pair.member}" => pair
|
||||
}
|
||||
parent_type = var.parent == null ? null : split("/", var.parent)[0]
|
||||
parent_id = var.parent == null ? null : split("/", var.parent)[1]
|
||||
prefix = var.prefix == null ? "" : "${var.prefix}-"
|
||||
|
@ -69,31 +34,6 @@ locals {
|
|||
name = try(data.google_project.project.0.name, null)
|
||||
}
|
||||
)
|
||||
logging_sinks = coalesce(var.logging_sinks, {})
|
||||
sink_type_destination = {
|
||||
gcs = "storage.googleapis.com"
|
||||
bigquery = "bigquery.googleapis.com"
|
||||
pubsub = "pubsub.googleapis.com"
|
||||
logging = "logging.googleapis.com"
|
||||
}
|
||||
sink_bindings = {
|
||||
for type in ["gcs", "bigquery", "pubsub", "logging"] :
|
||||
type => {
|
||||
for name, sink in local.logging_sinks :
|
||||
name => sink
|
||||
if sink.iam && sink.type == type
|
||||
}
|
||||
}
|
||||
service_encryption_key_ids = distinct(flatten([
|
||||
for s in keys(var.service_encryption_key_ids) : [
|
||||
for ss in try(local._service_encryption_key_dependent_services[s], [s]) : [
|
||||
for key in var.service_encryption_key_ids[s] : {
|
||||
service = ss
|
||||
key = key
|
||||
} if key != null
|
||||
]
|
||||
]
|
||||
]))
|
||||
}
|
||||
|
||||
data "google_project" "project" {
|
||||
|
@ -113,13 +53,12 @@ resource "google_project" "project" {
|
|||
skip_delete = var.skip_delete
|
||||
}
|
||||
|
||||
resource "google_project_iam_custom_role" "roles" {
|
||||
for_each = var.custom_roles
|
||||
project = local.project.project_id
|
||||
role_id = each.key
|
||||
title = "Custom role ${each.key}"
|
||||
description = "Terraform-managed"
|
||||
permissions = each.value
|
||||
resource "google_project_service" "project_services" {
|
||||
for_each = toset(var.services)
|
||||
project = local.project.project_id
|
||||
service = each.value
|
||||
disable_on_destroy = var.service_config.disable_on_destroy
|
||||
disable_dependent_services = var.service_config.disable_dependent_services
|
||||
}
|
||||
|
||||
resource "google_compute_project_metadata_item" "oslogin_meta" {
|
||||
|
@ -139,232 +78,6 @@ resource "google_resource_manager_lien" "lien" {
|
|||
reason = var.lien_reason
|
||||
}
|
||||
|
||||
resource "google_project_service" "project_services" {
|
||||
for_each = toset(var.services)
|
||||
project = local.project.project_id
|
||||
service = each.value
|
||||
disable_on_destroy = var.service_config.disable_on_destroy
|
||||
disable_dependent_services = var.service_config.disable_dependent_services
|
||||
}
|
||||
|
||||
# IAM notes:
|
||||
# - external users need to have accepted the invitation email to join
|
||||
# - oslogin roles also require role to list instances
|
||||
# - additive (non-authoritative) roles might fail due to dynamic values
|
||||
|
||||
resource "google_project_iam_binding" "authoritative" {
|
||||
for_each = local.iam
|
||||
project = local.project.project_id
|
||||
role = each.key
|
||||
members = each.value
|
||||
depends_on = [
|
||||
google_project_service.project_services,
|
||||
google_project_iam_custom_role.roles
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "additive" {
|
||||
for_each = (
|
||||
length(var.iam_additive) + length(var.iam_additive_members) > 0
|
||||
? local.iam_additive
|
||||
: {}
|
||||
)
|
||||
project = local.project.project_id
|
||||
role = each.value.role
|
||||
member = each.value.member
|
||||
depends_on = [
|
||||
google_project_service.project_services,
|
||||
google_project_iam_custom_role.roles
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "oslogin_iam_serviceaccountuser" {
|
||||
for_each = var.oslogin ? toset(distinct(concat(var.oslogin_admins, var.oslogin_users))) : toset([])
|
||||
project = local.project.project_id
|
||||
role = "roles/iam.serviceAccountUser"
|
||||
member = each.value
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "oslogin_compute_viewer" {
|
||||
for_each = var.oslogin ? toset(distinct(concat(var.oslogin_admins, var.oslogin_users))) : toset([])
|
||||
project = local.project.project_id
|
||||
role = "roles/compute.viewer"
|
||||
member = each.value
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "oslogin_admins" {
|
||||
for_each = var.oslogin ? toset(var.oslogin_admins) : toset([])
|
||||
project = local.project.project_id
|
||||
role = "roles/compute.osAdminLogin"
|
||||
member = each.value
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "oslogin_users" {
|
||||
for_each = var.oslogin ? toset(var.oslogin_users) : toset([])
|
||||
project = local.project.project_id
|
||||
role = "roles/compute.osLogin"
|
||||
member = each.value
|
||||
}
|
||||
|
||||
resource "google_project_organization_policy" "boolean" {
|
||||
for_each = var.policy_boolean
|
||||
project = local.project.project_id
|
||||
constraint = each.key
|
||||
|
||||
dynamic "boolean_policy" {
|
||||
for_each = each.value == null ? [] : [each.value]
|
||||
iterator = policy
|
||||
content {
|
||||
enforced = policy.value
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "restore_policy" {
|
||||
for_each = each.value == null ? [""] : []
|
||||
content {
|
||||
default = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_project_organization_policy" "list" {
|
||||
for_each = var.policy_list
|
||||
project = local.project.project_id
|
||||
constraint = each.key
|
||||
|
||||
dynamic "list_policy" {
|
||||
for_each = each.value.status == null ? [] : [each.value]
|
||||
iterator = policy
|
||||
content {
|
||||
inherit_from_parent = policy.value.inherit_from_parent
|
||||
suggested_value = policy.value.suggested_value
|
||||
dynamic "allow" {
|
||||
for_each = policy.value.status ? [""] : []
|
||||
content {
|
||||
values = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? policy.value.values
|
||||
: null
|
||||
)
|
||||
all = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? null
|
||||
: true
|
||||
)
|
||||
}
|
||||
}
|
||||
dynamic "deny" {
|
||||
for_each = policy.value.status ? [] : [""]
|
||||
content {
|
||||
values = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? policy.value.values
|
||||
: null
|
||||
)
|
||||
all = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? null
|
||||
: true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "restore_policy" {
|
||||
for_each = each.value.status == null ? [true] : []
|
||||
content {
|
||||
default = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_shared_vpc_host_project" "shared_vpc_host" {
|
||||
count = try(var.shared_vpc_host_config.enabled, false) ? 1 : 0
|
||||
project = local.project.project_id
|
||||
}
|
||||
|
||||
resource "google_compute_shared_vpc_service_project" "service_projects" {
|
||||
for_each = (
|
||||
try(var.shared_vpc_host_config.enabled, false)
|
||||
? toset(coalesce(var.shared_vpc_host_config.service_projects, []))
|
||||
: toset([])
|
||||
)
|
||||
host_project = local.project.project_id
|
||||
service_project = each.value
|
||||
depends_on = [google_compute_shared_vpc_host_project.shared_vpc_host]
|
||||
}
|
||||
|
||||
resource "google_compute_shared_vpc_service_project" "shared_vpc_service" {
|
||||
count = try(var.shared_vpc_service_config.attach, false) ? 1 : 0
|
||||
host_project = var.shared_vpc_service_config.host_project
|
||||
service_project = local.project.project_id
|
||||
}
|
||||
|
||||
resource "google_logging_project_sink" "sink" {
|
||||
for_each = local.logging_sinks
|
||||
name = each.key
|
||||
#description = "${each.key} (Terraform-managed)"
|
||||
project = local.project.project_id
|
||||
destination = "${local.sink_type_destination[each.value.type]}/${each.value.destination}"
|
||||
filter = each.value.filter
|
||||
unique_writer_identity = each.value.unique_writer
|
||||
|
||||
dynamic "exclusions" {
|
||||
for_each = each.value.exclusions
|
||||
iterator = exclusion
|
||||
content {
|
||||
name = exclusion.key
|
||||
filter = exclusion.value
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_project_iam_binding.authoritative,
|
||||
google_project_iam_member.additive
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_storage_bucket_iam_member" "gcs-sinks-binding" {
|
||||
for_each = local.sink_bindings["gcs"]
|
||||
bucket = each.value.destination
|
||||
role = "roles/storage.objectCreator"
|
||||
member = google_logging_project_sink.sink[each.key].writer_identity
|
||||
}
|
||||
|
||||
resource "google_bigquery_dataset_iam_member" "bq-sinks-binding" {
|
||||
for_each = local.sink_bindings["bigquery"]
|
||||
project = split("/", each.value.destination)[1]
|
||||
dataset_id = split("/", each.value.destination)[3]
|
||||
role = "roles/bigquery.dataEditor"
|
||||
member = google_logging_project_sink.sink[each.key].writer_identity
|
||||
}
|
||||
|
||||
resource "google_pubsub_topic_iam_member" "pubsub-sinks-binding" {
|
||||
for_each = local.sink_bindings["pubsub"]
|
||||
project = split("/", each.value.destination)[1]
|
||||
topic = split("/", each.value.destination)[3]
|
||||
role = "roles/pubsub.publisher"
|
||||
member = google_logging_project_sink.sink[each.key].writer_identity
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "bucket-sinks-binding" {
|
||||
for_each = local.sink_bindings["logging"]
|
||||
project = split("/", each.value.destination)[1]
|
||||
role = "roles/logging.bucketWriter"
|
||||
member = google_logging_project_sink.sink[each.key].writer_identity
|
||||
# TODO(jccb): use a condition to limit writer-identity only to this
|
||||
# bucket
|
||||
}
|
||||
|
||||
resource "google_logging_project_exclusion" "logging-exclusion" {
|
||||
for_each = coalesce(var.logging_exclusions, {})
|
||||
name = each.key
|
||||
project = local.project.project_id
|
||||
description = "${each.key} (Terraform-managed)"
|
||||
filter = each.value
|
||||
}
|
||||
|
||||
resource "google_essential_contacts_contact" "contact" {
|
||||
provider = google-beta
|
||||
for_each = var.contacts
|
||||
|
@ -374,43 +87,6 @@ resource "google_essential_contacts_contact" "contact" {
|
|||
notification_category_subscriptions = each.value
|
||||
}
|
||||
|
||||
resource "google_access_context_manager_service_perimeter_resource" "service-perimeter-resource-standard" {
|
||||
count = var.service_perimeter_standard != null ? 1 : 0
|
||||
|
||||
# If used, remember to uncomment 'lifecycle' block in the
|
||||
# modules/vpc-sc/google_access_context_manager_service_perimeter resource.
|
||||
perimeter_name = var.service_perimeter_standard
|
||||
resource = "projects/${local.project.number}"
|
||||
}
|
||||
|
||||
resource "google_access_context_manager_service_perimeter_resource" "service-perimeter-resource-bridges" {
|
||||
for_each = toset(var.service_perimeter_bridges != null ? var.service_perimeter_bridges : [])
|
||||
|
||||
# If used, remember to uncomment 'lifecycle' block in the
|
||||
# modules/vpc-sc/google_access_context_manager_service_perimeter resource.
|
||||
perimeter_name = each.value
|
||||
resource = "projects/${local.project.number}"
|
||||
}
|
||||
|
||||
resource "google_kms_crypto_key_iam_member" "crypto_key" {
|
||||
for_each = {
|
||||
for service_key in local.service_encryption_key_ids :
|
||||
"${service_key.service}.${service_key.key}" => service_key
|
||||
if service_key != service_key.key
|
||||
}
|
||||
crypto_key_id = each.value.key
|
||||
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
|
||||
member = "serviceAccount:${local.service_accounts_robots[each.value.service]}"
|
||||
depends_on = [
|
||||
google_project.project,
|
||||
google_project_service.project_services,
|
||||
google_project_service_identity.jit_si,
|
||||
data.google_bigquery_default_service_account.bq_sa,
|
||||
data.google_project.project,
|
||||
data.google_storage_project_service_account.gcs_sa,
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_monitoring_monitored_project" "primary" {
|
||||
provider = google-beta
|
||||
for_each = toset(coalesce(var.metric_scopes, []))
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Project-level organization policies.
|
||||
|
||||
resource "google_project_organization_policy" "boolean" {
|
||||
for_each = var.policy_boolean
|
||||
project = local.project.project_id
|
||||
constraint = each.key
|
||||
|
||||
dynamic "boolean_policy" {
|
||||
for_each = each.value == null ? [] : [each.value]
|
||||
iterator = policy
|
||||
content {
|
||||
enforced = policy.value
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "restore_policy" {
|
||||
for_each = each.value == null ? [""] : []
|
||||
content {
|
||||
default = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_project_organization_policy" "list" {
|
||||
for_each = var.policy_list
|
||||
project = local.project.project_id
|
||||
constraint = each.key
|
||||
|
||||
dynamic "list_policy" {
|
||||
for_each = each.value.status == null ? [] : [each.value]
|
||||
iterator = policy
|
||||
content {
|
||||
inherit_from_parent = policy.value.inherit_from_parent
|
||||
suggested_value = policy.value.suggested_value
|
||||
dynamic "allow" {
|
||||
for_each = policy.value.status ? [""] : []
|
||||
content {
|
||||
values = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? policy.value.values
|
||||
: null
|
||||
)
|
||||
all = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? null
|
||||
: true
|
||||
)
|
||||
}
|
||||
}
|
||||
dynamic "deny" {
|
||||
for_each = policy.value.status ? [] : [""]
|
||||
content {
|
||||
values = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? policy.value.values
|
||||
: null
|
||||
)
|
||||
all = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? null
|
||||
: true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "restore_policy" {
|
||||
for_each = each.value.status == null ? [true] : []
|
||||
content {
|
||||
default = true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ output "name" {
|
|||
google_project_organization_policy.list,
|
||||
google_project_service.project_services,
|
||||
google_compute_shared_vpc_service_project.service_projects,
|
||||
google_kms_crypto_key_iam_member.crypto_key
|
||||
google_kms_crypto_key_iam_member.service_identity_cmek
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ output "number" {
|
|||
google_project_organization_policy.list,
|
||||
google_project_service.project_services,
|
||||
google_compute_shared_vpc_service_project.service_projects,
|
||||
google_kms_crypto_key_iam_member.crypto_key
|
||||
google_kms_crypto_key_iam_member.service_identity_cmek
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ output "project_id" {
|
|||
google_project_organization_policy.list,
|
||||
google_project_service.project_services,
|
||||
google_compute_shared_vpc_service_project.service_projects,
|
||||
google_kms_crypto_key_iam_member.crypto_key
|
||||
google_kms_crypto_key_iam_member.service_identity_cmek
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ output "service_accounts" {
|
|||
}
|
||||
depends_on = [
|
||||
google_project_service.project_services,
|
||||
google_kms_crypto_key_iam_member.crypto_key,
|
||||
google_kms_crypto_key_iam_member.service_identity_cmek,
|
||||
google_project_service_identity.jit_si,
|
||||
data.google_bigquery_default_service_account.bq_sa,
|
||||
data.google_storage_project_service_account.gcs_sa
|
||||
|
|
|
@ -14,38 +14,59 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Service identities and supporting resources.
|
||||
|
||||
locals {
|
||||
service_account_cloud_services = "${local.project.number}@cloudservices.gserviceaccount.com"
|
||||
_service_accounts_cmek_service_dependencies = {
|
||||
"composer" : [
|
||||
"composer",
|
||||
"artifactregistry", "container-engine", "compute", "pubsub", "storage"
|
||||
]
|
||||
"dataflow" : ["dataflow", "compute"]
|
||||
}
|
||||
_service_accounts_robot_services = {
|
||||
artifactregistry = "service-%s@gcp-sa-artifactregistry"
|
||||
bq = "bq-%s@bigquery-encryption"
|
||||
cloudasset = "service-%s@gcp-sa-cloudasset"
|
||||
cloudbuild = "service-%s@gcp-sa-cloudbuild"
|
||||
composer = "service-%s@cloudcomposer-accounts"
|
||||
compute = "service-%s@compute-system"
|
||||
container-engine = "service-%s@container-engine-robot"
|
||||
containerregistry = "service-%s@containerregistry"
|
||||
dataflow = "service-%s@dataflow-service-producer-prod"
|
||||
dataproc = "service-%s@dataproc-accounts"
|
||||
gae-flex = "service-%s@gae-api-prod"
|
||||
gcf = "service-%s@gcf-admin-robot"
|
||||
pubsub = "service-%s@gcp-sa-pubsub"
|
||||
secretmanager = "service-%s@gcp-sa-secretmanager"
|
||||
storage = "service-%s@gs-project-accounts"
|
||||
}
|
||||
service_accounts_default = {
|
||||
compute = "${local.project.number}-compute@developer.gserviceaccount.com"
|
||||
gae = "${local.project.project_id}@appspot.gserviceaccount.com"
|
||||
}
|
||||
service_accounts_robot_services = {
|
||||
artifactregistry = "gcp-sa-artifactregistry"
|
||||
bq = "bigquery-encryption"
|
||||
cloudasset = "gcp-sa-cloudasset"
|
||||
cloudbuild = "gcp-sa-cloudbuild"
|
||||
composer = "cloudcomposer-accounts"
|
||||
compute = "compute-system"
|
||||
container-engine = "container-engine-robot"
|
||||
containerregistry = "containerregistry"
|
||||
dataflow = "dataflow-service-producer-prod"
|
||||
dataproc = "dataproc-accounts"
|
||||
gae-flex = "gae-api-prod"
|
||||
gcf = "gcf-admin-robot"
|
||||
pubsub = "gcp-sa-pubsub"
|
||||
secretmanager = "gcp-sa-secretmanager"
|
||||
storage = "gs-project-accounts"
|
||||
}
|
||||
service_account_cloud_services = (
|
||||
"${local.project.number}@cloudservices.gserviceaccount.com"
|
||||
)
|
||||
service_accounts_robots = {
|
||||
for service, name in local.service_accounts_robot_services :
|
||||
service => "${service == "bq" ? "bq" : "service"}-${local.project.number}@${name}.iam.gserviceaccount.com"
|
||||
for k, v in local._service_accounts_robot_services :
|
||||
k => "${format(v, local.project.number)}.iam.gserviceaccount.com"
|
||||
}
|
||||
jit_services = [
|
||||
service_accounts_jit_services = [
|
||||
"secretmanager.googleapis.com",
|
||||
"pubsub.googleapis.com",
|
||||
"cloudasset.googleapis.com"
|
||||
]
|
||||
service_accounts_cmek_service_keys = distinct(flatten([
|
||||
for s in keys(var.service_encryption_key_ids) : [
|
||||
for ss in try(local._service_accounts_cmek_service_dependencies[s], [s]) : [
|
||||
for key in var.service_encryption_key_ids[s] : {
|
||||
service = ss
|
||||
key = key
|
||||
} if key != null
|
||||
]
|
||||
]
|
||||
]))
|
||||
}
|
||||
|
||||
data "google_storage_project_service_account" "gcs_sa" {
|
||||
|
@ -62,9 +83,28 @@ data "google_bigquery_default_service_account" "bq_sa" {
|
|||
|
||||
# Secret Manager SA created just in time, we need to trigger the creation.
|
||||
resource "google_project_service_identity" "jit_si" {
|
||||
for_each = setintersection(var.services, local.jit_services)
|
||||
for_each = setintersection(var.services, local.service_accounts_jit_services)
|
||||
provider = google-beta
|
||||
project = local.project.project_id
|
||||
service = each.value
|
||||
depends_on = [google_project_service.project_services]
|
||||
}
|
||||
|
||||
resource "google_kms_crypto_key_iam_member" "service_identity_cmek" {
|
||||
for_each = {
|
||||
for service_key in local.service_accounts_cmek_service_keys :
|
||||
"${service_key.service}.${service_key.key}" => service_key
|
||||
if service_key != service_key.key
|
||||
}
|
||||
crypto_key_id = each.value.key
|
||||
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
|
||||
member = "serviceAccount:${local.service_accounts_robots[each.value.service]}"
|
||||
depends_on = [
|
||||
google_project.project,
|
||||
google_project_service.project_services,
|
||||
google_project_service_identity.jit_si,
|
||||
data.google_bigquery_default_service_account.bq_sa,
|
||||
data.google_project.project,
|
||||
data.google_storage_project_service_account.gcs_sa,
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Shared VPC project-level configuration.
|
||||
|
||||
resource "google_compute_shared_vpc_host_project" "shared_vpc_host" {
|
||||
count = try(var.shared_vpc_host_config.enabled, false) ? 1 : 0
|
||||
project = local.project.project_id
|
||||
}
|
||||
|
||||
resource "google_compute_shared_vpc_service_project" "service_projects" {
|
||||
for_each = (
|
||||
try(var.shared_vpc_host_config.enabled, false)
|
||||
? toset(coalesce(var.shared_vpc_host_config.service_projects, []))
|
||||
: toset([])
|
||||
)
|
||||
host_project = local.project.project_id
|
||||
service_project = each.value
|
||||
depends_on = [google_compute_shared_vpc_host_project.shared_vpc_host]
|
||||
}
|
||||
|
||||
resource "google_compute_shared_vpc_service_project" "shared_vpc_service" {
|
||||
count = try(var.shared_vpc_service_config.attach, false) ? 1 : 0
|
||||
host_project = var.shared_vpc_service_config.host_project
|
||||
service_project = local.project.project_id
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description VPC-SC project-level perimeter configuration.
|
||||
|
||||
moved {
|
||||
from = google_access_context_manager_service_perimeter_resource.service-perimeter-resource-standard
|
||||
to = google_access_context_manager_service_perimeter_resource.standard
|
||||
}
|
||||
|
||||
resource "google_access_context_manager_service_perimeter_resource" "standard" {
|
||||
count = var.service_perimeter_standard != null ? 1 : 0
|
||||
# this needs an additional lifecycle block in the vpc module on the
|
||||
# google_access_context_manager_service_perimeter resource
|
||||
perimeter_name = var.service_perimeter_standard
|
||||
resource = "projects/${local.project.number}"
|
||||
}
|
||||
|
||||
moved {
|
||||
from = google_access_context_manager_service_perimeter_resource.service-perimeter-resource-bridges
|
||||
to = google_access_context_manager_service_perimeter_resource.bridge
|
||||
}
|
||||
|
||||
resource "google_access_context_manager_service_perimeter_resource" "bridge" {
|
||||
for_each = toset(
|
||||
var.service_perimeter_bridges != null ? var.service_perimeter_bridges : []
|
||||
)
|
||||
# this needs an additional lifecycle block in the vpc module on the
|
||||
# google_access_context_manager_service_perimeter resource
|
||||
perimeter_name = each.value
|
||||
resource = "projects/${local.project.number}"
|
||||
}
|
|
@ -39,7 +39,7 @@ State = enum.Enum('State', 'OK FAIL SKIP')
|
|||
def _check_dir(dir_name, files=False, show_extra=False):
|
||||
'Invoke tfdoc on folder, using the relevant options.'
|
||||
dir_path = BASEDIR / dir_name
|
||||
for readme_path in dir_path.glob('**/README.md'):
|
||||
for readme_path in sorted(dir_path.glob('**/README.md')):
|
||||
if '.terraform' in str(readme_path):
|
||||
continue
|
||||
diff = None
|
||||
|
@ -50,8 +50,9 @@ def _check_dir(dir_name, files=False, show_extra=False):
|
|||
state = State.SKIP
|
||||
else:
|
||||
try:
|
||||
new_doc = tfdoc.create_doc(
|
||||
readme_path.parent, files=files, show_extra=show_extra)
|
||||
new_doc = tfdoc.create_doc(readme_path.parent, files=files,
|
||||
show_extra=show_extra, exclude_files=None,
|
||||
readme=readme)
|
||||
except SystemExit:
|
||||
state = state.SKIP
|
||||
else:
|
||||
|
@ -74,6 +75,7 @@ def _check_dir(dir_name, files=False, show_extra=False):
|
|||
@ click.option('--show-extra/--no-show-extra', default=False)
|
||||
def main(dirs, files=False, show_diffs=False, show_extra=False):
|
||||
'Cycle through modules and ensure READMEs are up-to-date.'
|
||||
print(f'files: {files}, extra: {show_extra}, diffs: {show_diffs}\n')
|
||||
errors = []
|
||||
state_labels = {State.FAIL: '✗', State.OK: '✓', State.SKIP: '?'}
|
||||
for dir_name in dirs:
|
||||
|
|
|
@ -71,6 +71,7 @@ FILE_RE_RESOURCES = re.compile(
|
|||
HEREDOC_RE = re.compile(r'(?sm)^<<\-?END(\s*.*?)\s*END$')
|
||||
MARK_BEGIN = '<!-- BEGIN TFDOC -->'
|
||||
MARK_END = '<!-- END TFDOC -->'
|
||||
MARK_OPTS_RE = re.compile(r'(?sm)<!-- TFDOC OPTS ((?:[a-z_]+:[0-1]\s*?)+) -->')
|
||||
OUT_ENUM = enum.Enum('O', 'OPEN ATTR ATTR_DATA CLOSE COMMENT TXT SKIP')
|
||||
OUT_RE = re.compile(r'''(?smx)
|
||||
# output open
|
||||
|
@ -237,8 +238,16 @@ def format_doc(outputs, variables, files, show_extra=False):
|
|||
def format_files(items):
|
||||
'Format files table.'
|
||||
items.sort(key=lambda i: i.name)
|
||||
yield '| name | description | modules | resources |'
|
||||
yield '|---|---|---|---|'
|
||||
num_modules = sum(len(i.modules) for i in items)
|
||||
num_resources = sum(len(i.resources) for i in items)
|
||||
yield '| name | description |{}{}'.format(
|
||||
' modules |' if num_modules else '',
|
||||
' resources |' if num_resources else ''
|
||||
)
|
||||
yield '|---|---|{}{}'.format(
|
||||
'---|' if num_modules else '',
|
||||
'---|' if num_resources else ''
|
||||
)
|
||||
for i in items:
|
||||
modules = resources = ''
|
||||
if i.modules:
|
||||
|
@ -247,7 +256,11 @@ def format_files(items):
|
|||
if i.resources:
|
||||
resources = '<code>%s</code>' % '</code> · <code>'.join(
|
||||
sorted(i.resources))
|
||||
yield f'| [{i.name}](./{i.name}) | {i.description} | {modules} | {resources} |'
|
||||
yield '| [{}](./{}) | {} |{}{}'.format(
|
||||
i.name, i.name, i.description,
|
||||
f' {modules} |' if num_modules else '',
|
||||
f' {resources} |' if num_resources else ''
|
||||
)
|
||||
|
||||
|
||||
def format_outputs(items, show_extra=True):
|
||||
|
@ -321,7 +334,28 @@ def get_doc(readme):
|
|||
return {'doc': m.group(1), 'start': m.start(), 'end': m.end()}
|
||||
|
||||
|
||||
def create_doc(module_path, files=False, show_extra=False, exclude_files=None):
|
||||
def get_doc_opts(readme):
|
||||
'Check if README file is setting options via a mark, and return options.'
|
||||
m = MARK_OPTS_RE.search(readme)
|
||||
opts = {}
|
||||
if not m:
|
||||
return opts
|
||||
try:
|
||||
for o in m.group(1).split():
|
||||
k, v = o.split(':')
|
||||
opts[k] = bool(int(v))
|
||||
except (TypeError, ValueError) as e:
|
||||
raise SystemExit(f'incorrect option mark: {e}')
|
||||
return opts
|
||||
|
||||
|
||||
def create_doc(module_path, files=False, show_extra=False, exclude_files=None,
|
||||
readme=None):
|
||||
if readme:
|
||||
# check for overrides in doc
|
||||
opts = get_doc_opts(readme)
|
||||
files = opts.get('files', files)
|
||||
show_extra = opts.get('show_extra', show_extra)
|
||||
try:
|
||||
mod_files = list(parse_files(module_path, exclude_files)) if files else []
|
||||
mod_variables = list(parse_variables(module_path))
|
||||
|
@ -331,13 +365,17 @@ def create_doc(module_path, files=False, show_extra=False, exclude_files=None):
|
|||
return format_doc(mod_outputs, mod_variables, mod_files, show_extra)
|
||||
|
||||
|
||||
def replace_doc(module_path, doc):
|
||||
'Replace document in module\'s README.md file.'
|
||||
readme_path = os.path.join(module_path, 'README.md')
|
||||
def get_readme(readme_path):
|
||||
'Open and return README.md in module.'
|
||||
try:
|
||||
readme = open(readme_path).read()
|
||||
return open(readme_path).read()
|
||||
except (IOError, OSError) as e:
|
||||
raise SystemExit(f'Error opening README {readme_path}: {e}')
|
||||
|
||||
|
||||
def replace_doc(readme_path, doc, readme=None):
|
||||
'Replace document in module\'s README.md file.'
|
||||
readme = readme or get_readme(readme_path)
|
||||
result = get_doc(readme)
|
||||
if not result:
|
||||
raise SystemExit(f'Mark not found in README {readme_path}')
|
||||
|
@ -356,17 +394,19 @@ def replace_doc(module_path, doc):
|
|||
|
||||
|
||||
@ click.command()
|
||||
@ click.argument('module', type=click.Path(exists=True))
|
||||
@ click.argument('module_path', type=click.Path(exists=True))
|
||||
@click.option('--exclude-file', '-x', multiple=True)
|
||||
@ click.option('--files/--no-files', default=False)
|
||||
@ click.option('--replace/--no-replace', default=True)
|
||||
@ click.option('--show-extra/--no-show-extra', default=False)
|
||||
def main(module=None, annotate=False, exclude_file=None, files=False, replace=True,
|
||||
def main(module_path=None, exclude_file=None, files=False, replace=True,
|
||||
show_extra=True):
|
||||
'Program entry point.'
|
||||
doc = create_doc(module, files, show_extra, exclude_file)
|
||||
readme_path = os.path.join(module_path, 'README.md')
|
||||
readme = get_readme(readme_path)
|
||||
doc = create_doc(module_path, files, show_extra, exclude_file, readme)
|
||||
if replace:
|
||||
replace_doc(module, doc)
|
||||
replace_doc(readme_path, doc, readme)
|
||||
else:
|
||||
print(doc)
|
||||
|
||||
|
|
Loading…
Reference in New Issue