<commit_before>/*
 *  Copyright 2016 Adobe Systems Incorporated. All rights reserved.
 *  This file is licensed to you under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License. You may obtain a copy
 *  of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under
 *  the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
 *  OF ANY KIND, either express or implied. See the License for the specific language
 *  governing permissions and limitations under the License.
 */
package host

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"github.com/adobe-platform/porter/aws_session"
	"github.com/adobe-platform/porter/conf"
	"github.com/adobe-platform/porter/constants"
	dockerutil "github.com/adobe-platform/porter/docker/util"
	"github.com/adobe-platform/porter/logger"
	"github.com/adobe-platform/porter/secrets"
	"github.com/adobe-platform/porter/util"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/inconshreveable/log15"
	"github.com/phylake/go-cli"
)

// This implementation is tightly coupled with HAProxyCmd and how these commands
// are called together
type DockerCmd struct{}

func (recv *DockerCmd) Name() string {
	return "docker"
}

func (recv *DockerCmd) ShortHelp() string {
	return "Docker container orchestration"
}

func (recv *DockerCmd) LongHelp() string {
	return `NAME
    docker -- Docker container orchestration

SYNOPSIS
    docker --start -e <environment> -r <region> -s <key>
    docker --clean
    docker --ip

DESCRIPTION
    docker manipulates docker containers.

    This command expects to receive a porter config on STDIN.

OPTIONS
    --start
        Start and configure all primary and secondary containers found in the
        config. Also, print to STDOUT the primary container's host port that is
        to be mapped in HAProxy

    -e  Environment from .porter/config

    -r  AWS region

    -s  Secrets payload S3 key

    --clean
        Cleanup containers not found in the config. This command removes
        old containers and images with the equivalent of
            docker stop <container id>
            docker rm <container id>
            docker rmi <image id>
        This is important in terms of resource utilization that the containers
        are no longer running and the disk space the images occupy is released
        for long-running services that typically only do a hotswap

    --ip
        print the docker interface's IPv4 address to STDOUT`
}

func (recv *DockerCmd) SubCommands() []cli.Command {
	return nil
}

func (recv *DockerCmd) Execute(args []string) bool {
	if len(args) > 0 {
		switch args[0] {
		case "--start":
			if len(args) == 1 {
				return false
			}

			var environment, region, secretsS3Key string
			flagSet := flag.NewFlagSet("", flag.ExitOnError)
			flagSet.StringVar(&environment, "e", "", "")
			flagSet.StringVar(&region, "r", "", "")
			flagSet.StringVar(&secretsS3Key, "s", "", "")
			flagSet.Usage = func() {
				fmt.Println(recv.LongHelp())
			}
			flagSet.Parse(args[1:])

			startContainers(environment, region, secretsS3Key)
		case "--clean":
			if len(args) == 1 {
				return false
			}

			var environment, region string
			flagSet := flag.NewFlagSet("", flag.ExitOnError)
			flagSet.StringVar(&environment, "e", "", "")
			flagSet.StringVar(&region, "r", "", "")
			flagSet.Usage = func() {
				fmt.Println(recv.LongHelp())
			}
			flagSet.Parse(args[1:])

			cleanContainers(environment, region)
		case "--ip":
			printIPv4()
		default:
			return false
		}
		return true
	}

	return false
}

