# Elasticsearch (Docker) Install

    module.exports =  header: 'Docker ES Install', handler: (options) ->

## Identities

      @system.group options.group
      @system.user options.user

## Limits

      @system.limits
        header: 'Ulimit'
        user: options.user.name
      , options.user.limits

# Kernel

Configure kernel parameters at runtime. There are no properties set by default,
here's a suggestion:

*    vm.swappiness = 10
*    vm.overcommit_memory = 1
*    vm.overcommit_ratio = 100
*    net.core.somaxconn = 4096 (default socket listen queue size 128)

Note, we might move this middleware to Masson.

      @tools.sysctl
        header: 'Kernel'
        properties: options.sysctl
        merge: true
        comment: true

## SSL Certificate

      @file.download
        header: 'SSL CAcert'
        source: options.ssl.cacert.source
        local: options.ssl.cacert.local
        target: options.ssl.dest_cacert
        mode: 0o0640
      @file.download
        header: 'SSL cert'
        source: options.ssl.cert.source
        local: options.ssl.cert.local
        target: options.ssl.dest_cert
        mode: 0o0640
      @file.download
        header: 'SSL key'
        source: options.ssl.key.source
        local: options.ssl.key.local
        target: options.ssl.dest_key
        mode: 0o0640

## Write YAML Files

      for es_name,es of options.clusters then do (es_name,es) =>
        docker_services = {}
        docker_networks = {}
        @call header: "#{es_name}: ", ->
          @file.yaml
            header: 'elasticsearch config file'
            target: "/etc/elasticsearch/#{es_name}/conf/elasticsearch.yml"
            content:es.config
            backup: true


          @file.render
            header: 'elasticsearch java policy'
            target: "/etc/elasticsearch/#{es_name}/conf/java.policy"
            source: "#{__dirname}/resources/java.policy.j2"
            local: true
            context: {es: logs_path: "#{es.logs_path}/#{es_name}"}
            backup: true

          @file
            header: 'elasticsearch logging'
            target: "/etc/elasticsearch/#{es_name}/conf/log4j2.properties"
            source: "#{__dirname}/resources/log4j2.properties"
            local: true
            backup: true

          @system.mkdir directory:"#{path}/#{es_name}" ,uid: options.user.name, gid: options.user.name for path in es.data_path
          @system.mkdir directory:"#{es.plugins_path}",uid: options.user.name, gid: options.user.name
          @system.mkdir directory:"#{es.plugins_path}/#{es.es_version}",uid: options.user.name, gid: options.user.name
          @system.mkdir directory:"#{es.logs_path}/#{es_name}", uid: options.user.name, gid: options.user.name
          @system.mkdir directory:"#{es.logs_path}/#{es_name}/logstash",uid: options.user.name, gid: options.user.name
          @system.mkdir directory:"/etc/elasticsearch/#{es_name}/scripts",uid: options.user.name, gid: options.user.name
          @system.mkdir directory:"/etc/elasticsearch/keytabs",uid: options.user.name, gid: options.user.name

          @each es.downloaded_urls, (opts,callback) ->
            extract_target  = if opts.value.indexOf("github") != -1  then "#{es.plugins_path}/#{es.es_version}/" else "#{es.plugins_path}/#{es.es_version}/#{opts.key}"
            @call header: "Plugin #{opts.key} installation...", ->
              @file.download
                cache_file: "./#{opts.key}.zip"
                source: opts.value
                target: "#{es.plugins_path}/#{es.es_version}/#{opts.key}.zip"
                uid: options.user.name
                gid: options.user.name
                shy: true
              @tools.extract
                format: "zip"
                source: "#{es.plugins_path}/#{es.es_version}/#{opts.key}.zip"
                target: extract_target
                shy: true
              es.volumes.push "#{es.plugins_path}/#{es.es_version}/#{options.key}/elasticsearch:/usr/share/elasticsearch/plugins/#{options.key}"
              @system.remove "#{es.plugins_path}/#{es.es_version}/#{options.key}.zip", shy: true
            @next callback


## IPTables
  
          matching_hosts = []
          matching_hosts.push options.hostname for type,es_node of es.nodes when minimatch(options.hostname, es_node.filter)

          @tools.iptables
            header: 'IPTables'
            if: (es.network.mode is 'host') and options.hostname in matching_hosts
            rules: [
              { chain: 'INPUT', jump: 'ACCEPT', dport: es.network.http_port, protocol: 'tcp', state: 'NEW', comment: "ES Docker #{es_name}" }
              { chain: 'INPUT', jump: 'ACCEPT', dport: es.network.tcp_port, protocol: 'tcp', state: 'NEW', comment: "ES Docker #{es_name}" }
            ]

