One place for hosting & domains

      Manage

      How To Deploy and Manage Your DNS using OctoDNS on Debian 10


      The author selected the Electronic Frontier Foundation to receive a donation as part of the Write for DOnations program.

      Introduction

      OctoDNS is an infrastructure-as-code tool that allows you to deploy and manage your DNS zones using standard software development principles, including version control, testing, and automated deployment. OctoDNS was created by GitHub and is written in Python.

      Using OctoDNS eliminates many of the pitfalls of manual DNS management, as zone files are stored in a structured format (YAML). This allows you to deploy zones to multiple DNS providers simultaneously, identify syntax errors, and push out your DNS configuration automatically, reducing the risk of human error. Another common usage of OctoDNS is to synchronize your DNS configuration between different providers, such as a testing and production system, or between live and failover environments.

      OctoDNS is similar to DNSControl, which is an equivalent tool created by Stack Exchange and written in Go. Unlike OctoDNS, DNSControl uses a JavaScript-based configuration language for defining DNS zones, which allows you to use advanced programmatic features such as loops to specify multiple similar records within the same zone. The article How to Deploy and Manage Your DNS Using DNSControl on Debian 10 covers the basic setup and configuration of DNSControl.

      In this tutorial, you’ll install and configure OctoDNS, create a basic DNS configuration, and begin deploying DNS records to a live provider. As part of this tutorial, we will use DigitalOcean as the example DNS provider. If you wish to use a different provider, the setup is very similar. When you’re finished, you’ll be able to manage and test your DNS configuration in a safe, offline environment, and then automatically deploy it to production.

      Prerequisites

      Before you begin this guide you’ll need the following:

      • One Debian 10 server set up by following the Initial Server Setup with Debian 10, including a sudo non-root user and enabled firewall to block non-essential ports. your-server-ipv4-address and your-server-ipv6-address refer to the IP addresses of the server where you’re hosting your website or domain.
      • A fully registered domain name with DNS hosted by a supported provider. This tutorial will use your-domain throughout and DigitalOcean as the service provider.
      • A DigitalOcean API key (Personal Access Token) with read and write permissions. To create one, visit How to Create a Personal Access Token.

      Once you have these ready, log in to your server as your non-root user to begin.

      Step 1 — Installing OctoDNS

      OctoDNS is distributed as a Python pip package, and runs in a Python Virtual Environment (virtualenv), so you’ll start this step by installing the packages required for this. A virtualenv is an isolated Python environment that can have its own libraries and configuration, separate from the main system-wide Python installation. Python and virtualenv are available within Debian’s default software repositories, making it possible to install using conventional package management tools.

      Begin by updating the local package index to reflect any new upstream changes:

      Then, install the python and virtualenv packages:

      • sudo apt install python virtualenv

      After confirming the installation, apt will download and install Python, virtualenv, and all of their required dependencies.

      Next, you’ll create the required directories for OctoDNS, where your DNS and program configuration will be stored. Start by creating the ~/octodns and ~/octodns/config directories:

      • mkdir ~/octodns ~/octodns/config

      Now move into ~/octodns:

      Next, you need to create the Python Virtual Environment—an isolated Python environment with its own libraries and configuration to run OctoDNS in:

      Activate your environment with the following command:

      This will output something similar to the following:

      Output

      Running virtualenv with interpreter /usr/bin/python2 New python executable in /home/user/octodns/env/bin/python2 Also creating executable in /home/user/octodns/env/bin/python Installing setuptools, pkg_resources, pip, wheel...done.

      Your Bash shell prompt will now also be prefixed with the name of the virtual environment. This shows that you are currently operating within the virtualenv:

      (env) user@digitalocean:~/octodns$
      

      If you wish to exit the virtualenv, you can use the deactivate command at any time. However, you should stay in your virtualenv to continue with this tutorial.

      Now that you’ve installed and configured Python and virtualenv, you can install OctoDNS. OctoDNS is distributed as a Python pip package, which is the standard package-management tool for Python packages and libraries.

      You can install the OctoDNS pip package using the following command within your virtualenv:

      Once this is complete, you can check the installed version to make sure that everything is working:

      Your output will look similar to the following:

      Output

      octoDNS 0.9.9

      If you see a octodns-sync: command not found error, double-check that you’re still inside your virtualenv.

      Now that you’ve installed OctoDNS, you can create the required configuration files to connect OctoDNS to your DNS provider to allow it to make changes to your DNS records.

      Step 2 — Configuring OctoDNS

      In this step, you’ll create the required configuration files for OctoDNS, and connect it to your DNS provider so that it can begin to make live changes to your DNS records.

      Note: This tutorial will focus on the initial setup of OctoDNS; however for production use it is recommended to store your OctoDNS configuration in a version control system (VCS) such as Git. The advantages of this include full version control, integration with CI/CD for testing, seamlessly rolling-back deployments, and so on.

      Firstly, you need to configure the config.yaml file, which defines the DNS zones for OctoDNS to manage, and allows it to authenticate to your DNS provider and make changes.

      The format of config.yaml differs slightly depending on the DNS provider that you are using. Please see the Supported Providers list in the official OctoDNS documentation to find the configuration for your own provider. When viewing this hyperlink, the configuration details are presented as a code comment in the actual Python code for your provider, which is linked in the ‘Provider’ column of the table. Once you have found the Python code for your provider, such as cloudflare.py or route53.py, the relevant code comment can be found directly under the class ProviderNameProvider. For example:

      Excerpt of octodns/provider/route53.py

      class Route53Provider(BaseProvider):
        '''
        AWS Route53 Provider
        route53:
            class: octodns.provider.route53.Route53Provider
            # The AWS access key id
            access_key_id:
            # The AWS secret access key
            secret_access_key:
            # The AWS session token (optional)
            # Only needed if using temporary security credentials
            session_token:
      

      Move into the ~/octodns/config directory:

      Then create and open config.yaml for editing:

      Add the sample config.yaml configuration for your DNS provider to the file. If you’re using DigitalOcean as your DNS provider, you can use the following:

      ~/octodns/config/config.yaml

      ---
      providers:
        config:
          class: octodns.provider.yaml.YamlProvider
          directory: ./config
          default_ttl: 300
          enforce_order: True
        digitalocean:
          class: octodns.provider.digitalocean.DigitalOceanProvider
          token: your-digitalocean-oauth-token
      
      zones:
        your-domain.:
          sources:
            - config
          targets:
            - digitalocean
      

      This file tells OctoDNS which DNS providers you want it to connect to, and which DNS zones it should manage for those providers.

      You’ll need to provide some form of authentication for your DNS provider. This is usually an API key or OAuth token.

      If you do not wish to store your access token in plain text in the configuration file, you can instead pass it as an environment variable when the program runs. To do this, you should use the following token: line instead in config.yaml:

      ~/octodns/config/config.yaml

      token: env/DIGITALOCEAN_OAUTH_TOKEN
      

      Then, before running OctoDNS, set the relevant environment variable to your access token, and OctoDNS will read it from there when run:

      • export DIGITALOCEAN_OAUTH_TOKEN=your-digitalocean-oauth-token

      Warning: This token will grant access to your DNS provider account, so you should protect it as you would a password. Also, ensure that if you’re using a version control system, either the file containing the token is excluded (e.g. using .gitignore), or is securely encrypted in some way.

      If you’re using DigitalOcean as your DNS provider, you can use the required OAuth token in your DigitalOcean account settings that you generated as part of the prerequisites.

      If you have multiple different DNS providers—for example, for multiple domain names, or delegated DNS zones—you can define these all in the same config.yaml file.

      You’ve set up the initial OctoDNS configuration file to allow the program to authenticate to your DNS provider and make changes. Next you’ll create the configuration for your DNS zones.

      Step 3 — Creating a DNS Configuration File

      In this step, you’ll create an initial DNS configuration file, which will contain the DNS records for your domain name or delegated DNS zone.

      Each DNS zone that you want to manage using OctoDNS has its own file, for example your-domain.yaml. In this file, the DNS records for the zone are defined using YAML.

      To begin, move into the ~/octodns/config directory:

      Then create and open your-domain.yaml for editing:

      Add the following sample configuration to the file:

      ~/octodns/config/your-domain.yaml

      ---
      '':
        - type: A
          value: your-server-ipv4-address
      
      www:
        - type: A
          value: your-server-ipv4-address
      

      This sample file defines a DNS zone for your-domain with two A records, pointing to the IPv4 address that you’re hosting your domain or website on. One A record is for the root domain (e.g. your-domain), and the other is for the www subdomain (e.g. www.your-domain).

      Once complete, save and close the file.

      You’ve set up a basic DNS zone configuration file for OctoDNS, with two basic A records pointing to the IPv4 address of your domain or website. Next, you’ll expand the file with some useful DNS records.

      Step 4 — Populating Your DNS Configuration File

      Next, you can populate the DNS configuration file with a practical set of DNS records for your website or service, using the YAML structured configuration language.

      Unlike traditional BIND zone files, where DNS records are written in a raw, line-by-line format, DNS records within OctoDNS are defined as YAML keys and subkeys with a number of associated values, as shown briefly in Step 3.

      The top-level key is usually the 'name', which is essentially the record identifier. www, subdomain1, and mail are all examples of DNS 'name'. In OctoDNS, there are two special-use names, which are '', for the root record (usually referred to as @), and '*', for wildcard records. A required value of each key (DNS record) is type. This defines which type of DNS record you are defining within that YAML top-level key. A type exists for each of the standard DNS record types, including A, AAAA, MX, TXT, NS, CNAME, and so on. A full list of available record types is available in the Records section of the OctoDNS documentation.

      The values for your DNS records are defined either directly as values to the top-level keys (if you only have one value), or as a list (if you have multiple values, e.g. multiple IP addresses or MX addresses).

      For example, to define a single value, you could use the following configuration:

      ~/octodns/config/your-domain.yaml

      'www':
        type: A
        value: 203.0.113.1
      

      Alternatively, to define multiple values for a single record:

      ~/octodns/config/your-domain.yaml

      'www':
        type: A
        values:
        - 203.0.113.1
        - 203.0.113.2
      

      The syntax for setting DNS records varies slightly for each record type. Following are some examples for the most common record types:

      A records:

      Purpose: To point to an IPv4 address.

      Syntax:

      'name':
        type: A
        value: ipv4-address
      

      Example:

      'www':
        type: A
        value: your-server-ipv4-address
      

      AAAA records:

      Purpose: To point to an IPv6 address.

      Syntax:

      'name':
        type: AAAA
        value: ipv6-address
      

      Example:

      'www':
        type: AAAA
        value: your-server-ipv6-address
      

      CNAME records:

      Purpose: To make your domain/subdomain an alias of another.

      Syntax:

      'name':
        type: CNAME
        value: fully-qualified-domain-name
      

      Example:

      'www':
        type: CNAME
        value: www.example.org
      

      MX records:

      Purpose: To direct email to specific servers/addresses.

      Syntax:

      'name':
        type: MX
        value:
          exchange: mail-server
          preference: priority-value
      

      Note that a trailing . must be included if there are any dots in the MX value.

      Example:

      '':
        type: MX
        value:
          exchange: mail.your-domain.
          preference: 10
      

      TXT records:

      Purpose: To add arbitrary plain text, often used for configurations without their own dedicated record type.

      Syntax:

      'name':
        type: TXT
        value: content
      

      Example:

      '':
        type: TXT
        value: This is a TXT record.
      

      In order to begin adding DNS records for your domain or delegated DNS zone, edit your DNS configuration file:

      • cd ~/octodns/config
      • nano your-domain.yaml

      Next, you can begin populating your DNS zone using the syntax described in the previous list, as well as the Records section of the official OctoDNS documentation.

      For reference, the code block here contains a full sample configuration for an initial DNS setup:

      ~/octodns/config/your-domain.yaml

      ---
      '':
        - type: A
          value: your-server-ipv4-address
      
        - type: AAAA
          value: your-server-ipv6-address
      
        - type: MX
          value:
            exchange: mail.your-domain.
            preference: 10
      
        - type: TXT
          value: v=spf1 -all
      
      _dmarc:
        type: TXT
        value: v=DMARC1; p=reject; rua=mailto:abuse@your-domain; aspf=s; adkim=s;
      
      mail:
        - type: A
          value: your-server-ipv4-address
      
        - type: AAAA
          value: your-server-ipv6-address
      
      www:
        - type: A
          value: your-server-ipv4-address
      
        - type: AAAA
          value: your-server-ipv6-address
      

      Once you have completed your initial DNS configuration, save and close the file.

      In this step, you set up the initial DNS configuration file, containing your DNS records. Next, you will test the configuration and deploy it.

      Step 5 — Testing and Deploying Your DNS Configuration

      In this step, you will run a local syntax check on your DNS configuration, and then deploy the changes to the live DNS server/provider.

      Firstly, move into your octodns directory:

      Double check that you’re still operating within your Python virtualenv by looking for the name of it before your Bash prompt:

      (env) user@digitalocean:~/octodns$
      

      Next, use the octodns-validate command to check the syntax of your configuration file(s). You’ll need to specify the path to your configuration file:

      • octodns-validate --config=./config/config.yaml

      If the YAML syntax of your DNS configuration file is correct, OctoDNS will return with no output. If you see an error or warning in your output, OctoDNS will provide details on what and where the error is located within your YAML file.

      Next, you can perform a dry-run push of the DNS configuration, which will output which changes will be made, without actually making them:

      • octodns-sync --config=./config/config.yaml

      This should produce an output similar to the following:

      Output

      ******************************************************************************** * your-domain. ******************************************************************************** * digitalocean (DigitalOceanProvider) * Create <ARecord A 300, mail.your-domain., ['your-server-ipv4-address']> (config) * Create <AaaaRecord AAAA 300, mail.your-domain., ['your-server-ipv6-address']> (config) * Create <TxtRecord TXT 300, your-domain., ['v=spf1 -all']> (config) * Create <AaaaRecord AAAA 300, your-domain., ['your-server-ipv6-address']> (config) * Create <ARecord A 300, your-domain., ['your-server-ipv4-address']> (config) * Create <ARecord A 300, www.your-domain., ['your-server-ipv4-address']> (config) * Create <MxRecord MX 300, your-domain., [''10 mail.your-domain.'']> (config) * Create <TxtRecord TXT 300, _dmarc.your-domain., ['v=DMARC1; p=reject; rua=mailto:abuse@your-domain; aspf=s; adkim=s;']> (config) * Create <AaaaRecord AAAA 300, www.your-domain., ['your-server-ipv6-address']> (config) * Summary: Creates=9, Updates=0, Deletes=0, Existing Records=2 ********************************************************************************

      Warning: The next command will make live changes to your DNS records and possibly other settings. Please ensure that you are prepared for this, including taking a backup of your existing DNS configuration, as well as ensuring that you have the means to roll back if needed.

      Finally, you can push out the changes to your live DNS provider:

      • octodns-sync --config=./config/config.yaml --doit

      Note: In some cases, OctoDNS will refuse to push changes if it is making a significant number of adjustments. This is an automatic protection feature to prevent accidental misconfigurations. If you encounter this refusal, you can re-run octodns-sync using the --force option, but please ensure you are ready to do so.

      You’ll see an output like the dry-run earlier in this step, but with the addition of something similar to the following:

      Output

      2019-07-07T23:17:27 INFO DigitalOceanProvider[digitalocean] apply: making changes 2019-07-07T23:17:30 INFO Manager sync: 9 total changes

      Now, if you check the DNS settings for your domain in the DigitalOcean control panel, you’ll see the changes.

      A screenshot of the DigitalOcean control panel, showing some of the DNS changes that OctoDNS has made.

      You can also check the record creation by running a DNS query for your domain/delegated zone using dig.

      If you don’t have dig installed, you’ll need to install the dnsutils package:

      • sudo apt install dnsutils

      Once you’ve installed dig, you can use it to make a DNS lookup for your domain. You’ll see that the records have been updated accordingly:

      You’ll see output showing the IP address and relevant DNS record from your zone that you deployed using OctoDNS. DNS records can take some time to propagate, so you may need to wait and run this command again.

      In this final step, you ran a local syntax check of the DNS configuration file, then deployed it to your live DNS provider, and tested that the changes were made successfully.

      Conclusion

      In this article you set up OctoDNS and deployed a DNS configuration to a live provider. Now you can manage and test your DNS configuration changes in a safe, offline environment before deploying them to production.

      If you wish to explore this subject further, OctoDNS is designed to be integrated into your CI/CD pipeline, allowing you to run in-depth tests and have more control over your deployment to production. You could also look into integrating OctoDNS into your infrastructure build/deployment processes, allowing you to deploy servers and add them to DNS completely automatically.

      If you wish to go further with OctoDNS, the following DigitalOcean articles provide some interesting next steps to help integrate OctoDNS into your change management and infrastructure deployment workflows:



      Source link

      How To Deploy and Manage Your DNS Using DNSControl on Debian 10


      The author selected the Electronic Frontier Foundation Inc to receive a donation as part of the Write for DOnations program.

      Introduction

      DNSControl is an infrastructure-as-code tool that allows you to deploy and manage your DNS zones using standard software development principles, including version control, testing, and automated deployment. DNSControl was created by Stack Exchange and is written in Go.

      Using DNSControl eliminates many of the pitfalls of manual DNS management, as zone files are stored in a programmable format. This allows you to deploy zones to multiple DNS providers simultaneously, identify syntax errors, and push out your DNS configuration automatically, reducing the risk of human error. Another common usage of DNSControl is to quickly migrate your DNS to a different provider; for example, in the event of a DDoS attack or system outage.

      In this tutorial, you’ll install and configure DNSControl, create a basic DNS configuration, and begin deploying DNS records to a live provider. As part of this tutorial, we will use DigitalOcean as the example DNS provider. If you wish to use a different provider, the setup is very similar. When you’re finished, you’ll be able to manage and test your DNS configuration in a safe, offline environment, and then automatically deploy it to production.

      Prerequisites

      Before you begin this guide you’ll need the following:

      • One Debian 10 server set up by following the Initial Server Setup with Debian 10, including a sudo non-root user and enabled firewall to block non-essential ports. your-server-ipv4-address refers to the IP address of the server where you’re hosting your website or domain. your-server-ipv6-address refers to the IPv6 address of the server where you’re hosting your website or domain.
      • A fully registered domain name with DNS hosted by a supported provider. This tutorial will use your_domain throughout and DigitalOcean as the service provider.
      • A DigitalOcean API key (Personal Access Token) with read and write permissions. To create one, visit How to Create a Personal Access Token.

      Once you have these ready, log in to your server as your non-root user to begin.

      Step 1 — Installing DNSControl

      DNSControl is written in Go, so you’ll start this step by installing Go to your server and setting your GOPATH.

      Go is available within Debian’s default software repositories, making it possible to install using conventional package management tools.

      You’ll also need to install Git, as this is required to allow Go to download and install the DNSControl software from it’s repository on GitHub.

      Begin by updating the local package index to reflect any new upstream changes:

      Then, install the golang-go and git packages:

      • sudo apt install golang-go git

      After confirming the installation, apt will download and install Go and Git, as well as all of their required dependencies.

      Next, you’ll configure the required path environment variables for Go. If you would like to know more about this, you can read this tutorial on Understanding the GOPATH. Start by editing the ~/.profile file:

      Add the following lines to the very end of your file:

      ~/.profile

      ...
      export GOPATH="$HOME/go"
      export PATH="$PATH:$GOPATH/bin"
      

      Once you have added these lines to the bottom of the file, save and close it. Then reload your profile by either logging out and back in, or sourcing the file again:

      Now you’ve installed and configured Go, you can install DNSControl.

      The go get command can be used to fetch a copy of the code, automatically compile it, and install it into your Go directory:

      • go get github.com/StackExchange/dnscontrol

      Once this is complete, you can check the installed version to make sure that everything is working:

      Your output will look similar to the following:

      Output

      dnscontrol 2.9-dev

      If you see a dnscontrol: command not found error, double-check your Go path setup.

      Now that you’ve installed DNSControl, you can create a configuration directory and connect DNSControl to your DNS provider in order to allow it to make changes to your DNS records.

      Step 2 — Configuring DNSControl

      In this step, you’ll create the required configuration directories for DNSControl, and connect it to your DNS provider so that it can begin to make live changes to your DNS records.

      Firstly, create a new directory in which you can store your DNSControl configuration, and then move into it:

      • mkdir ~/dnscontrol
      • cd ~/dnscontrol

      Note: This tutorial will focus on the initial set up of DNSControl; however for production use it is recommended to store your DNSControl configuration in a version control system (VCS) such as Git. The advantages of this include full version control, integration with CI/CD for testing, seamlessly rolling-back deployments, and so on.

      If you plan to use DNSControl to write BIND zone files, you should also create the zones directory:

      BIND zone files are a raw, standardized method for storing DNS zones/records in plain text format. They were originally used for the BIND DNS server software, but are now widely adopted as the standard method for storing DNS zones. BIND zone files produced by DNSControl are useful if you want to import them to a custom or self-hosted DNS server, or for auditing purposes.

      However, if you just want to use DNSControl to push DNS changes to a managed provider, the zones directory will not be needed.

      Next, you need to configure the creds.json file, which is what will allow DNSControl to authenticate to your DNS provider and make changes. The format of creds.json differs slightly depending on the DNS provider that you are using. Please see the Service Providers list in the official DNSControl documentation to find the configuration for your own provider.

      Create the file creds.json in the ~/dnscontrol directory:

      • cd ~/dnscontrol
      • nano creds.json

      Add the sample creds.json configuration for your DNS provider to the file. If you’re using DigitalOcean as your DNS provider, you can use the following:

      ~/dnscontrol/creds.json

      {
      "digitalocean": {
        "token": "your-digitalocean-oauth-token"
      }
      }
      

      This file tells DNSControl to which DNS providers you want it to connect.

      You’ll need to provide some form of authentication for your DNS provider. This is usually an API key or OAuth token, but some providers require extra information, as documented in the Service Providers list in the official DNSControl documentation.

      Warning: This token will grant access to your DNS provider account, so you should protect it as you would a password. Also, ensure that if you’re using a version control system, either the file containing the token is excluded (e.g. using .gitignore), or is securely encrypted in some way.

      If you’re using DigitalOcean as your DNS provider, you can use the required OAuth token in your DigitalOcean account settings that you generated as part of the prerequisites.

      If you have multiple different DNS providers—for example, for multiple domain names, or delegated DNS zones—you can define these all in the same creds.json file.

      You’ve set up the initial DNSControl configuration directories, and configured creds.json to allow DNSControl to authenticate to your DNS provider and make changes. Next you’ll create the configuration for your DNS zones.

      Step 3 — Creating a DNS Configuration File

      In this step, you’ll create an initial DNS configuration file, which will contain the DNS records for your domain name or delegated DNS zone.

      dnsconfig.js is the main DNS configuration file for DNSControl. In this file, DNS zones and their corresponding records are defined using JavaScript syntax. This is known as a DSL, or Domain Specific Language. The JavaScript DSL page in the official DNSControl documentation provides further details.

      To begin, create the DNS configuration file in the ~/dnscontrol directory:

      • cd ~/dnscontrol
      • nano dnsconfig.js

      Then, add the following sample configuration to the file:

      ~/dnscontrol/dnsconfig.js

      // Providers:
      
      var REG_NONE = NewRegistrar('none', 'NONE');
      var DNS_DIGITALOCEAN = NewDnsProvider('digitalocean', 'DIGITALOCEAN');
      
      // Domains:
      
      D('your_domain', REG_NONE, DnsProvider(DNS_DIGITALOCEAN),
        A('@', 'your-server-ipv4-address')
      );
      

      This sample file defines a domain name or DNS zone at a particular provider, which in this case is your_domain hosted by DigitalOcean. An example A record is also defined for the zone root (@), pointing to the IPv4 address of the server that you’re hosting your domain/website on.

      There are three main functions that make up a basic DNSControl configuration file:

      • NewRegistrar(name, type, metadata): defines the domain registrar for your domain name. DNSControl can use this to make required changes, such as modifying the authoritative nameservers. If you only want to use DNSControl to manage your DNS zones, this can generally be left as NONE.

      • NewDnsProvider(name, type, metadata): defines a DNS service provider for your domain name or delegated zone. This is where DNSControl will push the DNS changes that you make.

      • D(name, registrar, modifiers): defines a domain name or delegated DNS zone for DNSControl to manage, as well as the DNS records present in the zone.

      You should configure NewRegistrar(), NewDnsProvider(), and D() accordingly using the Service Providers list in the official DNSControl documentation.

      If you’re using DigitalOcean as your DNS provider, and only need to be able to make DNS changes (rather than authoritative nameservers as well), the sample in the preceding code block is already correct.

      Once complete, save and close the file.

      In this step, you set up a DNS configuration file for DNSControl, with the relevant providers defined. Next, you’ll populate the file with some useful DNS records.

      Step 4 — Populating Your DNS Configuration File

      Next, you can populate the DNS configuration file with useful DNS records for your website or service, using the DNSControl syntax.

      Unlike traditional BIND zone files, where DNS records are written in a raw, line-by-line format, DNS records within DNSControl are defined as a function parameter (domain modifier) to the D() function, as shown briefly in Step 3.

      A domain modifier exists for each of the standard DNS record types, including A, AAAA, MX, TXT, NS, CAA, and so on. A full list of available record types is available in the Domain Modifiers section of the DNSControl documentation.

      Modifiers for individual records are also available (record modifiers). Currently these are primarily used for setting the TTL (time to live) of individual records. A full list of available record modifiers is available in the Record Modifiers section of the DNSControl documentation. Record modifiers are optional, and in most basic use cases can be left out.

      The syntax for setting DNS records varies slightly for each record type. Following are some examples for the most common record types:

      • A records:

        • Purpose: To point to an IPv4 address.
        • Syntax: A('name', 'address', optional record modifiers)
        • Example: A('@', 'your-server-ipv4-address', TTL(30))
      • AAAA records:

        • Purpose: To point to an IPv6 address.
        • Syntax: AAAA('name', 'address', optional record modifiers)
        • Example: AAAA('@', 'your-server-ipv6-address') (record modifier left out, so default TTL will be used)
      • CNAME records:

        • Purpose: To make your domain/subdomain an alias of another.
        • Syntax: CNAME('name', 'target', optional record modifiers)
        • Example: CNAME('subdomain1', 'example.org.') (note that a trailing . must be included if there are any dots in the value)
      • MX records:

        • Purpose: To direct email to specific servers/addresses.
        • Syntax: MX('name', 'priority', 'target', optional record modifiers)
        • Example: MX('@', 10, 'mail.example.net') (note that a trailing . must be included if there are any dots in the value)
      • TXT records:

        • Purpose: To add arbitrary plain text, often used for configurations without their own dedicated record type.
        • Syntax: TXT('name', 'content', optional record modifiers)
        • Example: TXT('@', 'This is a TXT record.')
      • CAA records:

        • Purpose: To restrict and report on Certificate Authorities (CAs) who can issue TLS certificates for your domain/subdomains.
        • Syntax: CAA('name', 'tag', 'value', optional record modifiers)
        • Example: CAA('@', 'issue', 'letsencrypt.org')

      In order to begin adding DNS records for your domain or delegated DNS zone, edit your DNS configuration file:

      Next, you can begin populating the parameters for the existing D() function using the syntax described in the previous list, as well as the Domain Modifiers section of the official DNSControl documentation. A comma (,) must be used in-between each record.

      For reference, the code block here contains a full sample configuration for a basic, initial DNS setup:

      ~/dnscontrol/dnsconfig.js

      ...
      
      D('your_domain', REG_NONE, DnsProvider(DNS_DIGITALOCEAN),
        A('@', 'your-server-ipv4-address'),
        A('www', 'your-server-ipv4-address'),
        A('mail', 'your-server-ipv4-address'),
        AAAA('@', 'your-server-ipv6-address'),
        AAAA('www', 'your-server-ipv6-address'),
        AAAA('mail', 'your-server-ipv6-address'),
        MX('@', 10, 'mail.your_domain.'),
        TXT('@', 'v=spf1 -all'),
        TXT('_dmarc', 'v=DMARC1; p=reject; rua=mailto:abuse@your_domain; aspf=s; adkim=s;')
      );
      

      Once you have completed your initial DNS configuration, save and close the file.

      In this step, you set up the initial DNS configuration file, containing your DNS records. Next, you will test the configuration and deploy it.

      Step 5 — Testing and Deploying Your DNS Configuration

      In this step, you will run a local syntax check on your DNS configuration, and then deploy the changes to the live DNS server/provider.

      Firstly, move into your dnscontrol directory:

      Next, use the preview function of DNSControl to check the syntax of your file, and output what changes it will make (without actually making them):

      If the syntax of your DNS configuration file is correct, DNSControl will output an overview of the changes that it will make. This should look similar to the following:

      Output

      ******************** Domain: your_domain ----- Getting nameservers from: digitalocean ----- DNS Provider: digitalocean...8 corrections #1: CREATE A your_domain your-server-ipv4-address ttl=300 #2: CREATE A www.your_domain your-server-ipv4-address ttl=300 #3: CREATE A mail.your_domain your-server-ipv4-address ttl=300 #4: CREATE AAAA your_domain your-server-ipv6-address ttl=300 #5: CREATE TXT _dmarc.your_domain "v=DMARC1; p=reject; rua=mailto:abuse@your_domain; aspf=s; adkim=s;" ttl=300 #6: CREATE AAAA www.your_domain your-server-ipv6-address ttl=300 #7: CREATE AAAA mail.your_domain your-server-ipv6-address ttl=300 #8: CREATE MX your_domain 10 mail.your_domain. ttl=300 ----- Registrar: none...0 corrections Done. 8 corrections.

      If you see an error warning in your output, DNSControl will provide details on what and where the error is located within your file.

      Warning: The next command will make live changes to your DNS records and possibly other settings. Please ensure that you are prepared for this, including taking a backup of your existing DNS configuration, as well as ensuring that you have the means to roll back if needed.

      Finally, you can push out the changes to your live DNS provider:

      You’ll see an output similar to the following:

      Output

      ******************** Domain: your_domain ----- Getting nameservers from: digitalocean ----- DNS Provider: digitalocean...8 corrections #1: CREATE TXT _dmarc.your_domain "v=DMARC1; p=reject; rua=mailto:abuse@your_domain; aspf=s; adkim=s;" ttl=300 SUCCESS! #2: CREATE A your_domain your-server-ipv4-address ttl=300 SUCCESS! #3: CREATE AAAA your_domain your-server-ipv6-address ttl=300 SUCCESS! #4: CREATE AAAA www.your_domain your-server-ipv6-address ttl=300 SUCCESS! #5: CREATE AAAA mail.your_domain your-server-ipv6-address ttl=300 SUCCESS! #6: CREATE A www.your_domain your-server-ipv4-address ttl=300 SUCCESS! #7: CREATE A mail.your_domain your-server-ipv4-address ttl=300 SUCCESS! #8: CREATE MX your_domain 10 mail.your_domain. ttl=300 SUCCESS! ----- Registrar: none...0 corrections Done. 8 corrections.

      Now, if you check the DNS settings for your domain in the DigitalOcean control panel, you’ll see the changes.

      A screenshot of the DigitalOcean control panel, showing some of the DNS changes that DNSControl has made.

      You can also check the record creation by running a DNS query for your domain/delegated zone using dig.

      If you don’t have dig installed, you’ll need to install the dnsutils package:

      • sudo apt install dnsutils

      Once you’ve installed dig, you can use it to make a DNS lookup for your domain. You’ll see that the records have been updated accordingly:

      You’ll see output showing the IP address and relevant DNS record from your zone that was deployed using DNSControl. DNS records can take some time to propagate, so you may need to wait and run this command again.

      In this final step, you ran a local syntax check of the DNS configuration file, then deployed it to your live DNS provider, and tested that the changes were made successfully.

      Conclusion

      In this article you set up DNSControl and deployed a DNS configuration to a live provider. Now you can manage and test your DNS configuration changes in a safe, offline environment before deploying them to production.

      If you wish to explore this subject further, DNSControl is designed to be integrated into your CI/CD pipeline, allowing you to run in-depth tests and have more control over your deployment to production. You could also look into integrating DNSControl into your infrastructure build/deployment processes, allowing you to deploy servers and add them to DNS completely automatically.

      If you wish to go further with DNSControl, the following DigitalOcean articles provide some interesting next steps to help integrate DNSControl into your change management and infrastructure deployment workflows:



      Source link

      How To Manage and Use MySQL Database Triggers on Ubuntu 18.04


      The author selected the the Apache Software Foundation to receive a donation as part of the Write for DOnations program.

      Introduction

      In MySQL a trigger is a user-defined SQL command that is invoked automatically during an INSERT, DELETE, or UPDATE operation. The trigger code is associated with a table and is destroyed once a table is dropped. You can specify a trigger action time and set whether it will be activated before or after the defined database event.

      Triggers have several advantages. For instance, you can use them to generate the value of a derived column during an INSERT statement. Another use case is enforcing referential integrity where you can use a trigger to save a record to multiple related tables. Other benefits include logging user actions to audit tables as well as live-copying data across different database schemas for redundancy purposes to prevent a single point of failure.

      You can also use triggers to keep validation rules at the database level. This helps in sharing the data source across multiple applications without breaking the business logic. This greatly reduces round-trips to the database server, which therefore improves the response time of your applications. Since the database server executes triggers, they can take advantage of improved server resources such as RAM and CPU.

      In this tutorial, you’ll create, use, and delete different types of triggers on your MySQL database.

      Prerequisites

      Before you begin, make sure you have the following:

      Step 1 — Creating a Sample Database

      In this step, you’ll create a sample customer database with multiple tables for demonstrating how MySQL triggers work.

      To understand more about MySQL queries read our Introduction to Queries in MySQL.

      First, log in to your MySQL server as root:

      Enter your MySQL root password when prompted and hit ENTER to continue. When you see the mysql> prompt, run the following command to create a test_db database:

      Output

      Query OK, 1 row affected (0.00 sec)

      Next, switch to the test_db with:

      Output

      Database changed

      You’ll start by creating a customers table. This table will hold the customers’ records including the customer_id, customer_name, and level. There will be two customer levels: BASIC and VIP.

      • Create table customers(customer_id BIGINT PRIMARY KEY, customer_name VARCHAR(50), level VARCHAR(50) ) ENGINE=INNODB;

      Output

      Query OK, 0 rows affected (0.01 sec)

      Now, add a few records to the customers table. To do this, run the following commands one by one:

      • Insert into customers (customer_id, customer_name, level )values('1','JOHN DOE','BASIC');
      • Insert into customers (customer_id, customer_name, level )values('2','MARY ROE','BASIC');
      • Insert into customers (customer_id, customer_name, level )values('3','JOHN DOE','VIP');

      You’ll see the following output after running each of the INSERT commands:

      Output

      Query OK, 1 row affected (0.01 sec)

      To make sure that the sample records were inserted successfully, run the SELECT command:

      Output

      +-------------+---------------+-------+ | customer_id | customer_name | level | +-------------+---------------+-------+ | 1 | JOHN DOE | BASIC | | 2 | MARY ROE | BASIC | | 3 | JOHN DOE | VIP | +-------------+---------------+-------+ 3 rows in set (0.00 sec)

      You’ll also create another table for holding related information about the customers account. The table will have a customer_id and status_notes fields.

      Run the following command:

      • Create table customer_status(customer_id BIGINT PRIMARY KEY, status_notes VARCHAR(50)) ENGINE=INNODB;

      Next, you’ll create a sales table. This table will hold sales data related to the different customers through the customer_id column:

      • Create table sales(sales_id BIGINT PRIMARY KEY, customer_id BIGINT, sales_amount DOUBLE ) ENGINE=INNODB;

      Output

      Query OK, 0 rows affected (0.01 sec)

      You’ll add sample data to the sales data in the coming steps while testing the triggers. Next, create an audit_log table to log updates made to the sales table when you implement the AFTER UPDATE trigger in Step 5:

      • Create table audit_log(log_id BIGINT PRIMARY KEY AUTO_INCREMENT, sales_id BIGINT, previous_amount DOUBLE, new_amount DOUBLE, updated_by VARCHAR(50), updated_on DATETIME ) ENGINE=INNODB;

      Output

      Query OK, 0 rows affected (0.02 sec)

      With the test_db database and the four tables in place, you’ll now move on to work with the different MySQL triggers in your database.

      Step 2 — Creating a Before Insert Trigger

      In this step, you’ll examine the syntax of a MySQL trigger before applying this logic to create a BEFORE INSERT trigger that validates the sales_amount field when data is inserted into the sales table.

      The general syntax for creating a MySQL trigger is shown in the following example:

      DELIMITER //
      CREATE TRIGGER [TRIGGER_NAME]
      [TRIGGER TIME] [TRIGGER EVENT]
      ON [TABLE]
      FOR EACH ROW
      [TRIGGER BODY]//
      DELIMITER ;
      

      The structure of the trigger includes:

      DELIMITER //: The default MySQL delimiter is ;—it’s necessary to change it to something else in order for MySQL to treat the following lines as one command until it hits your custom delimiter. In this example, the delimiter is changed to // and then the ; delimiter is redefined at the end.

      [TRIGGER_NAME]: A trigger must have a name and this is where you include the value.

      [TRIGGER TIME]: A trigger can be invoked during different timings. MySQL allows you to define if the trigger will initiate before or after a database operation.

      [TRIGGER EVENT]: Triggers are only invoked by INSERT, UPDATE, and DELETE operations. You can use any value here depending on what you want to achieve.

      [TABLE]: Any trigger that you create on your MySQL database must be associated with a table.

      FOR EACH ROW: This statement tells MySQL to execute the trigger code for every row that the trigger affects.

      [TRIGGER BODY]: The code that is executed when the trigger is invoked is called a trigger body. This can be a single SQL statement or multiple commands. Note that if you are executing multiple SQL statements on the trigger body, you must wrap them between a BEGIN...END block.

      Note: When creating the trigger body, you can use the OLD and NEW keywords to access the old and new column values entered during an INSERT, UPDATE, and DELETE operation. In a DELETE trigger, only the OLD keyword can be used (which you’ll use in Step 4).

      Now you’ll create your first BEFORE INSERT trigger. This trigger will be associated with the sales table and it will be invoked before a record is inserted to validate the sales_amount. The function of the trigger is to check if the sales_amount being inserted to the sales table is greater than 10000 and raise an error if this evaluates to true.

      Make sure you’re logged in to the MySQL server. Then, enter the following MySQL commands one by one:

      • DELIMITER //
      • CREATE TRIGGER validate_sales_amount
      • BEFORE INSERT
      • ON sales
      • FOR EACH ROW
      • IF NEW.sales_amount>10000 THEN
      • SIGNAL SQLSTATE '45000'
      • SET MESSAGE_TEXT = 'Sale has exceeded the allowed amount of 10000.';
      • END IF//
      • DELIMITER ;

      You’re using the IF...THEN...END IF statement to evaluate if the amount being supplied during the INSERT statement is within your range. The trigger is able to extract the new sales_amount value being supplied by using the NEW keyword.

      To raise a generic error message, you use the following lines to inform the user about the error:

      SIGNAL SQLSTATE '45000'
      SET MESSAGE_TEXT = 'Sale has exceeded the allowed amount of 10000.';
      

      Next, insert a record with a sales_amount of 11000 to the sales table to check if the trigger will stop the operation:

      • Insert into sales(sales_id, customer_id, sales_amount) values('1','1','11000');

      Output

      ERROR 1644 (45000): Sale has exceeded the allowed amount of 10000.

      This error shows that the trigger code is working as expected.

      Now try a new record with a value of 7500 to check if the command will be successful:

      • Insert into sales(sales_id, customer_id, sales_amount) values('1','1','7500');

      Since the value is within the recommended range, you’ll see the following output:

      Output

      Query OK, 1 row affected (0.01 sec)

      To confirm that the data was inserted run the following command:

      The output confirms that the data is in the table:

      Output

      +----------+-------------+--------------+ | sales_id | customer_id | sales_amount | +----------+-------------+--------------+ | 1 | 1 | 7500 | +----------+-------------+--------------+ 1 row in set (0.00 sec)

      In this step you’ve tested triggers to validate data before insertion into a database.

      Next, you’ll work with the AFTER INSERT trigger to save related information into different tables.

      Step 3 — Creating an After Insert Trigger

      AFTER INSERT triggers are executed when records are successfully inserted into a table. This functionality can be used to run other business-related logics automatically. For instance, in a bank application, an AFTER INSERT trigger can close a loan account when a customer finishes paying off the loan. The trigger can monitor all payments inserted to a transaction table and close the loan automatically once the loan balance is zero.

      In this step, you’ll work with your customer_status table by using an AFTER INSERT trigger to enter related customer records.

      To create the AFTER INSERT trigger, enter the following commands:

      • DELIMITER //
      • CREATE TRIGGER customer_status_records
      • AFTER INSERT
      • ON customers
      • FOR EACH ROW
      • Insert into customer_status(customer_id, status_notes) VALUES(NEW.customer_id, 'ACCOUNT OPENED SUCCESSFULLY')//
      • DELIMITER ;

      Output

      Query OK, 0 rows affected (0.00 sec)

      Here you instruct MySQL to save another record to the customer_status table once a new customer record is inserted to the customers table.

      Now, insert a new record in the customers table to confirm your trigger code will be invoked:

      • Insert into customers (customer_id, customer_name, level )values('4','DAVID DOE','VIP');

      Output

      Query OK, 1 row affected (0.01 sec)

      Since the record was inserted successfully, check that a new status record was inserted into the customer_status table:

      • Select * from customer_status;

      Output

      +-------------+-----------------------------+ | customer_id | status_notes | +-------------+-----------------------------+ | 4 | ACCOUNT OPENED SUCCESSFULLY | +-------------+-----------------------------+ 1 row in set (0.00 sec)

      The output confirms that the trigger ran successfully.

      The AFTER INSERT trigger is useful in monitoring the lifecycle of a customer. In a production environment, customers’ accounts may undergo different stages such as account opening, suspension, and closing.

      In the following steps you’ll work with UPDATE triggers.

      Step 4 — Creating a Before Update Trigger

      A BEFORE UPDATE trigger is similar to the BEFORE INSERT trigger—the difference is when they are invoked. You can use the BEFORE UPDATE trigger to check a business logic before a record is updated. To test this, you’ll use the customers table in which you’ve inserted some data already.

      You have two levels for your customers in the database. In this example, once a customer account is upgraded to the VIP level, the account can not be downgraded to the BASIC level. To enforce such a rule, you will create a BEFORE UPDATE trigger that will execute before the UPDATE statement as shown following. If a database user tries to downgrade a customer to the BASIC level from the VIP level, a user-defined exception will be triggered.

      Enter the following SQL commands one by one to create the BEFORE UPDATE trigger:

      • DELIMITER //
      • CREATE TRIGGER validate_customer_level
      • BEFORE UPDATE
      • ON customers
      • FOR EACH ROW
      • IF OLD.level='VIP' THEN
      • SIGNAL SQLSTATE '45000'
      • SET MESSAGE_TEXT = 'A VIP customer can not be downgraded.';
      • END IF //
      • DELIMITER ;

      You use the OLD keyword to capture the level that the user is supplying when running the UPDATE command. Again, you use the IF...THEN...END IF statement to signal a generic error statement to the user.

      Next, run the following SQL command that tries to downgrade a customer account associated with the customer_id of 3:

      • Update customers set level='BASIC' where customer_id='3';

      You’ll see the following output providing the SET MESSAGE_TEXT:

      Output

      ERROR 1644 (45000): A VIP customer can not be downgraded.

      If you run the same command to a BASIC level customer, and try to upgrade the account to the VIP level, the command will execute successfully:

      • Update customers set level='VIP' where customer_id='1';

      Output

      Rows matched: 1 Changed: 1 Warnings: 0

      You’ve used the BEFORE UPDATE trigger to enforce a business rule. Now you’ll move on to use an AFTER UPDATE trigger for audit logging.

      Step 5 — Creating an After Update Trigger

      An AFTER UPDATE trigger is invoked once a database record is updated successfully. This behavior makes the trigger suitable for audit logging. In a multi-user environment, the administrator may want to view a history of users updating records in a particular table for audit purposes.

      You’ll create a trigger that logs the update activity of the sales table. Our audit_log table will contain information about the MySQL users updating the sales table, the date of the update, and the new and old sales_amount values.

      To create the trigger, run the following SQL commands:

      • DELIMITER //
      • CREATE TRIGGER log_sales_updates
      • AFTER UPDATE
      • ON sales
      • FOR EACH ROW
      • Insert into audit_log(sales_id, previous_amount, new_amount, updated_by, updated_on) VALUES (NEW.sales_id,OLD.sales_amount, NEW.sales_amount,(SELECT USER()), NOW() )//
      • DELIMITER ;

      You insert a new record to the audit_log table. You use the NEW keyword to retrieve the value of the sales_id and the new sales_amount. Also, you use the OLD keyword to retrieve the previous sales_amount since you want to log both amounts for audit purposes.

      The command SELECT USER() retrieves the current user performing the operation and the NOW() statement retrieves the value of the current date and time from the MySQL server.

      Now if a user tries to update the value of any record in the sales table, the log_sales_updates trigger will insert a new record to the audit_log table.

      Let’s create a new sales record with a random sales_id of 5 and try to update it. First, insert the sales record with:

      • Insert into sales(sales_id, customer_id, sales_amount) values('5', '2','8000');

      Output

      Query OK, 1 row affected (0.00 sec)

      Next, update the record:

      • Update sales set sales_amount='9000' where sales_id='5';

      You’ll see the following output:

      Output

      Rows matched: 1 Changed: 1 Warnings: 0

      Now run the following command to verify if the AFTER UPDATE trigger was able to register a new record into the audit_log table:

      The trigger logged the update. Your output shows the previous sales_amount and new amount registered with the user that updated the records:

      Output

      +--------+----------+-----------------+------------+----------------+---------------------+ | log_id | sales_id | previous_amount | new_amount | updated_by | updated_on | +--------+----------+-----------------+------------+----------------+---------------------+ | 1 | 5 | 8000 | 9000 | root@localhost | 2019-11-07 09:28:36 | +--------+----------+-----------------+------------+----------------+---------------------+ 1 row in set (0.00 sec)

      You also have the date and time the update was performed, which are valuable for audit purposes.

      Next you’ll use the DELETE trigger to enforce referencing integrity at the database level.

      Step 6 — Creating a Before Delete Trigger

      BEFORE DELETE triggers invoke before a DELETE statement executes on a table. These kinds of triggers are normally used to enforce referential integrity on different related tables. For example, each record on the sales table relates to a customer_id from the customers table. If a database user deleted a record from the customers table that has a related record in the sales table, you would have no way of knowing the customer associated with that record.

      To avoid this, you can create a BEFORE DELETE trigger to enforce your logic. Run the following SQL commands one by one:

      • DELIMITER //
      • CREATE TRIGGER validate_related_records
      • BEFORE DELETE
      • ON customers
      • FOR EACH ROW
      • IF OLD.customer_id in (select customer_id from sales) THEN
      • SIGNAL SQLSTATE '45000'
      • SET MESSAGE_TEXT = 'The customer has a related sales record.';
      • END IF//
      • DELIMITER ;

      Now, try to delete a customer that has a related sales record:

      • Delete from customers where customer_id='2';

      As a result you’ll receive the following output:

      Output

      ERROR 1644 (45000): The customer has a related sales record.

      The BEFORE DELETE trigger can prevent accidental deletion of related information in a database.

      However, in some situations, you may want to delete all the records associated with a particular record from the different related tables. In this situation you would use the AFTER DELETE trigger, which you’ll test in the next step.

      Step 7 — Creating an After Delete Trigger

      AFTER DELETE triggers are activated once a record has been deleted successfully. An example of how you can use an AFTER DELETE trigger is a situation in which the discount level a particular customer receives is determined by the number of sales made during a defined period. If some of the customer’s records are deleted from the sales table, the customer discount level would need to be downgraded.

      Another use of the AFTER DELETE trigger is deleting related information from another table once a record from a base table is deleted. For instance, you’ll set a trigger that deletes the customer record if the sales records with the related customer_id are deleted from the sales table. Run the following command to create your trigger:

      • DELIMITER //
      • CREATE TRIGGER delete_related_info
      • AFTER DELETE
      • ON sales
      • FOR EACH ROW
      • Delete from customers where customer_id=OLD.customer_id;//
      • DELIMITER ;

      Next, run the following to delete all sales records associated with a customer_id of 2:

      • Delete from sales where customer_id='2';

      Output

      Query OK, 1 row affected (0.00 sec)

      Now check if there are records for the customer from the sales table:

      • Select * from customers where customer_id='2';

      You will receive an Empty Set output since the customer record associated with the customer_id of 2 was deleted by the trigger:

      Output

      Empty set (0.00 sec)

      You’ve now used each of the different forms of triggers to perform specific functions. Next you will see how you can remove a trigger from the database if you no longer need it.

      Step 8 — Deleting Triggers

      Similarly to any other database object, you can delete triggers using the DROP command. The following is the syntax for deleting a trigger:

      Drop trigger [TRIGGER NAME];
      

      For instance, to delete the last AFTER DELETE trigger that you created, run the following command:

      • Drop trigger delete_related_info;

      Output

      Query OK, 0 rows affected (0.00 sec)

      The need to delete triggers arises when you want to recreate its structure. In such a case, you can drop the trigger and redefine a new one with the different trigger commands.

      Conclusion

      In this tutorial you’ve created, used, and deleted the different kinds of triggers from a MySQL database. Using an example customer-related database you’ve implemented triggers for different use cases such as data validation, business-logic application, audit logging, and enforcing referential integrity.

      For further information on using your MySQL database, check out the following:



      Source link