Merge pull request #984 from apichick/apigee

Apigee module and blueprint
This commit is contained in:
apichick 2022-11-17 17:20:27 +01:00 committed by GitHub
commit ddd47b0095
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 13614 additions and 749 deletions

5
.gitignore vendored
View File

@ -37,3 +37,8 @@ examples/cloud-operations/adfs/ansible/gssh.sh
examples/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/vars.yaml
examples/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/gssh.sh
blueprints/cloud-operations/network-dashboard/cloud-function.zip
blueprints/cloud-operations/apigee/bundle-export.zip
blueprints/cloud-operations/apigee/bundle-gcs2bq.zip
blueprints/cloud-operations/apigee/apiproxy.zip
blueprints/cloud-operations/apigee/create-datastore.sh
blueprints/cloud-operations/apigee/deploy-apiproxy.sh

View File

@ -33,7 +33,7 @@ Currently available modules:
- **networking** - [DNS](./modules/dns), [Cloud Endpoints](./modules/endpoints), [address reservation](./modules/net-address), [NAT](./modules/net-cloudnat), [Global Load Balancer (classic)](./modules/net-glb/), [L4 ILB](./modules/net-ilb), [L7 ILB](./modules/net-ilb-l7), [VPC](./modules/net-vpc), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), [VPN dynamic](./modules/net-vpn-dynamic), [HA VPN](./modules/net-vpn-ha), [VPN static](./modules/net-vpn-static), [Service Directory](./modules/service-directory)
- **compute** - [VM/VM group](./modules/compute-vm), [MIG](./modules/compute-mig), [COS container](./modules/cloud-config-container/cos-generic-metadata/) (coredns, mysql, onprem, squid), [GKE cluster](./modules/gke-cluster), [GKE hub](./modules/gke-hub), [GKE nodepool](./modules/gke-nodepool)
- **data** - [BigQuery dataset](./modules/bigquery-dataset), [Bigtable instance](./modules/bigtable-instance), [Cloud SQL instance](./modules/cloudsql-instance), [Data Catalog Policy Tag](./modules/data-catalog-policy-tag), [Datafusion](./modules/datafusion), [GCS](./modules/gcs), [Pub/Sub](./modules/pubsub)
- **development** - [API Gateway](./modules/api-gateway), [Apigee Organization](./modules/apigee-organization), [Apigee X Instance](./modules/apigee-x-instance), [Artifact Registry](./modules/artifact-registry), [Container Registry](./modules/container-registry), [Cloud Source Repository](./modules/source-repository)
- **development** - [API Gateway](./modules/api-gateway), [Apigee](./modules/apigee), [Artifact Registry](./modules/artifact-registry), [Container Registry](./modules/container-registry), [Cloud Source Repository](./modules/source-repository)
- **security** - [Binauthz](./modules/binauthz/), [KMS](./modules/kms), [SecretManager](./modules/secret-manager), [VPC Service Control](./modules/vpc-sc)
- **serverless** - [Cloud Function](./modules/cloud-function), [Cloud Run](./modules/cloud-run)

View File

