require 'spec_helper'

describe 'Links with local_dns enabled', type: :integration do
  let(:cloud_config) do
    cloud_config_hash = SharedSupport::DeploymentManifestHelper.simple_cloud_config
    cloud_config_hash['azs'] = [{ 'name' => 'z1' }]
    cloud_config_hash['networks'].first['subnets'].first['az'] = 'z1'
    cloud_config_hash['compilation']['az'] = 'z1'
    cloud_config_hash['networks'] << {
      'name' => 'manual-network',
      'type' => 'manual',
      'subnets' => [
        {'range' => '10.10.0.0/24',
         'gateway' => '10.10.0.1',
         'az' => 'z1'}]
    }
    cloud_config_hash['networks'] << {
      'name' => 'dynamic-network',
      'type' => 'dynamic',
      'subnets' => [{'az' => 'z1'}]
    }

    cloud_config_hash
  end

  let(:api_instance_group_spec) do
    spec = SharedSupport::DeploymentManifestHelper.simple_instance_group(
      name: 'my_api',
      jobs: [{
        'name' => 'api_server',
        'consumes' => {
          'db' => { 'from' => 'db' },
        },
        'release' => 'bosh-release',
      }],
      instances: 1,
    )
    spec['networks'] = [{ 'name' => 'manual-network'}]
    spec['azs'] = ['z1']
    spec
  end

  let(:mysql_instance_group_spec) do
    spec = SharedSupport::DeploymentManifestHelper.simple_instance_group(
      name: 'mysql',
      jobs: [{ 'name' => 'database', 'release' => 'bosh-release' }],
      instances: 1,
      static_ips: ['192.168.1.10']
    )
    spec['azs'] = ['z1']
    spec['networks'] = [{ 'name' => provider_network_name}]
    spec
  end

  let(:provider_network_name) { 'manual-network' }

  let(:manifest) do
    manifest = SharedSupport::DeploymentManifestHelper.simple_manifest_with_instance_groups
    manifest['instance_groups'] = [api_instance_group_spec, mysql_instance_group_spec]
    manifest
  end

  before do
    manifest['features'] = {}
  end

  context 'when use_dns_addresses director flag is TRUE' do
    with_reset_sandbox_before_each(local_dns: {'enabled' => true, 'include_index' => false, 'use_dns_addresses' => true})

    before do
      upload_links_release(bosh_runner_options: {})
      upload_stemcell

      upload_cloud_config(cloud_config_hash: cloud_config)
    end

    context 'when job requires link' do
      context 'when link is provided' do
        context 'when network is manual' do
          it 'outputs dns address when accessing instance.address of the link' do
            deploy_simple_manifest(manifest_hash: manifest)
            instances = director.instances
            api_instance = director.find_instance(instances, 'my_api', '0')
            mysql_0_instance = director.find_instance(instances, 'mysql', '0')
            template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))
            addresses = template['databases']['main'].map do |elem|
              elem['address']
            end
            expect(addresses).to eq(["#{mysql_0_instance.id}.mysql.manual-network.simple.bosh"])
          end

          context 'when deployment manifest features specifies use_dns_addresses to FALSE' do
            before do
              manifest['features'] = { 'use_dns_addresses' => false }
            end

            it 'outputs ip address when accessing instance.address of the link' do
              deploy_simple_manifest(manifest_hash: manifest)
              instances = director.instances
              api_instance = director.find_instance(instances, 'my_api', '0')
              template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))
              addresses = template['databases']['main'].map do |elem|
                elem['address']
              end
              expect(addresses).to eq(['10.10.0.3'])
            end
          end

          context "when 'ip_addresses' is set to true on the consumer jobs link options" do
            before do
              api_instance_group_spec['jobs'][0]['consumes']['db']['ip_addresses'] = true
            end

            it 'outputs ip addresses when accessing instance.address of the link' do
              deploy_simple_manifest(manifest_hash: manifest)

              instances = director.instances
              api_instance = director.find_instance(instances, 'my_api', '0')

              template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))

              addresses = template['databases']['main'].map do |elem|
                elem['address']
              end

              expect(addresses).to eq(['10.10.0.3'])
            end

            context 'when deployment manifest features specifies use_dns_addresses to TRUE' do
              before do
                manifest['features'] = {'use_dns_addresses' => true}
              end

              it 'outputs ip address when accessing instance.address of the link' do
                deploy_simple_manifest(manifest_hash: manifest)

                instances = director.instances
                api_instance = director.find_instance(instances, 'my_api', '0')

                template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))

                addresses = template['databases']['main'].map do |elem|
                  elem['address']
                end

                expect(addresses).to eq(['10.10.0.3'])
              end
            end
          end
        end

        context 'using link.address helper' do
          let(:job_link_overrided_spec) do
            instance_group_spec = SharedSupport::DeploymentManifestHelper.simple_instance_group(
              name: 'my_api',
              jobs: [
                {
                  'name' => 'api_server',
                  'release' => 'bosh-release',
                  'consumes' => {
                    'db' => {
                      'address' => 'broker.external-db.com',
                      'instances' => [],
                      'properties' => { 'foo' => 'bar' },
                    },
                    'backup_db' => {
                      'address' => 'nothing',
                      'instances' => [],
                      'properties' => { 'foo' => 'bar' },
                    },
                  },
                },
              ],
              instances: 1,
            )
            instance_group_spec['networks'] = [{ 'name' => 'manual-network'}]
            instance_group_spec['azs'] = ['z1']
            instance_group_spec
          end

          let(:rendered_template) do
            api_instance = director.find_instance(director.instances, 'my_api', '0')
            YAML.load(api_instance.read_job_template('api_server', 'config.yml'))
          end

          it 'returns a query string for az and healthiness' do
            deploy_simple_manifest(manifest_hash: manifest)
            expect(rendered_template['db_az_link']['address']).to eq('q-a1s0.mysql.manual-network.simple.bosh')
            expect(rendered_template['optional_backup_link'][0]['address']).to eq('q-s0.mysql.manual-network.simple.bosh')
          end

          it 'uses a short DNS name if manifest so indicates' do
            manifest['features'] = {
              'use_short_dns_addresses' => true,
              'use_link_dns_names' => false,
            }
            deploy_simple_manifest(manifest_hash: manifest)
            expect(rendered_template['db_az_link']['address']).to match(/q-a1n\ds0.q-g2.bosh/)
          end

          it 'uses a link DNS name if manifest so indicates' do
            manifest['features'] = {
              'use_short_dns_addresses' => true,
              'use_link_dns_names' => true,
            }
            deploy_simple_manifest(manifest_hash: manifest)
            expect(rendered_template['db_az_link']['address']).to match(/q-a1n\ds0.q-g3.bosh/)
          end

          it 'instances use a link DNS name if manifest so indicates' do
            manifest['features'] = {
              'use_short_dns_addresses' => true,
              'use_link_dns_names' => true,
            }
            deploy_simple_manifest(manifest_hash: manifest)
            expect(rendered_template['databases']['main'][0]['address']).to match(/q-m2n\ds0.q-g3.bosh/)
          end

          it 'respects address provided in a manual link' do
            manifest['instance_groups'] = [job_link_overrided_spec]
            deploy_simple_manifest(manifest_hash: manifest)
            expect(rendered_template['db_az_link']['address']).to eq('broker.external-db.com')
            expect(rendered_template['optional_backup_link'][0]['address']).to eq('nothing')
          end
        end

        context 'using link.instances[x].address helper' do
          let(:rendered_template) do
            api_instance = director.find_instance(director.instances, 'my_api', '0')
            YAML.load(api_instance.read_job_template('api_server', 'config.yml'))
          end

          let(:dns_address) { nil }
          let(:features_hash) do
            { 'use_dns_addresses' => true, 'use_short_dns_addresses' => true }
          end

          shared_examples 'matching DNS names' do
            it 'expects DNS address to match' do
              if features_hash
                manifest['features'] = features_hash
              end

              deploy_simple_manifest(manifest_hash: manifest)
              expect(rendered_template['databases']['main'][0]['address']).to match(dns_address)
            end
          end

          context 'uses short DNS name if manifest so indicates' do
            let(:dns_address) { /q-m\dn\ds0.q-g2.bosh/ }
            it_should_behave_like 'matching DNS names'
          end

          context 'use FULL DNS if manifest doesnt specify short DNS' do
            let(:dns_address) { /.mysql.manual-network.simple.bosh/ }
            let(:features_hash) { nil }
            it_should_behave_like 'matching DNS names'
          end
        end
      end

      context 'when having cross deployment links' do
        let(:mysql_instance_group_spec) do
          spec = SharedSupport::DeploymentManifestHelper.simple_instance_group(
            name: 'mysql',
            jobs: [
              {
                'name' => 'database',
                'provides' => {
                  'db' => {
                    'as' => 'mysql_link',
                    'shared' => true
                  }
                },
                'release' => 'bosh-release',
              }
            ],
            instances: 1,
            static_ips: ['192.168.1.10']
          )
          spec['azs'] = ['z1']
          spec['networks'] = [{ 'name' => network_name}]
          spec
        end

        let(:api_instance_group_spec) do
          spec = SharedSupport::DeploymentManifestHelper.simple_instance_group(
            name: 'my_api',
            jobs: [
              {
                'name' => 'api_server',
                'consumes' => {
                  'db' => {
                    'from' => 'mysql_link',
                    'deployment' => 'provider_deployment',
                  },
                  'backup_db' => {
                    'from' => 'mysql_link',
                    'deployment' => 'provider_deployment'
                  }
                },
                'release' => 'bosh-release',
              }],
            instances: 1
          )
          spec['networks'] = [{ 'name' => network_name}]
          spec['azs'] = ['z1']
          spec
        end

        let(:provider_deployment_manifest) do
          manifest = SharedSupport::DeploymentManifestHelper.deployment_manifest(name: 'provider_deployment')
          manifest['instance_groups'] = [mysql_instance_group_spec]
          manifest
        end

        let(:consumer_deployment_manifest) do
          manifest = SharedSupport::DeploymentManifestHelper.deployment_manifest(name: 'consumer_deployment')
          manifest['instance_groups'] = [api_instance_group_spec]
          manifest
        end

        context 'when provider job network is manual' do
          let(:network_name) { 'manual-network' }

          it 'outputs dns address when accessing instance.address of the link' do
            deploy_simple_manifest(manifest_hash: provider_deployment_manifest)
            deploy_simple_manifest(manifest_hash: consumer_deployment_manifest)

            instances = director.instances(deployment_name: 'consumer_deployment')
            api_instance = director.find_instance(instances, 'my_api', '0')
            template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))

            provider_instances = director.instances(deployment_name: 'provider_deployment')
            mysql_instance = director.find_instance(provider_instances, 'mysql', '0')

            addresses = template['databases']['backup'].map do |elem|
              elem['address']
            end

            expect(addresses).to eq(["#{mysql_instance.id}.mysql.manual-network.provider-deployment.bosh"])
          end

          context "when consumer job sets 'ip_addresses' to true in its manifest link options" do
            before do
              api_instance_group_spec['jobs'][0]['consumes']['db']['ip_addresses'] = true
            end

            it 'outputs ip address when accessing instance.address of the link' do
              deploy_simple_manifest(manifest_hash: provider_deployment_manifest)
              deploy_simple_manifest(manifest_hash: consumer_deployment_manifest)

              instances = director.instances(deployment_name: 'consumer_deployment')
              api_instance = director.find_instance(instances, 'my_api', '0')
              template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))

              addresses = template['databases']['main'].map do |elem|
                elem['address']
              end

              expect(addresses).to eq(['10.10.0.2'])
            end
          end
        end

        context 'when provider job network is dynamic' do
          let(:network_name) { 'dynamic-network' }

          it 'outputs dns address when accessing instance.address of the link' do
            deploy_simple_manifest(manifest_hash: provider_deployment_manifest)
            deploy_simple_manifest(manifest_hash: consumer_deployment_manifest)

            instances = director.instances(deployment_name: 'consumer_deployment')
            api_instance = director.find_instance(instances, 'my_api', '0')
            template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))

            provider_instances = director.instances(deployment_name: 'provider_deployment')
            mysql_instance = director.find_instance(provider_instances, 'mysql', '0')

            addresses = template['databases']['main'].map do |elem|
              elem['address']
            end

            expect(addresses).to eq(["#{mysql_instance.id}.mysql.dynamic-network.provider-deployment.bosh"])
          end
        end
      end
    end
  end

  context 'when use_dns_addresses director flag is FALSE' do
    with_reset_sandbox_before_each(local_dns: {'enabled' => true, 'include_index' => false, 'use_dns_addresses' => false} )

    before do
      upload_links_release(bosh_runner_options: {})
      upload_stemcell

      upload_cloud_config(cloud_config_hash: cloud_config)
    end

    it 'outputs ip address when accessing instance.address of the link' do
      deploy_simple_manifest(manifest_hash: manifest)

      instances = director.instances
      api_instance = director.find_instance(instances, 'my_api', '0')

      template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))

      addresses = template['databases']['main'].map do |elem|
        elem['address']
      end

      expect(addresses).to eq(['10.10.0.3'])
    end

    context 'when deployment manifest features specifies use_dns_addresses to TRUE' do
      before do
        manifest['features'] = {'use_dns_addresses' => true}
      end

      it 'outputs DNS address when accessing instance.address of the link' do
        deploy_simple_manifest(manifest_hash: manifest)
        instances = director.instances
        api_instance = director.find_instance(instances, 'my_api', '0')
        mysql_instance = director.find_instance(instances, 'mysql', '0')
        template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))
        addresses = template['databases']['main'].map do |elem|
          elem['address']
        end
        expect(addresses).to eq(["#{mysql_instance.id}.mysql.manual-network.simple.bosh"])
      end

      context 'when deployment manifest features specifies use_link_dns_names to TRUE' do
        before do
          manifest['features']['use_link_dns_names'] = true
        end

        it 'uses the instance short DNS address when accessing instance.address of the link' do
          deploy_simple_manifest(manifest_hash: manifest)
          instances = director.instances
          mysql_instance = director.find_instance(instances, 'mysql', '0')
          api_instance = director.find_instance(instances, 'my_api', '0')
          template = YAML.safe_load(api_instance.read_job_template('api_server', 'config.yml'))
          addresses = template['databases']['main'].map do |elem|
            elem['address']
          end
          expect(addresses.length).to eq(1)
          expect(addresses[0]).to match(/q-m\dn\ds0\.q-g\d\.bosh/)
        end
      end

      context 'when deployment manifest features specifies use_short_dns_addresses to TRUE' do
        before do
          manifest['features']['use_short_dns_addresses'] = true
        end

        it 'outputs an abbreviated DNS address when accessing instance.address of the link' do
          deploy_simple_manifest(manifest_hash: manifest)
          instances = director.instances
          api_instance = director.find_instance(instances, 'my_api', '0')
          template = YAML.safe_load(api_instance.read_job_template('api_server', 'config.yml'))
          addresses = template['databases']['main'].map do |elem|
            elem['address']
          end
          expect(addresses.length).to eq(1)
          expect(addresses[0]).to match(/q-m\dn\ds0\.q-g\d\.bosh/)
        end

        context 'when deployment manifest features specifies both use_short_dns_addresses and use_link_dns_names as TRUE' do
          before do
            manifest['features']['use_link_dns_names'] = true
            manifest['features']['use_short_dns_addresses'] = true
          end

          it 'outputs an abbreviated DNS address when accessing instance.address of the link' do
            deploy_simple_manifest(manifest_hash: manifest)
            instances = director.instances
            api_instance = director.find_instance(instances, 'my_api', '0')
            template = YAML.safe_load(api_instance.read_job_template('api_server', 'config.yml'))
            addresses = template['databases']['main'].map do |elem|
              elem['address']
            end
            expect(addresses.length).to eq(1)
            expect(addresses[0]).to match(/q-m\d+n\d+s0\.q-g\d+\.bosh/)
          end

          it 'outputs an abbreviated DNS address when accessing the short link address' do
            deploy_simple_manifest(manifest_hash: manifest)
            instances = director.instances
            api_instance = director.find_instance(instances, 'my_api', '0')
            template = YAML.safe_load(api_instance.read_job_template('api_server', 'config.yml'))
            backup_addresses = template['optional_backup_link'].map do |elem|
              elem['address']
            end
            expect(backup_addresses.length).to eq(1)
            expect(backup_addresses[0]).to match(/q-n\d+s0\.q-g\d+\.bosh/)
          end
        end
      end
    end

    context 'when ip_addresses field is explicitly set to FALSE in the consume link section' do
      before do
        api_instance_group_spec['jobs'][0]['consumes']['db']['ip_addresses'] = false
      end

      it 'outputs dns address when accessing instance.address of the link' do
        deploy_simple_manifest(manifest_hash: manifest)

        instances = director.instances
        api_instance = director.find_instance(instances, 'my_api', '0')
        mysql_instance = director.find_instance(instances, 'mysql', '0')

        template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))

        addresses = template['databases']['main'].map do |elem|
          elem['address']
        end

        expect(addresses).to eq(["#{mysql_instance.id}.mysql.manual-network.simple.bosh"])
      end

      context 'when deployment manifest features specifies use_dns_addresses to FALSE' do
        before do
          manifest['features'] = {'use_dns_addresses' => false}
        end

        it 'outputs DNS address when accessing instance.address of the link' do
          deploy_simple_manifest(manifest_hash: manifest)

          instances = director.instances
          api_instance = director.find_instance(instances, 'my_api', '0')
          mysql_instance = director.find_instance(instances, 'mysql', '0')

          template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))
          addresses = template['databases']['main'].map do |elem|
            elem['address']
          end

          expect(addresses).to eq(["#{mysql_instance.id}.mysql.manual-network.simple.bosh"])
        end
      end
    end

    context 'when having cross deployment links' do
      let(:mysql_instance_group_spec) do
        spec = SharedSupport::DeploymentManifestHelper.simple_instance_group(
          name: 'mysql',
          jobs: [
            {
              'name' => 'database',
              'provides' => {
                'db' => {
                  'as' => 'mysql_link',
                  'shared' => true,
                },
              },
              'release' => 'bosh-release',
            }
          ],
          instances: 1,
          static_ips: ['192.168.1.10']
        )
        spec['azs'] = ['z1']
        spec['networks'] = [{ 'name' => network_name}]
        spec
      end

      let(:api_instance_group_spec) do
        spec = SharedSupport::DeploymentManifestHelper.simple_instance_group(
          name: 'my_api',
          jobs: [
            {
              'name' => 'api_server',
              'consumes' => {
                'db' => {
                  'from' => 'mysql_link',
                  'deployment' => 'provider_deployment',
                },
                'backup_db' => {
                  'from' => 'mysql_link',
                  'deployment' => 'provider_deployment',
                },
              },
              'release' => 'bosh-release',
            }],
          instances: 1
        )
        spec['networks'] = [{ 'name' => network_name}]
        spec['azs'] = ['z1']
        spec
      end

      let(:provider_deployment_manifest) do
        manifest = SharedSupport::DeploymentManifestHelper.deployment_manifest(name: 'provider_deployment')
        manifest['instance_groups'] = [mysql_instance_group_spec]
        manifest
      end

      let(:consumer_deployment_manifest) do
        manifest = SharedSupport::DeploymentManifestHelper.deployment_manifest(name: 'consumer_deployment')
        manifest['instance_groups'] = [api_instance_group_spec]
        manifest
      end

      context 'when provider job network is manual' do
        let(:network_name) { 'manual-network' }

        it 'outputs ip address when accessing instance.address of the link' do
          deploy_simple_manifest(manifest_hash: provider_deployment_manifest)
          deploy_simple_manifest(manifest_hash: consumer_deployment_manifest)

          instances = director.instances(deployment_name: 'consumer_deployment')
          api_instance = director.find_instance(instances, 'my_api', '0')
          template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))

          addresses = template['databases']['backup'].map do |elem|
            elem['address']
          end

          expect(addresses).to eq(["10.10.0.2"])
        end

        context "when consumer job sets 'ip_addresses' to FALSE in its manifest link options" do
          before do
            api_instance_group_spec['jobs'][0]['consumes']['db']['ip_addresses'] = false
          end

          it 'outputs dns address when accessing instance.address of the link' do
            deploy_simple_manifest(manifest_hash: provider_deployment_manifest)
            deploy_simple_manifest(manifest_hash: consumer_deployment_manifest)

            instances = director.instances(deployment_name: 'consumer_deployment')
            api_instance = director.find_instance(instances, 'my_api', '0')
            template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))

            provider_instances = director.instances(deployment_name: 'provider_deployment')
            mysql_instance = director.find_instance(provider_instances, 'mysql', '0')

            addresses = template['databases']['main'].map do |elem|
              elem['address']
            end

            expect(addresses).to eq(["#{mysql_instance.id}.mysql.manual-network.provider-deployment.bosh"])
          end
        end
      end

      context 'when provider job network is dynamic' do
        let(:network_name) { 'dynamic-network' }

        context "when consumer job set 'ip_addresses' to FALSE in its manifest link options" do
          before do
            api_instance_group_spec['jobs'][0]['consumes']['db']['ip_addresses'] = false
          end

          it 'outputs dns address when accessing instance.address of the link' do
            deploy_simple_manifest(manifest_hash: provider_deployment_manifest)
            deploy_simple_manifest(manifest_hash: consumer_deployment_manifest)

            instances = director.instances(deployment_name: 'consumer_deployment')
            api_instance = director.find_instance(instances, 'my_api', '0')
            template = YAML.load(api_instance.read_job_template('api_server', 'config.yml'))

            provider_instances = director.instances(deployment_name: 'provider_deployment')
            mysql_instance = director.find_instance(provider_instances, 'mysql', '0')

            addresses = template['databases']['main'].map do |elem|
              elem['address']
            end

            expect(addresses).to eq(["#{mysql_instance.id}.mysql.dynamic-network.provider-deployment.bosh"])
          end
        end
      end
    end
  end
end