## Generate compose file

          #if options.fqdn is options.hosts[options.hosts.length-1]
          if options.fqdn is "#{if es.docker_compose_host then es.docker_compose_host else options.hosts[options.hosts.length-1]}"
            #TODO create overlay network if the network does not exist
            docker_networks["#{es.network.name}"] = external: es.network.external
            master_node = if es.master_nodes > 0
              "#{es.normalized_name}_master"
            else if es.master_data_nodes > 0
              "#{es.normalized_name}_master_data"
            es.volumes = [
              "/etc/elasticsearch/#{es_name}/conf/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml",
              "/etc/elasticsearch/#{es_name}/conf/log4j2.properties:/usr/share/elasticsearch/config/log4j2.properties",
              "/etc/elasticsearch/#{es_name}/scripts:/usr/share/elasticsearch/config/scripts",
              "#{es.logs_path}/#{es_name}:#{es.config['path.logs']}",
              "/etc/elasticsearch/#{es_name}/conf/java.policy:/usr/share/elasticsearch/config/java.policy"

            ].concat es.volumes
            es.volumes.push "#{path}/#{es_name}/:#{path}" for path in es.data_path
            # es.volumes.push "#{es.plugins_path}/#{es.es_version}/#{plugin}:/usr/share/elasticsearch/plugins/#{plugin}" for plugin in es.plugins
            for type,es_node of es.nodes
              command = switch type
                when "master" then "elasticsearch -Enode.master=true -Enode.data=false"
                when "master_data" then "elasticsearch -Enode.master=true -Enode.data=true"
                when "data" then "elasticsearch -Enode.master=false -Enode.data=true"
              es.environment.push "constraint:node==#{es_node.filter}" if es_node.filter != ""
              docker_services[type] = {'environment' : [].concat.apply([],[es.environment,"ES_JAVA_OPTS=-Xms#{es_node.heap_size} -Xmx#{es_node.heap_size} -Djava.security.policy=/usr/share/elasticsearch/config/java.policy","bootstrap.memory_lock=true"]) }

              service_def =
                image : es.docker_es_image
                restart: "always"
                command: command
                # networks: [es.network.name]
                user: "elasticsearch"
                volumes: es.volumes
                ports: es.ports
                mem_limit: if es_node.mem_limit? then es_node.mem_limit else es.default_mem
                ulimits:  es.ulimits
                cap_add:  es.cap_add
              
              if es.network.mode is 'host'
                service_def["network_mode"] = 'host'
              else 
                service_def["networks"] = [es.network.name]
              if es_node.cpuset?
                service_def["cpuset"] = es_node.cpuset
              else
                service_def["cpu_quota"] = if es_node.cpu_quota? then es_node.cpu_quota * 1000 else es.default_cpu_quota
              misc.merge docker_services[type], service_def
            if es.kibana?
              docker_services["#{es_name}_kibana"] =
                image: es.docker_kibana_image
                container_name: "#{es_name}_kibana"
                environment: ["ELASTICSEARCH_URL=http://#{master_node}_1:9200"]
                ports: ["#{es.kibana.port}:5601"]
                networks: [es.network.name]
                
            docker_compose_content = {}
            docker_compose_content.version ?= "2"
            docker_compose_content.services ?= docker_services
            docker_compose_content.networks ?= docker_networks if docker_networks[0] is not undefined
            
            @file.yaml
              header: 'docker-compose'
              target: "/etc/elasticsearch/#{es_name}/docker-compose.yml"
              content: docker_compose_content
              backup: true

## Run docker compose file

            docker_args =
                host: if options.swarm_manager then options.swarm_manager else ''
                tlsverify:" "
                tlscacert: options.ssl.dest_cacert
                tlscert: options.ssl.dest_cert
                tlskey:options.ssl.dest_key
            export_vars = 
              "export DOCKER_HOST=#{options.swarm_manager};export DOCKER_CERT_PATH=#{options.ssl.dest_dir};export DOCKER_TLS_VERIFY=1"

            for service, node of es.nodes then do (service, node) =>
              @system.execute
                cmd:"""
                #{export_vars}
                pushd /etc/elasticsearch/#{es_name}
                docker-compose --verbose scale #{service}=#{node.number}
                """

            @system.execute
              cmd:"""
              #{export_vars}
              pushd /etc/elasticsearch/#{es_name}
              docker-compose --verbose up -d #{es_name}_kibana
              """
              if: -> es.kibana is true

## Dependencies

    misc = require '@nikitajs/core/lib/misc'
    minimatch = require 'minimatch'
