Skip to content

Commit

Permalink
Merge pull request #440 from joesondow/ASGARD-217-Route53-self-servic…
Browse files Browse the repository at this point in the history
…e-DNS

ASGARD-217 - Route53 DNS management
  • Loading branch information
joesondow committed Dec 13, 2013
2 parents af714bc + eea1073 commit e3632f5
Show file tree
Hide file tree
Showing 15 changed files with 1,394 additions and 6 deletions.
229 changes: 229 additions & 0 deletions grails-app/controllers/com/netflix/asgard/HostedZoneController.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/*
* Copyright 2013 Netflix, Inc.
*
* 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.
*/
package com.netflix.asgard

import com.amazonaws.services.route53.model.AliasTarget
import com.amazonaws.services.route53.model.ChangeInfo
import com.amazonaws.services.route53.model.HostedZone
import com.amazonaws.services.route53.model.RRType
import com.amazonaws.services.route53.model.ResourceRecord
import com.amazonaws.services.route53.model.ResourceRecordSet
import com.amazonaws.services.route53.model.ResourceRecordSetFailover
import com.amazonaws.services.route53.model.ResourceRecordSetRegion
import grails.converters.JSON
import grails.converters.XML

/**
* Used to interact with Route53 Hosted Zones for DNS management.
*/
class HostedZoneController {

def awsRoute53Service

static editActions = ['prepareResourceRecordSet']

/**
* Lists all the Route53 DNS hosted zones in the account.
*/
def list() {
Collection<HostedZone> hostedZones = awsRoute53Service.getHostedZones()
withFormat {
html { [hostedZones: hostedZones] }
xml { new XML(hostedZones).render(response) }
json { new JSON(hostedZones).render(response) }
}
}

/**
* Shows the details of one Route53 DNS hosted zone, including the related resource record sets.
*/
def show() {
String hostedZoneIdOrName = params.id
UserContext userContext = UserContext.of(request)
HostedZone hostedZone = awsRoute53Service.getHostedZone(userContext, hostedZoneIdOrName)
if (!hostedZone) {
Requests.renderNotFound('Hosted Zone', hostedZoneIdOrName, this)
return
}

List<ResourceRecordSet> resourceRecordSets = awsRoute53Service.getResourceRecordSets(userContext, hostedZone.id)
resourceRecordSets.sort { it.name }
String deletionWarning = "Really delete Hosted Zone '${hostedZone.id}' with name '${hostedZone.name}' and " +
"its ${resourceRecordSets.size()} resource record set${resourceRecordSets.size() == 1 ? '' : 's'}?" +
(resourceRecordSets.size() ? "\n\nThis cannot be undone and could be dangerous." : '')
Map result = [hostedZone: hostedZone, resourceRecordSets: resourceRecordSets]
Map guiVars = result + [deletionWarning: deletionWarning]
withFormat {
html { guiVars }
xml { new XML(result).render(response) }
json { new JSON(result).render(response) }
}
}

/**
* Displays a form to create a new hosted zone.
*/
def create() {

}

/**
* Handles submission of a create form, to make a new Route53 DNS hosted zone.
*
* @param cmd the command object containing the user parameters for creating the new hosted zone
*/
def save(HostedZoneSaveCommand cmd) {
if (cmd.hasErrors()) {
chain(action: 'create', model: [cmd: cmd], params: params)
return
}
UserContext userContext = UserContext.of(request)
try {
HostedZone hostedZone = awsRoute53Service.createHostedZone(userContext, cmd.name, cmd.comment)
flash.message = "Hosted Zone '${hostedZone.id}' with name '${hostedZone.name}' has been created."
redirect(action: 'show', id: hostedZone.id)
} catch (Exception e) {
flash.message = e.message ?: e.cause?.message
chain(action: 'create', model: [cmd: cmd], params: params)
}
}

/**
* Deletes a Route53 DNS hosted zone, including all of its resource record sets.
*/
def delete = {
UserContext userContext = UserContext.of(request)
String id = params.id
HostedZone hostedZone = awsRoute53Service.getHostedZone(userContext, id)
if (hostedZone) {
ChangeInfo changeInfo = awsRoute53Service.deleteHostedZone(userContext, id)
flash.message = "Deletion of Hosted Zone '${id}' with name '${hostedZone.name}' has started. " +
"ChangeInfo: ${changeInfo}"
redirect([action: 'result'])
} else {
Requests.renderNotFound('Hosted Zone', id, this)
}
}

/**
* Renders a simple page showing the result of a deletion.
*/
def result() { render view: '/common/result' }

/**
* Displays a form to create a new resource record set for the specified Route53 DNS hosted zone.
*/
def prepareResourceRecordSet() {
[
hostedZoneId: params.id ?: params.hostedZoneId,
types: RRType.values()*.toString().sort(),
failoverValues: ResourceRecordSetFailover.values()*.toString().sort(),
resourceRecordSetRegions: ResourceRecordSetRegion.values()*.toString().sort()
]
}

/**
* Creates a new resource record set for an existing Route53 DNS hosted zone.
*
* @param cmd the command object containing all the parameters for creating the new resource record set
*/
def addResourceRecordSet(ResourceRecordSetCommand cmd) {

if (cmd.hasErrors()) {
chain(action: 'prepareResourceRecordSet', model: [cmd: cmd], params: params)
} else {
UserContext userContext = UserContext.of(request)
String id = cmd.hostedZoneId
String comment = cmd.comment
ResourceRecordSet recordSet = resourceRecordSetFromCommandObject(cmd)
try {
ChangeInfo changeInfo = awsRoute53Service.createResourceRecordSet(userContext, id, recordSet, comment)
flash.message = "DNS CREATE change submitted. ChangeInfo: ${changeInfo}"
redirect(action: 'show', id: id)
} catch (Exception e) {
flash.message = "Could not add resource record set: ${e}"
chain(action: 'prepareResourceRecordSet', model: [cmd: cmd], params: params)
}
}
}

/**
* Deletes a resource record set from a Route53 DNS hosted zone.
*
* @param cmd the command object enough parameters to identify and delete a distinct resource record set
*/
def removeResourceRecordSet(ResourceRecordSetCommand cmd) {
if (cmd.hasErrors()) {
chain(action: 'show', id: id)
} else {
UserContext userContext = UserContext.of(request)
String id = cmd.hostedZoneId
String comment = cmd.comment
ResourceRecordSet recordSet = resourceRecordSetFromCommandObject(cmd)
try {
ChangeInfo changeInfo = awsRoute53Service.deleteResourceRecordSet(userContext, id, recordSet, comment)
flash.message = "DNS DELETE change submitted. ChangeInfo: ${changeInfo}"
} catch (Exception e) {
flash.message = "Could not delete resource record set: ${e}"
}
redirect(action: 'show', id: id)
}
}

private resourceRecordSetFromCommandObject(ResourceRecordSetCommand cmd) {
String hostedZoneId = cmd.hostedZoneId
List<String> resourceRecordStrings = Requests.ensureList(cmd.resourceRecords?.split('\n')).collect { it.trim() }
String aliasTarget = cmd.aliasTarget
new ResourceRecordSet(
name: cmd.resourceRecordSetName,
type: cmd.type,
setIdentifier: cmd.setIdentifier ?: null,
weight: cmd.weight ?: null,
region: cmd.resourceRecordSetRegion ?: null,
failover: cmd.failover ?: null,
tTL: cmd.ttl ?: null,
resourceRecords: resourceRecordStrings.collect { new ResourceRecord(it) } ?: null,
aliasTarget: aliasTarget ? new AliasTarget(hostedZoneId, aliasTarget) : null,
healthCheckId: cmd.healthCheckId ?: null
)
}
}