@ -0,0 +1,78 @@
# Apigee X Analytics in Bigquery
The following blueprint shows to how to create an Apigee X trial organization, with an environment group, an environment attached to that environment group and an instance attached to that environment. It creates a NEG that exposes Apigee service attachment. The NEG is added as a backend to a GLB. API proxy requests will pass through the GLB.
![Analytics northbound networking](diagram1.png)
In addition to this it also creates the setup depicted in the diagram below to export the Apigee analytics of an organization daily to a BigQuery table.
![Apigee analytics in BigQuery](diagram2.png)
Find below a description on how the analytics export to BigQuery works:
1. A Cloud Scheduler Job runs daily at a selected time, publishing a message to a Pub/Sub topic.
2. The message published triggers the execution of a function that makes a call to the Apigee Analytics Export API to export the analytical data available for the previous day.
3. The export function is passed the Apigee organization, environments, datastore name as environment variables. The service account used to run the function needs to be granted the Apigee Admin role on the project. The Apigee Analytics engine asynchronously exports the analytical data to a GCS bucket. This requires the _Apigee Service Agent_ service account to be granted the _Storage Admin_ role on the project.
4. A notification of the files created on GCS is received in a Pub/Sub topic that triggers the execution of the cloud function in charge of loading the data from GCS to the right BigQuery table partition. This function is passed the name of the BigQuery dataset, its location and the name of the table inside that dataset as environment variables. The service account used to run the function needs to be granted the _Storage Object Viewer_ role on the GCS bucket, the _BigQuery Job User_ role on the project and the _BigQuery Data Editor_ role on the table.
Note: This setup only works if you are not using custom analytics.
## Running the blueprint
1. Clone this repository or [open it in cloud shell](https://ssh.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2Fterraform-google-modules%2Fcloud-foundation-fabric&cloudshell_print=cloud-shell-readme.txt&cloudshell_working_dir=blueprints%2Fcloud-operations%apigee), then go through the following steps to create resources:
2. Copy the file [terraform.tfvars.sample](./terraform.tfvars.sample) to a file called ```terraform.tfvars``` and update the values if required.
3. Initialize the terraform configuration
```terraform init```
4. Apply the terraform configuration
```terraform apply```
Once the resources have been created, do the following:
Create an A record in your DNS registrar to point the environment group hostname to the public IP address returned after the terraform configuration was applied. You might need to wait some time until the certificate is provisioned.
## Testing the blueprint
Do the following to verify that everything works as expected.
1. Create an Apigee datastore
```./create-datastore.sh```
2. Deploy an api proxy
```./deploy-apiproxy.sh test```
3. Send some traffic to the proxy
```./send-requests.sh test.my-domain.com 1000```
4. At 4am (UTC) every day the Cloud Scheduler will run and will export the analytics to the BigQuery table. Double-check they are there.
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [envgroups](variables.tf#L54) | Environment groups (NAME => [HOSTNAMES]). | <code>map&#40;list&#40;string&#41;&#41;</code> | ✓ | |
| [environments](variables.tf#L60) | Environments. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string&#41;&#10; node_config &#61; optional&#40;object&#40;&#123;&#10; min_node_count &#61; optional&#40;number&#41;&#10; max_node_count &#61; optional&#40;number&#41;&#10; current_aggregate_node_count &#61; number&#10; &#125;&#41;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; envgroups &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | ✓ | |
| [instances](variables.tf#L76) | Instance. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string&#41;&#10; region &#61; string&#10; environments &#61; list&#40;string&#41;&#10; psa_ip_cidr_range &#61; string&#10; disk_encryption_key &#61; optional&#40;string&#41;&#10; consumer_accept_list &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | ✓ | |
| [project_id](variables.tf#L32) | Project ID. | <code>string</code> | ✓ | |
| [psc_config](variables.tf#L104) | PSC configuration. | <code>map&#40;string&#41;</code> | ✓ | |
| [datastore_name](variables.tf#L97) | Datastore | <code>string</code> | | <code>&#34;gcs&#34;</code> |
| [organization](variables.tf#L38) | Apigee organization. | <code title="object&#40;&#123;&#10; display_name &#61; optional&#40;string, &#34;Apigee organization created by tf module&#34;&#41;&#10; description &#61; optional&#40;string, &#34;Apigee organization created by tf module&#34;&#41;&#10; authorized_network &#61; optional&#40;string, &#34;vpc&#34;&#41;&#10; runtime_type &#61; optional&#40;string, &#34;CLOUD&#34;&#41;&#10; billing_type &#61; optional&#40;string&#41;&#10; database_encryption_key &#61; optional&#40;string&#41;&#10; analytics_region &#61; optional&#40;string, &#34;europe-west1&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [path](variables.tf#L90) | Bucket path. | <code>string</code> | | <code>&#34;&#47;analytics&#34;</code> |
| [project_create](variables.tf#L17) | Parameters for the creation of the new project. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [vpc_create](variables.tf#L26) | Boolean flag indicating whether the VPC should be created or not. | <code>bool</code> | | <code>true</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [ip_address](outputs.tf#L17) | IP address. | |
<!-- END TFDOC -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -0,0 +1,103 @@
// Copyright 2020 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.
const functions = require("@google-cloud/functions-framework");
const superagent = require("superagent");
const { LoggingBunyan } = require("@google-cloud/logging-bunyan");
const bunyan = require("bunyan");
const { GoogleAuth } = require("google-auth-library");
const loggingBunyan = new LoggingBunyan();
const logger = bunyan.createLogger({
name: "analyticsExport",
streams: [
{ stream: process.stdout, level: "info" },
loggingBunyan.stream("info"),
],
});
const ORGANIZATION = process.env.ORGANIZATION;
const ENVIRONMENTS = process.env.ENVIRONMENTS.split(',');
const DATASTORE = process.env.DATASTORE;
const MANAGEMENT_API_URL = "https://apigee.googleapis.com/v1";
function formatDate(date) {
var d = new Date(date),
month = "" + (d.getMonth() + 1),
day = "" + d.getDate(),
year = d.getFullYear();
if (month.length < 2) month = "0" + month;
if (day.length < 2) day = "0" + day;
return [year, month, day].join("-");
}
async function getAccessToken() {
logger.info("Requesting access token...");
const auth = new GoogleAuth();
const token = await auth.getAccessToken();
logger.info("Got access token ");
return token;
}
async function scheduleAnalyticsExport(org, env, token, startDate, endDate) {
logger.info(
`Sending request for an analytics export from ${startDate} to ${endDate} for environment ${env}`
);
try {
const response = await superagent
.post(
`${MANAGEMENT_API_URL}/organizations/${org}/environments/${env}/analytics/exports`
)
.send({
name: `Analytics from ${startDate} to ${endDate}`,
description: `Analytics from ${startDate} to ${endDate}`,
dateRange: {
start: startDate,
end: endDate,
},
outputFormat: "csv",
csvDelimiter: ",",
datastoreName: DATASTORE,
})
.set('Authorization', `Bearer ${token}`)
.accept('json');
logger.info('Analytics export scheduled');
return response;
} catch (error) {
logger.error('Error scheduling analytics export');
logger.error(error);
throw error;
}
}
functions.cloudEvent("export", async (cloudEvent) => {
const today = new Date();
const endDate = formatDate(today);
const yesterday = new Date(today.setDate(today.getDate() - 1));
const startDate = formatDate(yesterday);
const token = await getAccessToken();
try {
for(let i = 0; i < ENVIRONMENTS.length; i++) {
const env = ENVIRONMENTS[i];
const response = await scheduleAnalyticsExport(ORGANIZATION, env, token, startDate, endDate);
logger.error('Export scheduled: ' + response.body.self);
}
} catch (error) {
logger.error('Analytics exports was not scheduled');
logger.error(error);
}
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
{
"name": "export",
"version": "1.0.0",
"description": "Apigee analytics export to GCS",
"main": "index.js",
"engines": {
"node": ">=16.0.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@google-cloud/functions-framework": "^3.1.2",
"@google-cloud/logging-bunyan": "^4.2.0",
"bunyan": "^1.8.15",
"express": "^4.18.2",
"google-auth-library": "^8.6.0",
"superagent": "^8.0.3"
}
}

View File

@ -0,0 +1,76 @@
/**
* Copyright 2020 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.
*/
const functions = require("@google-cloud/functions-framework");
const { Storage } = require('@google-cloud/storage');
const { BigQuery } = require('@google-cloud/bigquery');
const bunyan = require('bunyan');
const schema = require('./schema.json');
const { LoggingBunyan } = require('@google-cloud/logging-bunyan');
const loggingBunyan = new LoggingBunyan();
const logger = bunyan.createLogger({
name: 'gcs2bq',
streams: [
{ stream: process.stdout, level: 'info' },
loggingBunyan.stream('info')
],
});
const DATASET = process.env.DATASET
const TABLE = process.env.TABLE
const LOCATION = process.env.LOCATION
async function loadCSVFromGCS(datasetId, tableId, timePartition, bucket, filename) {
const metadata = {
sourceFormat: 'CSV',
skipLeadingRows: 1,
maxBadRecords: 1000,
schema: {
fields: schema
},
location: LOCATION
};
logger.info(`Trying to load ${bucket}/${filename} in ${timePartition} time partition of table ${tableId}...`);
const bigquery = new BigQuery();
const storage = new Storage();
const [job] = await bigquery
.dataset(datasetId)
.table(`${tableId}\$${timePartition}`)
.load(storage.bucket(bucket).file(filename), metadata);
logger.info(`Job ${job.id} completed.`);
const errors = job.status.errors;
if (errors && errors.length > 0) {
logger.info('Errors occurred:' + JSON.stringify(errors));
throw new Error('File could not be loaded in time partition');
}
}
functions.cloudEvent("gcs2bq", async (cloudEvent) => {
const data = JSON.parse(Buffer.from(cloudEvent.data.message.data, 'base64').toString());
logger.info('Notification received');
logger.info(data);
const pattern = /([^/]+\/)?[0-9]{14}\-[0-9a-z]{8}\-[0-9a-z]{4}\-[0-9a-z]{4}\-[0-9a-z]{4}\-[0-9a-z]{12}\-api\-from\-([0-9]{8})0000\-to\-([0-9]{8})0000\/result\-[0-9]+\.csv\.gz/
const result = data.name.match(pattern);
const timePartition = result[2];
await loadCSVFromGCS(DATASET, TABLE, timePartition, data.bucket, data.name)
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
{
"name": "gcs2bq",
"version": "1.0.0",
"description": "",
"main": "index.js",
"engines": {
"node": ">=16.0.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@google-cloud/bigquery": "^6.0.3",
"@google-cloud/functions-framework": "^3.1.2",
"@google-cloud/logging-bunyan": "^4.2.0",
"@google-cloud/storage": "^6.7.0",
"bunyan": "^1.8.15"
}
}

View File

@ -0,0 +1,447 @@
[
{
"mode": "NULLABLE",
"name": "organization",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "environment",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "apiproxy",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "request_uri",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "proxy",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "proxy_basepath",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "request_verb",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "request_size",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "response_status_code",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "is_error",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "client_received_start_timestamp",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "client_received_end_timestamp",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "target_sent_start_timestamp",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "target_sent_end_timestamp",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "target_received_start_timestamp",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "target_received_end_timestamp",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "client_sent_start_timestamp",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "client_sent_end_timestamp",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "client_ip",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "client_id",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "developer",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "developer_app",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "api_product",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "flow_resource",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "target",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "target_url",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "target_host",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "apiproxy_revision",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "proxy_pathsuffix",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "proxy_client_ip",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "target_basepath",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "target_ip",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "request_path",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "response_size",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "developer_email",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "virtual_host",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "gateway_flow_id",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "message_count",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "total_response_time",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "request_processing_latency",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "response_processing_latency",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "target_response_time",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "cache_hit",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "x_forwarded_for_ip",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "useragent",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "target_response_code",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "target_error",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "policy_error",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "ax_created_time",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "ax_ua_agent_type",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_ua_os_version",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_ua_os_family",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_ua_agent_version",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_ua_device_category",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_ua_agent_family",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "gateway_source",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_month_of_year",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_hour_of_day",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_week_of_month",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_day_of_week",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_cache_key",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_cache_l1_count",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "ax_cache_source",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_cache_executed",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "ax_cache_name",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_edge_execution_fault_code",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_edge_is_apigee_fault",
"type": "INTEGER"
},
{
"mode": "NULLABLE",
"name": "ax_execution_fault_policy_name",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_execution_fault_flow_state",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_execution_fault_flow_name",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_dn_region",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_true_client_ip",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "x_apigee_edge_execution_stats_request_flow_end_timestamp",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "x_apigee_edge_execution_stats_request_flow_start_timestamp",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "x_apigee_edge_execution_stats_response_flow_end_timestamp",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "x_apigee_edge_execution_stats_response_flow_start_timestamp",
"type": "TIMESTAMP"
},
{
"mode": "NULLABLE",
"name": "x_apigee_edge_stats_steps",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "senseaction",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "ax_resolved_client_ip",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "x_forwarded_proto",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "x_apigee_intelligence_service",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "envgroup_hostname",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "x_apigee_mintng_rate",
"type": "NUMERIC"
},
{
"mode": "NULLABLE",
"name": "x_apigee_mintng_rate_plan_id",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "x_apigee_mintng_dev_share",
"type": "NUMERIC"
},
{
"mode": "NULLABLE",
"name": "x_apigee_mintng_currency",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "x_apigee_mintng_price",
"type": "NUMERIC"
},
{
"mode": "NULLABLE",
"name": "x_apigee_mintng_tx_success",
"type": "BOOLEAN"
},
{
"mode": "NULLABLE",
"name": "x_apigee_mintng_price_multiplier",
"type": "NUMERIC"
}
]

View File

@ -0,0 +1,311 @@
/**
* 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.
*/
module "project" {
source = "../../../modules/project"
billing_account = (var.project_create != null
? var.project_create.billing_account_id
: null
)
parent = (var.project_create != null
? var.project_create.parent
: null
)
name = var.project_id
project_create = var.project_create == null ? false : true
services = [
"apigee.googleapis.com",
"bigquery.googleapis.com",
"cloudbuild.googleapis.com",
"cloudfunctions.googleapis.com",
"cloudscheduler.googleapis.com",
"logging.googleapis.com",
"compute.googleapis.com",
"pubsub.googleapis.com",
"servicenetworking.googleapis.com",
"storage.googleapis.com"
]
iam = {
"roles/bigquery.jobUser" = [module.function_gcs2bq.service_account_iam_email]
"roles/logging.logWriter" = [module.function_export.service_account_iam_email]
"roles/logging.logWriter" = [module.function_gcs2bq.service_account_iam_email]
"roles/apigee.admin" = [module.function_export.service_account_iam_email]
"roles/storage.admin" = ["serviceAccount:${module.project.service_accounts.robots.apigee}"]
}
}
module "vpc" {
source = "../../../modules/net-vpc"
project_id = module.project.project_id
name = var.organization.authorized_network
vpc_create = var.vpc_create
subnets_psc = [for k, v in var.psc_config :
{
ip_cidr_range = v
name = "subnet-psc-${k}"
region = k
}
]
psa_config = {
ranges = { for k, v in var.instances :
"apigee-${k}" => v.psa_ip_cidr_range
}
}
}
module "apigee" {
source = "../../../modules/apigee"
project_id = module.project.project_id
organization = var.organization
envgroups = var.envgroups
environments = var.environments
instances = var.instances
depends_on = [
module.vpc
]
}
resource "google_compute_region_network_endpoint_group" "neg" {
for_each = var.instances
name = "apigee-neg-${each.key}"
project = module.project.project_id
region = each.value.region
network_endpoint_type = "PRIVATE_SERVICE_CONNECT"
psc_target_service = module.apigee.instances[each.key].service_attachment
network = module.vpc.network.self_link
subnetwork = module.vpc.subnets_psc["${each.value.region}/subnet-psc-${each.value.region}"].self_link
}
module "glb" {
source = "../../../modules/net-glb"
name = "glb"
project_id = module.project.project_id
https = true
reserve_ip_address = true
ssl_certificates_config = { for k, v in var.envgroups :
"${k}-domain" => {
domains = v,
unmanaged_config = null
}
}
target_proxy_https_config = {
ssl_certificates = [for k, v in var.envgroups : "${k}-domain"]
}
health_checks_config_defaults = null
backend_services_config = {
apigee = {
bucket_config = null
enable_cdn = false
cdn_config = null
group_config = {
backends = [for k, v in google_compute_region_network_endpoint_group.neg :
{
group = v.id
options = null
}
],
health_checks = []
log_config = null
options = {
affinity_cookie_ttl_sec = null
custom_request_headers = null
custom_response_headers = null
connection_draining_timeout_sec = null
load_balancing_scheme = "EXTERNAL_MANAGED"
locality_lb_policy = null
port_name = null
security_policy = null
session_affinity = null
timeout_sec = null
circuits_breakers = null
consistent_hash = null
iap = null
protocol = "HTTPS"
}
}
}
}
global_forwarding_rule_config = {
load_balancing_scheme = "EXTERNAL_MANAGED"
ip_protocol = "TCP"
ip_version = "IPV4"
port_range = null
}
}
module "pubsub_export" {
source = "../../../modules/pubsub"
project_id = module.project.project_id
name = "topic-export"
}
module "bucket_export" {
source = "../../../modules/gcs"
project_id = module.project.project_id
name = "${module.project.project_id}-export"
iam = {
"roles/storage.objectViewer" = [module.function_gcs2bq.service_account_iam_email]
}
notification_config = {
enabled = true
payload_format = "JSON_API_V1"
sa_email = module.project.service_accounts.robots.storage
topic_name = "topic-gcs2bq"
event_types = ["OBJECT_FINALIZE"]
custom_attributes = {}
}
}
module "function_export" {
source = "../../../modules/cloud-function"
project_id = module.project.project_id
name = "export"
bucket_name = "${module.project.project_id}-code-export"
region = var.organization.analytics_region
ingress_settings = "ALLOW_INTERNAL_ONLY"
bucket_config = {
location = null
lifecycle_delete_age = 1
}
bundle_config = {
source_dir = "${path.module}/functions/export"
output_path = "${path.module}/bundle-export.zip"
excludes = null
}
function_config = {
entry_point = "export"
instances = null
memory = null
runtime = "nodejs16"
timeout = 180
}
environment_variables = {
ORGANIZATION = module.apigee.org_name,
ENVIRONMENTS = join(",", [for k, v in module.apigee.environments : k])
DATASTORE = var.datastore_name
}
trigger_config = {
event = "google.pubsub.topic.publish"
resource = module.pubsub_export.id
retry = null
}
service_account_create = true
}
module "function_gcs2bq" {
source = "../../../modules/cloud-function"
project_id = module.project.project_id
name = "gcs2bq"
bucket_name = "${module.project.project_id}-code-gcs2bq"
region = var.organization.analytics_region
ingress_settings = "ALLOW_INTERNAL_ONLY"
bucket_config = {
location = null
lifecycle_delete_age = 1
}
bundle_config = {
source_dir = "${path.module}/functions/gcs2bq"
output_path = "${path.module}/bundle-gcs2bq.zip"
excludes = null
}
function_config = {
entry_point = "gcs2bq"
instances = null
memory = null
runtime = "nodejs16"
timeout = 180
}
environment_variables = {
DATASET = module.bigquery_dataset.dataset_id
TABLE = module.bigquery_dataset.tables["analytics"].table_id
LOCATION = var.organization.analytics_region
}
trigger_config = {
event = "google.pubsub.topic.publish"
resource = module.bucket_export.topic
retry = null
}
service_account_create = true
}
module "bigquery_dataset" {
source = "../../../modules/bigquery-dataset"
project_id = module.project.project_id
id = "apigee"
location = var.organization.analytics_region
tables = {
analytics = {
friendly_name = "analytics"
labels = {}
options = null
partitioning = {
field = "client_received_end_timestamp"
range = null
time = { type = "DAY", expiration_ms = null }
}
schema = file("${path.module}/functions/gcs2bq/schema.json")
deletion_protection = false
}
}
iam = {
"roles/bigquery.dataEditor" = [module.function_gcs2bq.service_account_iam_email]
}
}
resource "google_app_engine_application" "app" {
project = module.project.project_id
location_id = ((var.organization.analytics_region == "europe-west1" || var.organization.analytics_region == "us-central1") ?
substr(var.organization.analytics_region, 0, length(var.organization.analytics_region) - 1) :
var.organization.analytics_region)
}
resource "google_cloud_scheduler_job" "job" {
name = "export"
schedule = "0 4 * * *"
time_zone = "Etc/UTC"
attempt_deadline = "320s"
project = module.project.project_id
region = var.organization.analytics_region
pubsub_target {
topic_name = module.pubsub_export.id
data = base64encode("test")
}
}
resource "local_file" "create_datastore_file" {
content = templatefile("${path.module}/templates/create-datastore.sh.tpl", {
org_name = module.apigee.org_name
project_id = module.project.project_id
datastore_name = var.datastore_name
bucket_name = module.bucket_export.name
path = var.path
})
filename = "${path.module}/create-datastore.sh"
file_permission = "0777"
}
resource "local_file" "deploy_apiproxy_file" {
content = templatefile("${path.module}/templates/deploy-apiproxy.sh.tpl", {
org_name = module.apigee.org_name
})
filename = "${path.module}/deploy-apiproxy.sh"
file_permission = "0777"
}

View File

@ -14,12 +14,7 @@
* limitations under the License.
*/
variable "analytics_region" {
type = string
default = "europe-west1"
}
variable "network" {
type = string
default = "apigee-vpc"
output "ip_address" {
description = "IP address."
value = module.glb.ip_address
}

View File

@ -0,0 +1,251 @@
{
"name": "apigee",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"superagent-debugger": "^1.2.9"
}
},
"node_modules/ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
"dependencies": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
"dependencies": {
"ansi-regex": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/query-string": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
"integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==",
"dependencies": {
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
"dependencies": {
"ansi-regex": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/superagent-debugger": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/superagent-debugger/-/superagent-debugger-1.2.9.tgz",
"integrity": "sha512-iH4NvJl1utorgRbrsYoOM8yoeTbS7YWLoDkAwRy2rgB6aP5Lr36XxmpE8GbgvmUY6R4QmYr+4R4IdAGMPmwR9g==",
"dependencies": {
"chalk": "^1.1.3",
"debug": "^2.6.0",
"lodash": "^4.17.4",
"moment": "^2.17.1",
"query-string": "^4.3.1"
}
},
"node_modules/supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
"engines": {
"node": ">=0.8.0"
}
}
},
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA=="
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
}
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
},
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
},
"query-string": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
"integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==",
"requires": {
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
},
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ=="
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"superagent-debugger": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/superagent-debugger/-/superagent-debugger-1.2.9.tgz",
"integrity": "sha512-iH4NvJl1utorgRbrsYoOM8yoeTbS7YWLoDkAwRy2rgB6aP5Lr36XxmpE8GbgvmUY6R4QmYr+4R4IdAGMPmwR9g==",
"requires": {
"chalk": "^1.1.3",
"debug": "^2.6.0",
"lodash": "^4.17.4",
"moment": "^2.17.1",
"query-string": "^4.3.1"
}
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g=="
}
}
}

View File

@ -0,0 +1,28 @@
# 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.
#!/bin/bash
if [ $# -lt 2 ]; then
echo "Usage: $0 ENVGROUP_HOSTNAME NUM_REQUESTS"
exit 1
fi
ENVGROUP_HOSTNAME=$1
NUM_REQUESTS=$2
for i in $(seq 1 $NUM_REQUESTS)
do
curl -v https://$ENVGROUP_HOSTNAME/httpbin/headers
done

View File

@ -0,0 +1,30 @@
# 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.
#!/bin/bash
curl "https://apigee.googleapis.com/v1/organizations/${org_name}/analytics/datastores" \
-X POST \
-H "Content-type:application/json" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-d \
'{
"displayName": "${datastore_name}",
"targetType": "gcs",
"datastoreConfig": {
"projectId": "${project_id}",
"bucketName": "${bucket_name}",
"path": "${path}"
}
}'

View File

@ -0,0 +1,41 @@
# 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.
#!/bin/bash
if [ $# -lt 1 ]; then
echo "Usage: $0 ENV_NAME"
exit 1
fi
ORG_NAME=${org_name}
ENV_NAME=$1
wget https://github.com/apigee/api-platform-samples/raw/master/sample-proxies/apigee-quickstart/httpbin_rev1_2020_02_02.zip -O apiproxy.zip
export TOKEN=$(gcloud auth print-access-token)
curl -v -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type:application/octet-stream" \
-T 'apiproxy.zip' \
"https://apigee.googleapis.com/v1/organizations/$ORG_NAME/apis?name=httpbin&action=import"
curl -v -X POST \
-H "Authorization: Bearer $TOKEN" \
"https://apigee.googleapis.com/v1/organizations/$ORG_NAME/environments/$ENV_NAME/apis/httpbin/revisions/1/deployments"
curl -v \
-H "Authorization: Bearer $TOKEN" \
"https://apigee.googleapis.com/v1/organizations/$ORG_NAME/environments/$ENV_NAME/apis/httpbin/revisions/1/deployments"

View File

@ -0,0 +1,23 @@
project_create = {
billing_account_id = "011D94-9C86C1-ADD197"
parent = "folders/586929298360"
}
project_id = "g-prj-cd-sb-apigee-bq-10"
envgroups = {
test = ["test.cool-demos.space"]
}
environments = {
apis-test = {
envgroups = ["test"]
}
}
instances = {
instance-ew1 = {
region = "europe-west1"
environments = ["apis-test"]
psa_ip_cidr_range = "10.0.4.0/22"
}
}
psc_config = {
europe-west1 = "10.0.0.0/28"
}

View File

@ -0,0 +1,108 @@
/**
* 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.
*/
variable "project_create" {
description = "Parameters for the creation of the new project."
type = object({
billing_account_id = string
parent = string
})
default = null
}
variable "vpc_create" {
description = "Boolean flag indicating whether the VPC should be created or not."
type = bool
default = true
}
variable "project_id" {
description = "Project ID."
type = string
nullable = false
}
variable "organization" {
description = "Apigee organization."
type = object({
display_name = optional(string, "Apigee organization created by tf module")
description = optional(string, "Apigee organization created by tf module")
authorized_network = optional(string, "vpc")
runtime_type = optional(string, "CLOUD")
billing_type = optional(string)
database_encryption_key = optional(string)
analytics_region = optional(string, "europe-west1")
})
nullable = false
default = {
}
}
variable "envgroups" {
description = "Environment groups (NAME => [HOSTNAMES])."
type = map(list(string))
nullable = false
}
variable "environments" {
description = "Environments."
type = map(object({
display_name = optional(string)
description = optional(string)
node_config = optional(object({
min_node_count = optional(number)
max_node_count = optional(number)
current_aggregate_node_count = number
}))
iam = optional(map(list(string)))
envgroups = list(string)
}))
nullable = false
}
variable "instances" {
description = "Instance."
type = map(object({
display_name = optional(string)
description = optional(string)
region = string
environments = list(string)
psa_ip_cidr_range = string
disk_encryption_key = optional(string)
consumer_accept_list = optional(list(string))
}))
nullable = false
}
variable "path" {
description = "Bucket path."
type = string
default = "/analytics"
nullable = false
}
variable "datastore_name" {
description = "Datastore"
type = string
nullable = false
default = "gcs"
}
variable "psc_config" {
description = "PSC configuration."
type = map(string)
nullable = false
}

View File

@ -78,8 +78,7 @@ These modules are used in the examples included in this repository. If you are u
## Development
- [API Gateway](./api-gateway)
- [Apigee Organization](./apigee-organization)
- [Apigee X Instance](./apigee-x-instance)
- [Apigee](./apigee)
- [Artifact Registry](./artifact-registry)
- [Container Registry](./container-registry)
- [Cloud Source Repository](./source-repository)

View File

@ -1,143 +0,0 @@
# Google Apigee Organization Module
This module allows managing a single Apigee organization and its environments and environmentgroups.
## Examples
### Apigee X Evaluation Organization
```hcl
module "apigee-organization" {
source = "./fabric/modules/apigee-organization"
project_id = "my-project"
analytics_region = "us-central1"
runtime_type = "CLOUD"
authorized_network = "my-vpc"
apigee_environments = {
eval1 = {
api_proxy_type = "PROGRAMMABLE"
deployment_type = "PROXY"
}
eval2 = {
api_proxy_type = "CONFIGURABLE"
deployment_type = "ARCHIVE"
}
}
apigee_envgroups = {
eval = {
environments = [
"eval1",
"eval2"
]
hostnames = [
"eval.api.example.com"
]
}
}
}
# tftest modules=1 resources=6
```
### Apigee X Paid Organization
```hcl
module "apigee-organization" {
source = "./fabric/modules/apigee-organization"
project_id = "my-project"
analytics_region = "us-central1"
runtime_type = "CLOUD"
authorized_network = "my-vpc"
database_encryption_key = "my-data-key"
apigee_environments = {
dev1 = {
api_proxy_type = "PROGRAMMABLE"
deployment_type = "PROXY"
}
dev2 = {
api_proxy_type = "CONFIGURABLE"
deployment_type = "ARCHIVE"
}
test1 = {}
test2 = {}
}
apigee_envgroups = {
dev = {
environments = [
"dev1",
"dev2"
]
hostnames = [
"dev.api.example.com"
]
}
test = {
environments = [
"test1",
"test2"
]
hostnames = [
"test.api.example.com"
]
}
}
}
# tftest modules=1 resources=11
```
### Apigee hybrid Organization
```hcl
module "apigee-organization" {
source = "./fabric/modules/apigee-organization"
project_id = "my-project"
analytics_region = "us-central1"
runtime_type = "HYBRID"
apigee_environments = {
eval1 = {
api_proxy_type = "PROGRAMMABLE"
deployment_type = "PROXY"
}
eval2 = {}
}
apigee_envgroups = {
eval = {
environments = [
"eval1",
"eval2"
]
hostnames = [
"eval.api.example.com"
]
}
}
}
# tftest modules=1 resources=6
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [analytics_region](variables.tf#L17) | Analytics Region for the Apigee Organization (immutable). See https://cloud.google.com/apigee/docs/api-platform/get-started/install-cli. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L72) | Project ID to host this Apigee organization (will also become the Apigee Org name). | <code>string</code> | ✓ | |
| [runtime_type](variables.tf#L77) | Apigee runtime type. Must be `CLOUD` or `HYBRID`. | <code>string</code> | ✓ | |
| [apigee_envgroups](variables.tf#L22) | Apigee Environment Groups. | <code title="map&#40;object&#40;&#123;&#10; environments &#61; list&#40;string&#41;&#10; hostnames &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [apigee_environments](variables.tf#L31) | Apigee Environment Names. | <code title="map&#40;object&#40;&#123;&#10; api_proxy_type &#61; optional&#40;string, &#34;API_PROXY_TYPE_UNSPECIFIED&#34;&#41;&#10; deployment_type &#61; optional&#40;string, &#34;DEPLOYMENT_TYPE_UNSPECIFIED&#34;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [authorized_network](variables.tf#L48) | VPC network self link (requires service network peering enabled (Used in Apigee X only). | <code>string</code> | | <code>null</code> |
| [billing_type](variables.tf#L86) | Billing type of the Apigee organization. | <code>string</code> | | <code>null</code> |
| [database_encryption_key](variables.tf#L54) | Cloud KMS key self link (e.g. `projects/foo/locations/us/keyRings/bar/cryptoKeys/baz`) used for encrypting the data that is stored and replicated across runtime instances (immutable, used in Apigee X only). | <code>string</code> | | <code>null</code> |
| [description](variables.tf#L60) | Description of the Apigee Organization. | <code>string</code> | | <code>&#34;Apigee Organization created by tf module&#34;</code> |
| [display_name](variables.tf#L66) | Display Name of the Apigee Organization. | <code>string</code> | | <code>null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [envs](outputs.tf#L17) | Apigee Environments. | |
| [org](outputs.tf#L22) | Apigee Organization. | |
| [org_ca_certificate](outputs.tf#L27) | Apigee organization CA certificate. | |
| [org_id](outputs.tf#L32) | Apigee Organization ID. | |
| [subscription_type](outputs.tf#L37) | Apigee subscription type. | |
<!-- END TFDOC -->

View File

@ -1,66 +0,0 @@
/**
* 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.
*/
locals {
env_pairs = flatten([
for env_name, env in var.apigee_environments : {
api_proxy_type = env.api_proxy_type
deployment_type = env.deployment_type
env_name = env_name
}
])
env_envgroup_pairs = flatten([
for eg_name, eg in var.apigee_envgroups : [
for e in eg.environments : {
envgroup = eg_name
env = e
}
]
])
}
resource "google_apigee_organization" "apigee_org" {
project_id = var.project_id
analytics_region = var.analytics_region
display_name = var.display_name
description = var.description
runtime_type = var.runtime_type
billing_type = var.billing_type
authorized_network = var.authorized_network
runtime_database_encryption_key_name = var.database_encryption_key
}
resource "google_apigee_environment" "apigee_env" {
for_each = { for env in local.env_pairs : env.env_name => env }
api_proxy_type = each.value.api_proxy_type
deployment_type = each.value.deployment_type
name = each.key
org_id = google_apigee_organization.apigee_org.id
}
resource "google_apigee_envgroup" "apigee_envgroup" {
for_each = var.apigee_envgroups
org_id = google_apigee_organization.apigee_org.id
name = each.key
hostnames = each.value.hostnames
}
resource "google_apigee_envgroup_attachment" "env_to_envgroup_attachment" {
for_each = { for pair in local.env_envgroup_pairs : "${pair.envgroup}-${pair.env}" => pair }
envgroup_id = google_apigee_envgroup.apigee_envgroup[each.value.envgroup].id
environment = google_apigee_environment.apigee_env[each.value.env].name
}

View File

@ -1,40 +0,0 @@
/**
* 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.
*/
output "envs" {
description = "Apigee Environments."
value = google_apigee_environment.apigee_env
}
output "org" {
description = "Apigee Organization."
value = google_apigee_organization.apigee_org
}
output "org_ca_certificate" {
description = "Apigee organization CA certificate."
value = google_apigee_organization.apigee_org.ca_certificate
}
output "org_id" {
description = "Apigee Organization ID."
value = google_apigee_organization.apigee_org.id
}
output "subscription_type" {
description = "Apigee subscription type."
value = google_apigee_organization.apigee_org.subscription_type
}

View File

@ -1,90 +0,0 @@
/**
* 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.
*/
variable "analytics_region" {
description = "Analytics Region for the Apigee Organization (immutable). See https://cloud.google.com/apigee/docs/api-platform/get-started/install-cli."
type = string
}
variable "apigee_envgroups" {
description = "Apigee Environment Groups."
type = map(object({
environments = list(string)
hostnames = list(string)
}))
default = {}
}
variable "apigee_environments" {
description = "Apigee Environment Names."
type = map(object({
api_proxy_type = optional(string, "API_PROXY_TYPE_UNSPECIFIED")
deployment_type = optional(string, "DEPLOYMENT_TYPE_UNSPECIFIED")
}))
default = {}
validation {
condition = alltrue([for k, v in var.apigee_environments : contains(["API_PROXY_TYPE_UNSPECIFIED", "PROGRAMMABLE", "CONFIGURABLE"], v.api_proxy_type)])
error_message = "Allowed values for api_proxy_type \"API_PROXY_TYPE_UNSPECIFIED\", \"PROGRAMMABLE\" or \"CONFIGURABLE\"."
}
validation {
condition = alltrue([for k, v in var.apigee_environments : contains(["DEPLOYMENT_TYPE_UNSPECIFIED", "PROXY", "ARCHIVE"], v.deployment_type)])
error_message = "Allowed values for deployment_type \"DEPLOYMENT_TYPE_UNSPECIFIED\", \"PROXY\" or \"ARCHIVE\"."
}
}
variable "authorized_network" {
description = "VPC network self link (requires service network peering enabled (Used in Apigee X only)."
type = string
default = null
}
variable "database_encryption_key" {
description = "Cloud KMS key self link (e.g. `projects/foo/locations/us/keyRings/bar/cryptoKeys/baz`) used for encrypting the data that is stored and replicated across runtime instances (immutable, used in Apigee X only)."
type = string
default = null
}
variable "description" {
description = "Description of the Apigee Organization."
type = string
default = "Apigee Organization created by tf module"
}
variable "display_name" {
description = "Display Name of the Apigee Organization."
type = string
default = null
}
variable "project_id" {
description = "Project ID to host this Apigee organization (will also become the Apigee Org name)."
type = string
}
variable "runtime_type" {
description = "Apigee runtime type. Must be `CLOUD` or `HYBRID`."
type = string
validation {
condition = contains(["CLOUD", "HYBRID"], var.runtime_type)
error_message = "Allowed values for runtime_type \"CLOUD\" or \"HYBRID\"."
}
}
variable "billing_type" {
description = "Billing type of the Apigee organization."
type = string
default = null
}

View File

@ -1,70 +0,0 @@
# Google Apigee X Instance Module
This module allows managing a single Apigee X instance and its environment attachments.
## Examples
### Apigee X Evaluation Instance
```hcl
module "apigee-x-instance" {
source = "./fabric/modules/apigee-x-instance"
name = "my-us-instance"
region = "us-central1"
ip_range = "10.0.0.0/22"
apigee_org_id = "my-project"
apigee_environments = [
"eval1",
"eval2"
]
}
# tftest modules=1 resources=3
```
### Apigee X Paid Instance
```hcl
module "apigee-x-instance" {
source = "./fabric/modules/apigee-x-instance"
name = "my-us-instance"
region = "us-central1"
ip_range = "10.0.0.0/22"
disk_encryption_key = "my-disk-key"
apigee_org_id = "my-project"
apigee_environments = [
"dev1",
"dev2",
"test1",
"test2"
]
}
# tftest modules=1 resources=5
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [apigee_org_id](variables.tf#L32) | Apigee Organization ID. | <code>string</code> | ✓ | |
| [name](variables.tf#L55) | Apigee instance name. | <code>string</code> | ✓ | |
| [region](variables.tf#L60) | Compute region. | <code>string</code> | ✓ | |
| [apigee_envgroups](variables.tf#L17) | Apigee Environment Groups. | <code title="map&#40;object&#40;&#123;&#10; environments &#61; list&#40;string&#41;&#10; hostnames &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [apigee_environments](variables.tf#L26) | Apigee Environment Names. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [consumer_accept_list](variables.tf#L37) | List of projects (id/number) that can privately connect to the service attachment. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [disk_encryption_key](variables.tf#L49) | Customer Managed Encryption Key (CMEK) self link (e.g. `projects/foo/locations/us/keyRings/bar/cryptoKeys/baz`) used for disk and volume encryption (required for PAID Apigee Orgs only). | <code>string</code> | | <code>null</code> |
| [ip_range](variables.tf#L43) | Input: Customer-provided CIDR blocks of length 22 (e.g. `10.0.0.0/22`) Output: Main and Support CIDR (e.g. `10.0.0.0/22,10.1.0.0/28`). | <code>string</code> | | <code>null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [endpoint](outputs.tf#L17) | Internal endpoint of the Apigee instance. | |
| [id](outputs.tf#L22) | Apigee instance ID. | |
| [instance](outputs.tf#L27) | Apigee instance. | |
| [port](outputs.tf#L32) | Port number of the internal endpoint of the Apigee instance. | |
| [service_attachment](outputs.tf#L37) | Resource name of the service attachment created for this Apigee instance. | |
<!-- END TFDOC -->

View File

@ -1,30 +0,0 @@
/**
* 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.
*/
resource "google_apigee_instance" "apigee_instance" {
org_id = var.apigee_org_id
name = var.name
location = var.region
ip_range = var.ip_range
disk_encryption_key_name = var.disk_encryption_key
consumer_accept_list = var.consumer_accept_list
}
resource "google_apigee_instance_attachment" "apigee_instance_attchment" {
for_each = toset(var.apigee_environments)
instance_id = google_apigee_instance.apigee_instance.id
environment = each.key
}

View File

@ -1,40 +0,0 @@
/**
* 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.
* limitations under the License.
* See the License for the specific language governing permissions and
*/
output "endpoint" {
description = "Internal endpoint of the Apigee instance."
value = google_apigee_instance.apigee_instance.host
}
output "id" {
description = "Apigee instance ID."
value = google_apigee_instance.apigee_instance.id
}
output "instance" {
description = "Apigee instance."
value = google_apigee_instance.apigee_instance
}
output "port" {
description = "Port number of the internal endpoint of the Apigee instance."
value = google_apigee_instance.apigee_instance.port
}
output "service_attachment" {
description = "Resource name of the service attachment created for this Apigee instance."
value = google_apigee_instance.apigee_instance.service_attachment
}

View File

@ -1,63 +0,0 @@
/**
* 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.
*/
variable "apigee_envgroups" {
description = "Apigee Environment Groups."
type = map(object({
environments = list(string)
hostnames = list(string)
}))
default = {}
}
variable "apigee_environments" {
description = "Apigee Environment Names."
type = list(string)
default = []
}
variable "apigee_org_id" {
description = "Apigee Organization ID."
type = string
}
variable "consumer_accept_list" {
description = "List of projects (id/number) that can privately connect to the service attachment."
type = list(string)
default = null
}
variable "ip_range" {
description = "Input: Customer-provided CIDR blocks of length 22 (e.g. `10.0.0.0/22`) Output: Main and Support CIDR (e.g. `10.0.0.0/22,10.1.0.0/28`)."
type = string
default = null
}
variable "disk_encryption_key" {
description = "Customer Managed Encryption Key (CMEK) self link (e.g. `projects/foo/locations/us/keyRings/bar/cryptoKeys/baz`) used for disk and volume encryption (required for PAID Apigee Orgs only)."
type = string
default = null
}
variable "name" {
description = "Apigee instance name."
type = string
}
variable "region" {
description = "Compute region."
type = string
}

162
modules/apigee/README.md Normal file
View File

@ -0,0 +1,162 @@
# Apigee
This module simplifies the creation of a Apigee resources (organization, environment groups, environment group attachments, environments, instances and instance attachments).
## Example
### All resources (CLOUD)
```hcl
module "apigee" {
source = "./fabric/modules/apigee"
project_id = "my-project"
organization = {
display_name = "My Organization"
description = "My Organization"
authorized_network = "my-vpc"
runtime_type = "CLOUD"
billing_type = "PAYG"
database_encryption_key = "123456789"
analytics_region = "europe-west1"
}
envgroups = {
test = ["test.example.com"]
prod = ["prod.example.com"]
}
environments = {
apis-test = {
display_name = "APIs test"
description = "APIs Test"
envgroups = ["test"]
}
apis-prod = {
display_name = "APIs prod"
description = "APIs prod"
envgroups = ["prod"]
iam = {
"roles/viewer" = ["group:devops@myorg.com"]
}
}
}
instances = {
instance-test-ew1 = {
region = "europe-west1"
environments = ["apis-test"]
psa_ip_cidr_range = "10.0.4.0/22"
}
instance-prod-ew1 = {
region = "europe-west1"
environments = ["apis-prod"]
psa_ip_cidr_range = "10.0.4.0/22"
}
}
}
# tftest modules=1 resources=12
```
### All resources (HYBRID control plane)
```hcl
module "apigee" {
source = "./fabric/modules/apigee"
project_id = "my-project"
organization = {
display_name = "My Organization"
description = "My Organization"
runtime_type = "HYBRID"
analytics_region = "europe-west1"
}
envgroups = {
test = ["test.example.com"]
prod = ["prod.example.com"]
}
environments = {
apis-test = {
display_name = "APIs test"
description = "APIs Test"
envgroups = ["test"]
}
apis-prod = {
display_name = "APIs prod"
description = "APIs prod"
envgroups = ["prod"]
iam = {
"roles/viewer" = ["group:devops@myorg.com"]
}
}
}
}
# tftest modules=1 resources=8
```
### New environment group in an existing organization
```hcl
module "apigee" {
source = "./fabric/modules/apigee"
project_id = "my-project"
envgroups = {
test = ["test.example.com"]
}
}
# tftest modules=1 resources=1
```
### New environment in an existing environment group
```hcl
module "apigee" {
source = "./fabric/modules/apigee"
project_id = "my-project"
environments = {
apis-test = {
display_name = "APIs test"
description = "APIs Test"
envgroups = ["test"]
}
}
}
# tftest modules=1 resources=2
```
### New instance attached to an existing environment
```hcl
module "apigee" {
source = "./fabric/modules/apigee"
project_id = "my-project"
instances = {
instance-test-ew1 = {
region = "europe-west1"
environments = ["apis-test"]
psa_ip_cidr_range = "10.0.4.0/22"
}
}
}
# tftest modules=1 resources=2
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [project_id](variables.tf#L17) | Project ID. | <code>string</code> | ✓ | |
| [envgroups](variables.tf#L36) | Environment groups (NAME => [HOSTNAMES]). | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>null</code> |
| [environments](variables.tf#L42) | Environments. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string, &#34;Terraform-managed&#34;&#41;&#10; node_config &#61; optional&#40;object&#40;&#123;&#10; min_node_count &#61; optional&#40;number&#41;&#10; max_node_count &#61; optional&#40;number&#41;&#10; current_aggregate_node_count &#61; number&#10; &#125;&#41;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; envgroups &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> |
| [instances](variables.tf#L58) | Instance. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string, &#34;Terraform-managed&#34;&#41;&#10; region &#61; string&#10; environments &#61; list&#40;string&#41;&#10; psa_ip_cidr_range &#61; string&#10; disk_encryption_key &#61; optional&#40;string&#41;&#10; consumer_accept_list &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> |
| [organization](variables.tf#L22) | Apigee organization. If set to null the organization must already exist. | <code title="object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string, &#34;Terraform-managed&#34;&#41;&#10; authorized_network &#61; optional&#40;string&#41;&#10; runtime_type &#61; optional&#40;string, &#34;CLOUD&#34;&#41;&#10; billing_type &#61; optional&#40;string&#41;&#10; database_encryption_key &#61; optional&#40;string&#41;&#10; analytics_region &#61; optional&#40;string, &#34;europe-west1&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [envgroups](outputs.tf#L32) | Environment groups. | |
| [environments](outputs.tf#L37) | Environment. | |
| [instances](outputs.tf#L42) | Instances | |
| [org_id](outputs.tf#L22) | Organization ID. | |
| [org_name](outputs.tf#L27) | Organization name. | |
| [organization](outputs.tf#L17) | Organization. | |
| [service_attachments](outputs.tf#L47) | Service attachments. | |
<!-- END TFDOC -->

106
modules/apigee/main.tf Normal file
View File

@ -0,0 +1,106 @@
/**
* 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.
*/
locals {
org_id = try(google_apigee_organization.organization[0].id, "organizations/${var.project_id}")
envgroups = coalesce(var.envgroups, {})
environments = coalesce(var.environments, {})
instances = coalesce(var.instances, {})
}
resource "google_apigee_organization" "organization" {
count = var.organization == null ? 0 : 1
analytics_region = var.organization.analytics_region
project_id = var.project_id
authorized_network = var.organization.authorized_network
billing_type = var.organization.billing_type
runtime_type = var.organization.runtime_type
runtime_database_encryption_key_name = var.organization.database_encryption_key
}
resource "google_apigee_envgroup" "envgroups" {
for_each = local.envgroups
name = each.key
hostnames = each.value
org_id = local.org_id
}
resource "google_apigee_environment" "environments" {
for_each = local.environments
name = each.key
display_name = each.value.display_name
description = each.value.description
dynamic "node_config" {
for_each = try(each.value.node_config, null) != null ? [""] : []
content {
min_node_count = node_config.min_node_count
max_node_count = node_config.max_node_count
current_aggregate_node_count = node_config.current_aggregate_node_count
}
}
org_id = local.org_id
}
resource "google_apigee_envgroup_attachment" "envgroup_attachments" {
for_each = merge(concat([for k1, v1 in local.environments : {
for v2 in v1.envgroups : "${k1}-${v2}" => {
environment = k1
envgroup = v2
}
}])...)
envgroup_id = try(google_apigee_envgroup.envgroups[each.value.envgroup].id, each.value.envgroup)
environment = google_apigee_environment.environments[each.value.environment].name
}
resource "google_apigee_environment_iam_binding" "binding" {
for_each = merge(concat([for k1, v1 in local.environments : {
for k2, v2 in coalesce(v1.iam, {}) : "${k1}-${k2}" => {
environment = "${k1}"
role = k2
members = v2
}
}])...)
org_id = local.org_id
env_id = google_apigee_environment.environments[each.value.environment].name
role = each.value.role
members = each.value.members
}
resource "google_apigee_instance" "instances" {
for_each = local.instances
name = each.key
display_name = each.value.display_name
description = each.value.description
location = each.value.region
org_id = local.org_id
ip_range = each.value.psa_ip_cidr_range
disk_encryption_key_name = each.value.disk_encryption_key
consumer_accept_list = each.value.consumer_accept_list
}
resource "google_apigee_instance_attachment" "instance_attachments" {
for_each = merge(concat([for k1, v1 in local.instances : {
for v2 in v1.environments :
"${k1}-${v2}" => {
instance = k1
environment = v2
}
}])...)
instance_id = google_apigee_instance.instances[each.value.instance].id
environment = try(google_apigee_environment.environments[each.value.environment].name,
"${local.org_id}/environments/${each.value.environment}")
}

50
modules/apigee/outputs.tf Normal file
View File

@ -0,0 +1,50 @@
/**
* 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.
*/
output "organization" {
description = "Organization."
value = try(google_apigee_organization.organization[0], null)
}
output "org_id" {
description = "Organization ID."
value = local.org_id
}
output "org_name" {
description = "Organization name."
value = try(google_apigee_organization.organization[0].name, var.project_id)
}
output "envgroups" {
description = "Environment groups."
value = try(google_apigee_envgroup.envgroups, null)
}
output "environments" {
description = "Environment."
value = try(google_apigee_environment.environments, null)
}
output "instances" {
description = "Instances"
value = try(google_apigee_instance.instances, null)
}
output "service_attachments" {
description = "Service attachments."
value = { for k, v in google_apigee_instance.instances : k => v.service_attachment }
}

View File

@ -0,0 +1,70 @@
/**
* 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.
*/
variable "project_id" {
description = "Project ID."
type = string
}
variable "organization" {
description = "Apigee organization. If set to null the organization must already exist."
type = object({
display_name = optional(string)
description = optional(string, "Terraform-managed")
authorized_network = optional(string)
runtime_type = optional(string, "CLOUD")
billing_type = optional(string)
database_encryption_key = optional(string)
analytics_region = optional(string, "europe-west1")
})
default = null
}
variable "envgroups" {
description = "Environment groups (NAME => [HOSTNAMES])."
type = map(list(string))
default = null
}
variable "environments" {
description = "Environments."
type = map(object({
display_name = optional(string)
description = optional(string, "Terraform-managed")
node_config = optional(object({
min_node_count = optional(number)
max_node_count = optional(number)
current_aggregate_node_count = number
}))
iam = optional(map(list(string)))
envgroups = list(string)
}))
default = null
}
variable "instances" {
description = "Instance."
type = map(object({
display_name = optional(string)
description = optional(string, "Terraform-managed")
region = string
environments = list(string)
psa_ip_cidr_range = string
disk_encryption_key = optional(string)
consumer_accept_list = optional(list(string))
}))
default = null
}

View File

@ -25,6 +25,7 @@ locals {
"dataflow" : ["dataflow", "compute"]
}
_service_accounts_robot_services = {
apigee = "service-%s@gcp-sa-apigee"
artifactregistry = "service-%s@gcp-sa-artifactregistry"
bq = "bq-%s@bigquery-encryption"
cloudasset = "service-%s@gcp-sa-cloudasset"
@ -67,6 +68,7 @@ locals {
}
)
service_accounts_jit_services = [
"apigee.googleapis.com",
"artifactregistry.googleapis.com",
"cloudasset.googleapis.com",
"gkehub.googleapis.com",

View File

@ -14,19 +14,15 @@
* limitations under the License.
*/
module "apigee-x-instance" {
source = "../../../../modules/apigee-x-instance"
name = var.name
region = var.region
ip_range = var.ip_range
apigee_org_id = "my-project"
apigee_environments = [
"eval1",
"eval2"
]
consumer_accept_list = [
"project1",
"project2"
]
module "test" {
source = "../../../../../blueprints/cloud-operations/apigee"
project_create = var.project_create
project_id = var.project_id
organization = var.organization
envgroups = var.envgroups
environments = var.environments
instances = var.instances
path = var.path
datastore_name = var.datastore_name
psc_config = var.psc_config
}

View File

@ -0,0 +1,23 @@
project_create = {
billing_account_id = "12345-12345-12345"
parent = "folders/123456789"
}
project_id = "my-project"
envgroups = {
test = ["test.cool-demos.space"]
}
environments = {
apis-test = {
envgroups = ["test"]
}
}
instances = {
instance-ew1 = {
region = "europe-west1"
environments = ["apis-test"]
psa_ip_cidr_range = "10.0.4.0/22"
}
}
psc_config = {
europe-west1 = "10.0.0.0/28"
}

View File

@ -0,0 +1,124 @@
/**
* 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.
*/
/**
* 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.
*/
variable "project_create" {
description = "Parameters for the creation of the new project."
type = object({
billing_account_id = string
parent = string
})
default = null
}
variable "vpc_create" {
description = "Boolean flag indicating whether the VPC should be created or not."
type = bool
default = true
}
variable "project_id" {
description = "Project ID."
type = string
nullable = false
}
variable "organization" {
description = "Apigee organization."
type = object({
display_name = optional(string, "Apigee organization created by tf module")
description = optional(string, "Apigee organization created by tf module")
authorized_network = optional(string, "vpc")
runtime_type = optional(string, "CLOUD")
billing_type = optional(string)
database_encryption_key = optional(string)
analytics_region = optional(string, "europe-west1")
})
nullable = false
default = {
}
}
variable "envgroups" {
description = "Environment groups (NAME => [HOSTNAMES])."
type = map(list(string))
nullable = false
}
variable "environments" {
description = "Environments."
type = map(object({
display_name = optional(string)
description = optional(string)
node_config = optional(object({
min_node_count = optional(number)
max_node_count = optional(number)
current_aggregate_node_count = number
}))
iam = optional(map(list(string)))
envgroups = list(string)
}))
nullable = false
}
variable "instances" {
description = "Instance."
type = map(object({
display_name = optional(string)
description = optional(string)
region = string
environments = list(string)
psa_ip_cidr_range = string
disk_encryption_key = optional(string)
consumer_accept_list = optional(list(string))
}))
nullable = false
}
variable "path" {
description = "Bucket path."
type = string
default = "/analytics"
nullable = false
}
variable "datastore_name" {
description = "Datastore"
type = string
nullable = false
default = "gcs"
}
variable "psc_config" {
description = "PSC configuration."
type = map(string)
nullable = false
}

View File

@ -0,0 +1,21 @@
# 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.
import collections
def test_blueprint(recursive_e2e_plan_runner):
"Test that all blueprint resources are created."
count_modules, count_resources = recursive_e2e_plan_runner(tf_var_file='test.regular.tfvars')
assert count_modules == 10
assert count_resources == 60

View File

@ -74,11 +74,11 @@ def plan_runner(_plan_runner):
def e2e_plan_runner(_plan_runner):
"Returns a function to run Terraform plan on an end-to-end fixture."
def run_plan(fixture_path=None, targets=None, refresh=True,
include_bare_resources=False, **tf_vars):
def run_plan(fixture_path=None, tf_var_file=None, targets=None,
refresh=True, include_bare_resources=False, **tf_vars):
"Runs Terraform plan on an end-to-end module using defaults, returns data."
plan = _plan_runner(fixture_path, targets=targets, refresh=refresh,
**tf_vars)
plan = _plan_runner(fixture_path, tf_var_file=tf_var_file, targets=targets,
refresh=refresh, **tf_vars)
# skip the fixture
root_module = plan.root_module['child_modules'][0]
modules = dict((mod['address'], mod['resources'])
@ -106,12 +106,12 @@ def recursive_e2e_plan_runner(_plan_runner):
for module in new_modules:
walk_plan(module, modules, resources)
def run_plan(fixture_path=None, targets=None, refresh=True,
def run_plan(fixture_path=None, tf_var_file=None, targets=None, refresh=True,
include_bare_resources=False, compute_sums=True, tmpdir=True,
**tf_vars):
"Runs Terraform plan on a root module using defaults, returns data."
plan = _plan_runner(fixture_path, targets=targets, refresh=refresh,
tmpdir=tmpdir, **tf_vars)
plan = _plan_runner(fixture_path, tf_var_file=tf_var_file, targets=targets,
refresh=refresh, tmpdir=tmpdir, **tf_vars)
modules = []
resources = []
walk_plan(plan.root_module, modules, resources)

View File

@ -14,17 +14,11 @@
* limitations under the License.
*/
variable "name" {
type = string
default = "my-test-instance"
module "test" {
source = "../../../../modules/apigee"
project_id = var.project_id
organization = var.organization
envgroups = var.envgroups
environments = var.environments
instances = var.instances
}
variable "region" {
type = string
default = "europe-west1"
}
variable "ip_range" {
type = string
default = "10.0.0.0/22,10.1.0.0/28"
}

View File

@ -0,0 +1,41 @@
project_id = "my-project"
organization = {
display_name = "My Organization"
description = "My Organization"
authorized_network = "my-vpc"
runtime_type = "CLOUD"
billing_type = "Pay-as-you-go"
database_encryption_key = "123456789"
analytics_region = "europe-west1"
}
envgroups = {
test = ["test.example.com"]
prod = ["prod.example.com"]
}
environments = {
apis-test = {
display_name = "APIs test"
description = "APIs Test"
envgroups = ["test"]
}
apis-prod = {
display_name = "APIs prod"
description = "APIs prod"
envgroups = ["prod"]
iam = {
"roles/viewer" = ["group:devops@myorg.com"]
}
}
}
instances = {
instance-test-ew1 = {
region = "europe-west1"
environments = ["apis-test"]
psa_ip_cidr_range = "10.0.4.0/22"
}
instance-prod-ew1 = {
region = "europe-west1"
environments = ["apis-prod"]
psa_ip_cidr_range = "10.0.4.0/22"
}
}

View File

@ -0,0 +1,8 @@
project_id = "my-project"
environments = {
apis-test = {
display_name = "APIs test"
description = "APIs Test"
envgroups = ["test"]
}
}

View File

@ -0,0 +1,4 @@
project_id = "my-project"
envgroups = {
test = ["test.example.com"]
}

View File

@ -0,0 +1,8 @@
project_id = "my-project"
instances = {
instance-test-ew1 = {
region = "europe-west1"
environments = ["apis-test"]
psa_ip_cidr_range = "10.0.4.0/22"
}
}

View File

@ -0,0 +1,26 @@
project_id = "my-project"
organization = {
display_name = "My Organization"
description = "My Organization"
authorized_network = "my-vpc"
runtime_type = "CLOUD"
billing_type = "PAYG"
database_encryption_key = "123456789"
analytics_region = "europe-west1"
}
envgroups = {
test = ["test.example.com"]
prod = ["prod.example.com"]
}
environments = {
apis-test = {
display_name = "APIs test"
description = "APIs Test"
envgroups = ["test"]
}
apis-prod = {
display_name = "APIs prod"
description = "APIs prod"
envgroups = ["prod"]
}
}

View File

@ -0,0 +1,10 @@
project_id = "my-project"
organization = {
display_name = "My Organization"
description = "My Organization"
authorized_network = "my-vpc"
runtime_type = "CLOUD"
billing_type = "PAYG"
database_encryption_key = "123456789"
analytics_region = "europe-west1"
}

View File

@ -0,0 +1,70 @@
/**
* 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.
*/
variable "project_id" {
description = "Project ID."
type = string
}
variable "organization" {
description = "Apigee organization"
type = object({
display_name = optional(string)
description = optional(string, "Apigee Organization created by tf module")
authorized_network = optional(string)
runtime_type = optional(string, "CLOUD")
billing_type = optional(string)
database_encryption_key = optional(string)
analytics_region = optional(string, "europe-west1")
})
default = null
}
variable "envgroups" {
description = "Environment groups (NAME => [HOSTNAMES])."
type = map(list(string))
default = null
}
variable "environments" {
description = "Environments."
type = map(object({
display_name = optional(string)
description = optional(string)
node_config = optional(object({
min_node_count = optional(number)
max_node_count = optional(number)
current_aggregate_node_count = number
}))
iam = optional(map(list(string)))
envgroups = list(string)
}))
default = null
}
variable "instances" {
description = "Instance."
type = map(object({
display_name = optional(string)
description = optional(string)
region = string
environments = list(string)
psa_ip_cidr_range = string
disk_encryption_key = optional(string)
consumer_accept_list = optional(list(string))
}))
default = null
}

View File

@ -0,0 +1,74 @@
# 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.
import collections
def test_all(plan_runner):
"Test that creates all resources."
_, resources = plan_runner(tf_var_file='test.all.tfvars')
counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources)
assert counts == {
'google_apigee_organization.organization': 1,
'google_apigee_envgroup.envgroups': 2,
'google_apigee_environment.environments': 2,
'google_apigee_envgroup_attachment.envgroup_attachments': 2,
'google_apigee_instance.instances': 2,
'google_apigee_instance_attachment.instance_attachments': 2,
'google_apigee_environment_iam_binding.binding': 1
}
def test_organization_only(plan_runner):
"Test that creates only an organization."
_, resources = plan_runner(tf_var_file='test.organization_only.tfvars')
counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources)
assert counts == {
'google_apigee_organization.organization': 1
}
def test_envgroup_only(plan_runner):
"Test that creates only an environment group in an existing organization."
_, resources = plan_runner(tf_var_file='test.envgroup_only.tfvars')
counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources)
assert counts == {
'google_apigee_envgroup.envgroups': 1,
}
def test_env_only(plan_runner):
"Test that creates an environment in an existing environment group."
_, resources = plan_runner(tf_var_file='test.env_only.tfvars')
counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources)
assert counts == {
'google_apigee_environment.environments': 1,
'google_apigee_envgroup_attachment.envgroup_attachments': 1,
}
def test_instance_only(plan_runner):
"Test that creates only an instance."
_, resources = plan_runner(tf_var_file='test.instance_only.tfvars')
counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources)
assert counts == {
'google_apigee_instance.instances': 1,
'google_apigee_instance_attachment.instance_attachments': 1
}
def test_no_instances(plan_runner):
"Test that creates everything but the instances."
_, resources = plan_runner(tf_var_file='test.no_instances.tfvars')
counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources)
assert counts == {
'google_apigee_organization.organization': 1,
'google_apigee_envgroup.envgroups': 2,
'google_apigee_environment.environments': 2,
'google_apigee_envgroup_attachment.envgroup_attachments': 2,
}

View File

@ -1,46 +0,0 @@
/**
* 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.
*/
module "test" {
source = "../../../../modules/apigee-organization"
project_id = "my-project"
analytics_region = var.analytics_region
runtime_type = "CLOUD"
billing_type = "EVALUATION"
authorized_network = var.network
apigee_environments = {
eval1 = {
api_proxy_type = "PROGRAMMABLE"
deployment_type = "PROXY"
}
eval2 = {
api_proxy_type = "CONFIGURABLE"
deployment_type = "ARCHIVE"
}
eval3 = {}
}
apigee_envgroups = {
eval = {
environments = [
"eval1",
"eval2"
]
hostnames = [
"eval.api.example.com"
]
}
}
}

View File

@ -1,60 +0,0 @@
# 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.
import pytest
@pytest.fixture
def resources(plan_runner):
_, resources = plan_runner()
return resources
def test_resource_count(resources):
"Test number of resources created."
assert len(resources) == 7
def test_envgroup_attachment(resources):
"Test Apigee Envgroup Attachments."
attachments = [r['values'] for r in resources if r['type']
== 'google_apigee_envgroup_attachment']
assert len(attachments) == 2
assert set(a['environment'] for a in attachments) == set(['eval1', 'eval2'])
def test_envgroup(resources):
"Test env group."
envgroups = [r['values'] for r in resources if r['type']
== 'google_apigee_envgroup']
assert len(envgroups) == 1
assert envgroups[0]['name'] == 'eval'
assert len(envgroups[0]['hostnames']) == 1
assert envgroups[0]['hostnames'][0] == 'eval.api.example.com'
def test_env(resources):
"Test environments."
envs = [r['values'] for r in resources if r['type']
== 'google_apigee_environment']
assert len(envs) == 3
assert envs[0]['name'] == 'eval1'
assert envs[0]['api_proxy_type'] == 'PROGRAMMABLE'
assert envs[0]['deployment_type'] == 'PROXY'
assert envs[1]['name'] == 'eval2'
assert envs[1]['api_proxy_type'] == 'CONFIGURABLE'
assert envs[1]['deployment_type'] == 'ARCHIVE'
assert envs[2]['name'] == 'eval3'
assert envs[2]['api_proxy_type'] == 'API_PROXY_TYPE_UNSPECIFIED'
assert envs[2]['deployment_type'] == 'DEPLOYMENT_TYPE_UNSPECIFIED'

View File

@ -1,55 +0,0 @@
# 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.
import pytest
@pytest.fixture
def resources(plan_runner):
_, resources = plan_runner()
return resources
def test_resource_count(resources):
"Test number of resources created."
assert len(resources) == 3
def test_instance_attachment(resources):
"Test Apigee Instance Attachments."
attachments = [
r['values']
for r in resources
if r['type'] == 'google_apigee_instance_attachment'
]
assert len(attachments) == 2
assert set(a['environment'] for a in attachments) == set(['eval1', 'eval2'])
def test_instance(resources):
"Test Instance."
instances = [
r['values'] for r in resources if r['type'] == 'google_apigee_instance'
]
assert len(instances) == 1
assert instances[0]['ip_range'] == '10.0.0.0/22,10.1.0.0/28'
assert instances[0]['name'] == 'my-test-instance'
assert instances[0]['location'] == 'europe-west1'
def test_instance_consumer_accept_list(resources):
instances = [
r['values'] for r in resources if r['type'] == 'google_apigee_instance'
]
assert instances[0]['consumer_accept_list'] == ['project1', 'project2']