Skip to content
Permalink
Browse files

add support for specifying dns zone, and disabling it. Closes #536

  • Loading branch information...
tj committed May 9, 2018
1 parent 27fb78a commit 534930d09367fa76bf841326d8ec05e30076ed81
Showing with 108 additions and 10 deletions.
  1. +26 −4 config/stages.go
  2. +40 −3 config/stages_test.go
  3. +30 −1 docs/04-configuration.md
  4. +12 −2 platform/lambda/stack/resources/resources.go
@@ -16,10 +16,11 @@ var defaultStages = []string{

// Stage config.
type Stage struct {
Domain string `json:"domain"`
Path string `json:"path"`
Cert string `json:"cert"`
Name string `json:"-"`
Domain string `json:"domain"`
Zone interface{} `json:"zone"`
Path string `json:"path"`
Cert string `json:"cert"`
Name string `json:"-"`
StageOverrides
}

@@ -39,6 +40,20 @@ func (s *Stage) Validate() error {
return errors.Wrap(err, ".name")
}

switch s.Zone.(type) {
case bool, string:
return nil
default:
return errors.Errorf(".zone is an invalid type, must be string or boolean")
}
}

// Default implementation.
func (s *Stage) Default() error {
if s.Zone == nil {
s.Zone = true
}

return nil
}

@@ -73,6 +88,13 @@ func (s Stages) Default() error {
s.Name = name
}

// defaults
for _, s := range s {
if err := s.Default(); err != nil {
return errors.Wrapf(err, "stage %q", s.Name)
}
}

return nil
}

@@ -54,7 +54,7 @@ func TestStages_Default(t *testing.T) {
t.Run("no custom stages", func(t *testing.T) {
s := Stages{}

assert.NoError(t, s.Default(), "validate")
assert.NoError(t, s.Default(), "default")
assert.NoError(t, s.Validate(), "validate")

assert.Len(t, s, 3)
@@ -67,11 +67,12 @@ func TestStages_Default(t *testing.T) {
"beta": &Stage{},
}

assert.NoError(t, s.Default(), "validate")
assert.NoError(t, s.Default(), "default")
assert.NoError(t, s.Validate(), "validate")

assert.Len(t, s, 4)
assert.Equal(t, "beta", s["beta"].Name)
assert.Equal(t, true, s["beta"].Zone)
})
}

@@ -91,11 +92,47 @@ func TestStages_Validate(t *testing.T) {
},
}

assert.NoError(t, s.Default(), "validate")
assert.NoError(t, s.Default(), "default")
assert.NoError(t, s.Validate(), "validate")
assert.Equal(t, "staging", s["staging"].Name)
assert.Equal(t, "production", s["production"].Name)
})

t.Run("valid zone boolean", func(t *testing.T) {
s := Stages{
"production": &Stage{
Domain: "gh-polls.com",
Zone: false,
},
}

assert.NoError(t, s.Default(), "default")
assert.NoError(t, s.Validate(), "validate")
})

t.Run("valid zone string", func(t *testing.T) {
s := Stages{
"production": &Stage{
Domain: "api.gh-polls.com",
Zone: "api.gh-polls.com",
},
}

assert.NoError(t, s.Default(), "default")
assert.NoError(t, s.Validate(), "validate")
})

t.Run("invalid zone type", func(t *testing.T) {
s := Stages{
"production": &Stage{
Domain: "api.gh-polls.com",
Zone: 123,
},
}

assert.NoError(t, s.Default(), "default")
assert.EqualError(t, s.Validate(), `stage "production": .zone is an invalid type, must be string or boolean`)
})
}

func TestStages_List(t *testing.T) {
@@ -675,11 +675,40 @@ You may also provide an optional base path, for example to prefix your API with
}
```


Plan the changes via `up stack plan` and `up stack apply` to perform the changes. You may [purchase domains](#guides.development_to_production_workflow.purchasing_a_domain) from the command-line, or map custom domains from other registrars. Up uses Route53 to purchase domains using your AWS account credit card. See `up help domains`.

Note: CloudFront can take up to ~40 minutes to distribute this configuration the first time, so grab a coffee while these changes are applied. Also note that ACM certificates are always created in the Virginia (us-east-1) region due to how API Gateway interoperates with CloudFront.

### DNS Zones

By default when you specify a stage `domain` — such as "api.example.com" — a DNS zone is created in Route53 for the top level domain "example.com", and an ALIAS record "api.example.com" is added to this zone.

If you're using external DNS and wish to omit the zone entirely you can disable it with the `zone` property:

```json
{
"stages": {
"production": {
"domain": "gh-polls.com",
"zone": false
}
}
}
```

You may also explicitly specify the zone by providing a string. In the following example an "api.gh-polls.com" zone will be created, instead of putting the record in "gh-polls.com".

```json
{
"stages": {
"production": {
"domain": "api.gh-polls.com",
"zone": "api.gh-polls.com"
}
}
}
```

## Stage Overrides

Up allows some configuration properties to be overridden at the stage level. The following example illustrates how you can tune lambda memory and hooks per-stage.
@@ -277,7 +277,10 @@ func stageDomain(c *Config, s *config.Stage, m Map, deploymentID string) {
}

stagePathMapping(c, s, m, deploymentID, id)
stageDNSRecord(c, s, m, id)

if s.Zone != false {
stageDNSRecord(c, s, m, id)
}
}

// stagePathMapping sets up the stage deployment mapping.
@@ -299,7 +302,14 @@ func stagePathMapping(c *Config, s *config.Stage, m Map, deploymentID, domainID
// stageDNSRecord sets up an ALIAS record and zone if necessary for a custom domain.
func stageDNSRecord(c *Config, s *config.Stage, m Map, domainID string) {
id := util.Camelcase("dns_zone_%s_record_%s", util.Domain(s.Domain), s.Domain)
zone := dnsZone(c, m, util.Domain(s.Domain))
zoneName := util.Domain(s.Domain)

// explicit .zone was specified
if s, ok := s.Zone.(string); ok {
zoneName = s
}

zone := dnsZone(c, m, zoneName)

m[id] = Map{
"Type": "AWS::Route53::RecordSet",

0 comments on commit 534930d

Please sign in to comment.
You can’t perform that action at this time.