/**
* User parameters for creating a new hosted zone.
*/
class HostedZoneSaveCommand {
String name
String comment
}

/**
* The parameters for creating or deleting a resource record set.
*/
class ResourceRecordSetCommand {
String hostedZoneId
String resourceRecordSetName
String type // From enum RRType
String setIdentifier
Long weight
String resourceRecordSetRegion // From enum ResourceRecordSetRegion
String failover // From ResourceRecordSetFailover
Long ttl
String resourceRecords
String aliasTarget
String healthCheckId
String comment
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.amazonaws.services.cloudwatch.AmazonCloudWatchClient
import com.amazonaws.services.ec2.AmazonEC2Client
import com.amazonaws.services.elasticloadbalancing.AmazonElasticLoadBalancingClient
import com.amazonaws.services.rds.AmazonRDSClient
import com.amazonaws.services.route53.AmazonRoute53Client
import com.amazonaws.services.s3.AmazonS3Client
import com.amazonaws.services.simpledb.AmazonSimpleDBClient
import com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflowClient
Expand All @@ -31,6 +32,7 @@ import com.netflix.asgard.mock.MockAmazonCloudWatchClient
import com.netflix.asgard.mock.MockAmazonEC2Client
import com.netflix.asgard.mock.MockAmazonElasticLoadBalancingClient
import com.netflix.asgard.mock.MockAmazonRDSClient
import com.netflix.asgard.mock.MockAmazonRoute53Client
import com.netflix.asgard.mock.MockAmazonS3Client
import com.netflix.asgard.mock.MockAmazonSimpleDBClient
import com.netflix.asgard.mock.MockAmazonSimpleWorkflowClient
Expand Down Expand Up @@ -66,6 +68,7 @@ class AwsClientService implements InitializingBean {
AmazonElasticLoadBalancing: concrete(AmazonElasticLoadBalancingClient,
MockAmazonElasticLoadBalancingClient),
AmazonRDS: concrete(AmazonRDSClient, MockAmazonRDSClient),
AmazonRoute53: concrete(AmazonRoute53Client, MockAmazonRoute53Client),
AmazonS3: concrete(AmazonS3Client, MockAmazonS3Client),
AmazonSimpleDB: concrete(AmazonSimpleDBClient, MockAmazonSimpleDBClient),
AmazonSimpleWorkflow: concrete(AmazonSimpleWorkflowClient, MockAmazonSimpleWorkflowClient),
Expand Down
Loading

0 comments on commit e3632f5

Please sign in to comment.