Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cloud nat template #429

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions community/cloud-foundation/templates/cloud_nat/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Cloud NAT

This template creates a Cloud NAT.

## Prerequisites

- Install [gcloud](https://cloud.google.com/sdk)
- Create a [GCP project, set up billing, enable requisite APIs](../project/README.md)
- Create a [network](../network/README.md)
- Grant the [compute.networkAdmin](https://cloud.google.com/compute/docs/access/iam) IAM role to the project service account

## Deployment

### Resources

- [compute.v1.router](https://cloud.google.com/compute/docs/reference/rest/beta/routers)

### Properties

See the `properties` section in the schema file(s):
- [Cloud NAT](cloud_nat.py.schema)

### Usage

1. Clone the [Cloud Foundation Toolkit Template repository](https://github.com/GoogleCloudPlatform/deploymentmanager-samples):

```
git clone https://github.com/GoogleCloudPlatform/deploymentmanager-samples.git
```

2. Go to the [templates](templates/) directory:

```
cd templates
```

3. Copy the example DM config to be used as a model for the deployment; in this case, [examples/cloud_nat.yaml](examples/cloud_nat.yaml):

```
cp templates/cloud_nat/examples/cloud_nat.yaml my_cloud_nat.yaml
```

4. Change the values in the config file to match your specific GCP setup (for properties, refer to the schema files listed above):

```
vim my_cloud_nat.yaml # <== change values to match your GCP setup
```

5. Create your deployment (replace <YOUR_DEPLOYMENT_NAME> with the relevant deployment name):

```
gcloud deployment-manager deployments create <YOUR_DEPLOYMENT_NAME> \
--config my_cloud_nat.yaml
```

6. In case you need to delete your deployment:

```
gcloud deployment-manager deployments delete <YOUR_DEPLOYMENT_NAME>
```

## Examples

- [Cloud NAT](examples/cloud_nat.yaml)
98 changes: 98 additions & 0 deletions community/cloud-foundation/templates/cloud_nat/cloud_nat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
""" This template creates a Cloud NAT. """
MIN_PORTS_PER_VM = 64
UDP_IDLE_TIMEOUT_SEC = 30
ICMP_IDLE_TIMEOUT_SEC = 30
TCP_ESTABLISHED_IDLE_TIMEOUT_SEC = 1200
TCP_TRANSITORY_IDLE_TIMEOUT_SEC = 30

def get_nat_ip_option_enum(natips):
""" Returns one of the two supported Enum for Nat IP Option
AUTO_ONLY or MANUAL_ONLY """
if natips:
return 'MANUAL_ONLY'
return 'AUTO_ONLY'

def generate_config(context):
""" Entry point for the deployment resources. """

name = context.properties.get('name', context.env['name'])

resources = [
{
'name': context.env['name'],
# compute.v1.router seems to be doing the job though
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://cloud.google.com/compute/docs/reference/rest/v1/routers Managed NAT got GA, so itś part of compute.v1

# doc says compute.beta has the NAT feature.
'type': 'compute.v1.router',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please use gcp-types/compute-v1:routers as type, we are moving towards the GCP types.

'properties':
{
'name':
name,
'network':
generate_network_url(
context,
context.properties['network']
),
'region':
context.properties['region'],
'nats':
[{
'name': name,
# Force using All subnet all primary IP range by default
# Force using one of the two enums below:
# ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES and
# ALL_SUBNETWORKS_ALL_IP_RANGES
'sourceSubnetworkIpRangesToNat': context.properties.get(
'sourceSubnetworkIpRangesToNat',
'ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES'),
'natIps': context.properties.get('natIps', []),
'natIpAllocateOption': get_nat_ip_option_enum(
context.properties.get('natIps')),
# A min of 64, anything below will
# still be translated to 64 ports
'minPortsPerVm': context.properties.get(
'minPortsPerVm', MIN_PORTS_PER_VM),
'udpIdleTimeoutSec': context.properties.get(
'udpIdleTimeoutSec', UDP_IDLE_TIMEOUT_SEC),
'icmpIdleTimeoutSec': context.properties.get(
'icmpIdleTimeoutSec', ICMP_IDLE_TIMEOUT_SEC),
'tcpEstablishedIdleTimeoutSec': context.properties.get(
'tcpEstablishedIdleTimeoutSec',
TCP_ESTABLISHED_IDLE_TIMEOUT_SEC),
'tcpTransitoryIdleTimeoutSec': context.properties.get(
'tcpTransitoryIdleTimeoutSec',
TCP_TRANSITORY_IDLE_TIMEOUT_SEC)
}]
}
}
]

return {
'resources':
resources,
'outputs':
[
{
'name': 'name',
'value': name
},
{
'name': 'selfLink',
'value': '$(ref.' + context.env['name'] + '.selfLink)'
},
{
'name':
'nats',
'value':
'$(ref.' + context.env['name'] + '.nats)'
}
]
}


def generate_network_url(context, network):
"""Format the resource name as a resource URI."""

return 'projects/{}/global/networks/{}'.format(
context.env['project'],
network
)
81 changes: 81 additions & 0 deletions community/cloud-foundation/templates/cloud_nat/cloud_nat.py.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
info:
title: Cloud Nat
author: [email protected]
description: |
Deploys a Cloud Nat.

For more information on this resource:
https://cloud.google.com/nat/docs/overview

imports:
- path: cloud_nat.py

required:
- network
- region
- name
- sourceSubnetworkIpRangesToNat

properties:
name:
type: string
description: The name of Cloud Nat the resource.
network:
type: string
description: The name of the network to which the Cloud Nat belongs.
region:
type: string
description: The URI of the region where the Cloud Nat resides.
sourceSubnetworkIpRangesToNat:
type: string
description: IP Range inside the VPC effected by the nats service.
enum:
- ALL_SUBNETWORKS_ALL_IP_RANGES
- ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES
minPortsPerVm:
type: number
description: |
Minimum number of ports allocated to a VM from this NAT config,
any setting belong 64 will be automatically converted to 64
udpIdleTimeoutSec:
type: number
description: Timeout for UDP connections in seconds
default: 30
icmpIdleTimeoutSec:
type: number
description: Timeout for ICMP connections in seconds
default: 30
tcpEstablishedIdleTimeoutSec:
type: number
description: Timeout for established TCP connections in seconds
default: 1200
tcpTransitoryIdleTimeoutSec:
type: number
description: Timeout for TCP connections in seconds
default: 30
natIps:
type: array
description: Static External IPs used as the NAT IPs.
items:
type: string
description: Static External IP self link

outputs:
properties:
- name:
type: string
description: The name of the Cloud Nat resource.
- selfLink:
type: string
description: The URI (SelfLink) of the Cloud Nat resource.
- nat:
type: object
description: |
Object containing NAT information.
https://cloud.google.com/compute/docs/reference/rest/beta/routers

documentation:
- templates/cloud_nat/README.md

examples:
- templates/cloud_nat/examples/cloud_nat.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Example of the Cloud NAT template usage.
# Replace <FIXME:> with your resource names

imports:
- path: templates/cloud_nat/cloud_nat.py
name: cloud_nat.py

resources:
- name: cloud-nat
type: cloud_nat.py
properties:
name: nat
network: <FIXME:YOURNETWORKNAME>
region: us-east1
sourceSubnetworkIpRangesToNat: ALL_SUBNETWORKS_ALL_IP_RANGES
minPortsPerVm: 64
udpIdleTimeoutSec: 30
icmpIdleTimeoutSec: 30
tcpEstablishedIdleTimeoutSec: 1200
tcpTransitoryIdleTimeoutSec: 30
natIps:
- projects/<FIXME:YOURPROJECTNAME>/regions/us-east1/addresses/<FIXME:YOURIPNAME>
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env bats

source tests/helpers.bash

TEST_NAME=$(basename "${BATS_TEST_FILENAME}" | cut -d '.' -f 1)

# Create a random 10-char string and save it in a file.
RANDOM_FILE="/tmp/${CLOUD_FOUNDATION_ORGANIZATION_ID}-${TEST_NAME}.txt"
if [[ ! -e "${RANDOM_FILE}" ]]; then
RAND=$(head /dev/urandom | LC_ALL=C tr -dc a-z0-9 | head -c 10)
echo ${RAND} > "${RANDOM_FILE}"
fi

# Set variables based on the random string saved in the file.
# envsubst requires all variables used in the example/config to be exported.
if [[ -e "${RANDOM_FILE}" ]]; then
export RAND=$(cat "${RANDOM_FILE}")
DEPLOYMENT_NAME="${CLOUD_FOUNDATION_PROJECT_ID}-${TEST_NAME}-${RAND}"
# Replace underscores with dashes in the deployment name.
DEPLOYMENT_NAME=${DEPLOYMENT_NAME//_/-}
CONFIG=".${DEPLOYMENT_NAME}.yaml"
fi

########## HELPER FUNCTIONS ##########

function create_config() {
echo "Creating ${CONFIG}"
envsubst < "templates/cloud_nat/tests/integration/${TEST_NAME}.yaml" > "${CONFIG}"
}

function delete_config() {
echo "Deleting ${CONFIG}"
rm -f "${CONFIG}"
}

function setup() {
# Global setup; executed once per test file.
if [ ${BATS_TEST_NUMBER} -eq 1 ]; then
gcloud compute networks create network-${RAND} \
--project "${CLOUD_FOUNDATION_PROJECT_ID}" \
--description "integration test ${RAND}" \
--subnet-mode custom
gcloud compute addresses create ip-${RAND} \
--region us-east1 \
--project "${CLOUD_FOUNDATION_PROJECT_ID}"
create_config
fi

# Per-test setup steps.
}

function teardown() {
# Global teardown; executed once per test file.
if [[ "$BATS_TEST_NUMBER" -eq "${#BATS_TEST_NAMES[@]}" ]]; then
gcloud compute networks delete network-${RAND} \
--project "${CLOUD_FOUNDATION_PROJECT_ID}" -q
gcloud compute addresses delete ip-${RAND} -q --region us-east1
rm -f "${RANDOM_FILE}"
delete_config
fi

# Per-test teardown steps.
}


@test "Creating deployment ${DEPLOYMENT_NAME} from ${CONFIG}" {
gcloud deployment-manager deployments create "${DEPLOYMENT_NAME}" \
--config ${CONFIG} \
--project "${CLOUD_FOUNDATION_PROJECT_ID}"
}

@test "Verifying that NATS were created in deployment ${DEPLOYMENT_NAME}" {
run gcloud compute routers nats describe nat-"${RAND}" --router=cloud-nat-"${RAND}" --region=us-east1 --project "${CLOUD_FOUNDATION_PROJECT_ID}"
echo '---Verification Output Start---'
echo "$output"
echo '---Verification Output Complete---'
[[ "$output" =~ "icmpIdleTimeoutSec: 60" ]]
[[ "$output" =~ "minPortsPerVm: 96" ]]
[[ "$output" =~ "name: nat-${RAND}" ]]
[[ "$output" =~ "tcpEstablishedIdleTimeoutSec: 1140" ]]
[[ "$output" =~ "tcpTransitoryIdleTimeoutSec: 60" ]]
[[ "$output" =~ "udpIdleTimeoutSec: 55" ]]
[[ "$output" =~ "/regions/us-east1/addresses/ip-${RAND}" ]]
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add the test from HA NAT which actually creates an pivate instance and able to reach the internet?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I would be happy to see an instance using the managed NAT ( and maybe one not using it) and verifying it's internet connectivity via SSH.

@test "Deleting deployment" {
gcloud deployment-manager deployments delete "${DEPLOYMENT_NAME}" \
--project "${CLOUD_FOUNDATION_PROJECT_ID}" -q

run gcloud compute nats list --project "${CLOUD_FOUNDATION_PROJECT_ID}"
[[ ! "$output" =~ "name: cloud-nat-${RAND}" ]]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Test of the Cloud Router template usage.
#
# Variables:
# RAND: a random string used by the testing suite.
#

imports:
- path: templates/cloud_nat/cloud_nat.py
name: cloud_nat.py

resources:
- name: cloud-nat-${RAND}
type: cloud_nat.py
properties:
name: nat-${RAND}
network: network-${RAND}
region: us-east1
# ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES
sourceSubnetworkIpRangesToNat: ALL_SUBNETWORKS_ALL_IP_RANGES
minPortsPerVm: 96
udpIdleTimeoutSec: 55
icmpIdleTimeoutSec: 60
tcpEstablishedIdleTimeoutSec: 1140
tcpTransitoryIdleTimeoutSec: 60
natIps:
- projects/${CLOUD_FOUNDATION_PROJECT_ID}/regions/us-east1/addresses/ip-${RAND}