func startContainers(environmentStr, regionStr, secretsS3Key string) {
	var (
		err          error
		stdoutBuf    bytes.Buffer
		haproxyStdin HAPStdin
	)

	log := logger.Host("cmd", "docker")

	config, getStdinConfigSucces := conf.GetStdinConfig(log)
	if !getStdinConfigSucces {
		os.Exit(1)
	}

	environment, err := config.GetEnvironment(environmentStr)
	if err != nil {
		log.Crit("GetEnvironment", "Error", err)
		os.Exit(1)
	}

	region, err := environment.GetRegion(regionStr)
	if err != nil {
		log.Crit("GetRegion", "Error", err)
		os.Exit(1)
	}

	log.Info("starting docker containers")

	dockerIPv4 := dockerIfaceIPv4(log)

	if !prepareNetwork(log) {
		os.Exit(1)
	}

	secretsPayload := getSecretsPayload(log, region, secretsS3Key)

	if secretsPayload.DockerPullUsername != "" && secretsPayload.DockerPullPassword != "" {
		log.Info("docker login")

		loginCmd := exec.Command("docker", "login",
			"-u", secretsPayload.DockerPullUsername,
			"-p", secretsPayload.DockerPullPassword,
			secretsPayload.DockerRegistry)

		loginCmd.Stderr = os.Stderr
		err := loginCmd.Run()
		if err != nil {
			log.Error("docker login", "Error", err)
			os.Exit(1)
		}
	}

	for _, container := range region.Containers {

		runArgs := []string{
			"run",

			// daemonize
			"-d",

			// log driver with defaults since facility override doesn't work
			"--log-driver=syslog",

			// try to keep the container alive
			// CIS Docker Benchmark 1.11.0 5.14
			"--restart=on-failure:5",

			// set ulimit for container
			// TODO calculate this
			"--ulimit", "nofile=200000",
<commit_msg>CIS Docker Benchmark 1.11.0 5.25<commit_after>/*
 *  Copyright 2016 Adobe Systems Incorporated. All rights reserved.
 *  This file is licensed to you under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License. You may obtain a copy
 *  of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under
 *  the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
 *  OF ANY KIND, either express or implied. See the License for the specific language
 *  governing permissions and limitations under the License.
 */
package host

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"github.com/adobe-platform/porter/aws_session"
	"github.com/adobe-platform/porter/conf"
	"github.com/adobe-platform/porter/constants"
	dockerutil "github.com/adobe-platform/porter/docker/util"
	"github.com/adobe-platform/porter/logger"
	"github.com/adobe-platform/porter/secrets"
	"github.com/adobe-platform/porter/util"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/inconshreveable/log15"
	"github.com/phylake/go-cli"
)

// This implementation is tightly coupled with HAProxyCmd and how these commands
// are called together
type DockerCmd struct{}

func (recv *DockerCmd) Name() string {
	return "docker"
}

func (recv *DockerCmd) ShortHelp() string {
	return "Docker container orchestration"
}

func (recv *DockerCmd) LongHelp() string {
	return `NAME
    docker -- Docker container orchestration

SYNOPSIS
    docker --start -e <environment> -r <region> -s <key>
    docker --clean
    docker --ip

DESCRIPTION
    docker manipulates docker containers.

    This command expects to receive a porter config on STDIN.

OPTIONS
    --start
        Start and configure all primary and secondary containers found in the
        config. Also, print to STDOUT the primary container's host port that is
        to be mapped in HAProxy

    -e  Environment from .porter/config

    -r  AWS region

    -s  Secrets payload S3 key

    --clean
        Cleanup containers not found in the config. This command removes
        old containers and images with the equivalent of
            docker stop <container id>
            docker rm <container id>
            docker rmi <image id>
        This is important in terms of resource utilization that the containers
        are no longer running and the disk space the images occupy is released
        for long-running services that typically only do a hotswap

    --ip
        print the docker interface's IPv4 address to STDOUT`
}

func (recv *DockerCmd) SubCommands() []cli.Command {
	return nil
}

func (recv *DockerCmd) Execute(args []string) bool {
	if len(args) > 0 {
		switch args[0] {
		case "--start":
			if len(args) == 1 {
				return false
			}

			var environment, region, secretsS3Key string
			flagSet := flag.NewFlagSet("", flag.ExitOnError)
			flagSet.StringVar(&environment, "e", "", "")
			flagSet.StringVar(&region, "r", "", "")
			flagSet.StringVar(&secretsS3Key, "s", "", "")
			flagSet.Usage = func() {
				fmt.Println(recv.LongHelp())
			}
			flagSet.Parse(args[1:])

			startContainers(environment, region, secretsS3Key)
		case "--clean":
			if len(args) == 1 {
				return false
			}

			var environment, region string
			flagSet := flag.NewFlagSet("", flag.ExitOnError)
			flagSet.StringVar(&environment, "e", "", "")
			flagSet.StringVar(&region, "r", "", "")
			flagSet.Usage = func() {
				fmt.Println(recv.LongHelp())
			}
			flagSet.Parse(args[1:])

			cleanContainers(environment, region)
		case "--ip":
			printIPv4()
		default:
			return false
		}
		return true
	}

	return false
}

func startContainers(environmentStr, regionStr, secretsS3Key string) {
	var (
		err          error
		stdoutBuf    bytes.Buffer
		haproxyStdin HAPStdin
	)

	log := logger.Host("cmd", "docker")

	config, getStdinConfigSucces := conf.GetStdinConfig(log)
	if !getStdinConfigSucces {
		os.Exit(1)
	}

	environment, err := config.GetEnvironment(environmentStr)
	if err != nil {
		log.Crit("GetEnvironment", "Error", err)
		os.Exit(1)
	}

	region, err := environment.GetRegion(regionStr)
	if err != nil {
		log.Crit("GetRegion", "Error", err)
		os.Exit(1)
	}

	log.Info("starting docker containers")

	dockerIPv4 := dockerIfaceIPv4(log)

	if !prepareNetwork(log) {
		os.Exit(1)
	}

	secretsPayload := getSecretsPayload(log, region, secretsS3Key)

	if secretsPayload.DockerPullUsername != "" && secretsPayload.DockerPullPassword != "" {
		log.Info("docker login")

		loginCmd := exec.Command("docker", "login",
			"-u", secretsPayload.DockerPullUsername,
			"-p", secretsPayload.DockerPullPassword,
			secretsPayload.DockerRegistry)

		loginCmd.Stderr = os.Stderr
		err := loginCmd.Run()
		if err != nil {
			log.Error("docker login", "Error", err)
			os.Exit(1)
		}
	}

	for _, container := range region.Containers {

		runArgs := []string{
			"run",

			// daemonize
			"-d",

			// log driver with defaults since facility override doesn't work
			"--log-driver=syslog",

			// try to keep the container alive
			// CIS Docker Benchmark 1.11.0 5.14
			"--restart=on-failure:5",

			// CIS Docker Benchmark 1.11.0 5.25
			"--security-opt=no-new-privileges",

			// set ulimit for container
			// TODO calculate this
			"--ulimit", "nofile=200000",

{'fraction_non_alphanumeric': 0.11855294332057591, 'fraction_numerical': 0.009659194459631856, 'mean_word_length': 3.3379446640316206, 'pattern_counts': {'":': 6, '<': 15, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4787369', 'n_tokens_mistral': 3928, 'n_tokens_neox': 3491, 'n_words': 1020}