<commit_before>package connector

import (
	"fmt"
	"net"
	"os"
	"os/exec"

	"path"

	"time"

	"strings"

	humanize "github.com/dustin/go-humanize"
	"github.com/ellcrys/crypto"
	"github.com/ellcrys/util"
	cutil "github.com/ncodes/cocoon-util"
	"github.com/ncodes/cocoon/core/api/api"
	"github.com/ncodes/cocoon/core/common"
	"github.com/ncodes/cocoon/core/config"
	"github.com/ncodes/cocoon/core/connector/monitor"
	"github.com/ncodes/cocoon/core/orderer"
	docker "github.com/ncodes/go-dockerclient"
	logging "github.com/op/go-logging"
	context "golang.org/x/net/context"
)

var log = logging.MustGetLogger("connector")
var buildLog = logging.MustGetLogger("ccode.build")
var runLog = logging.MustGetLogger("ccode.run")
var configLog = logging.MustGetLogger("ccode.config")
var ccodeLog = logging.MustGetLogger("ccode")
var dckClient *docker.Client

var bridgeName = os.Getenv("BRIDGE_NAME")

func init() {
	runLog.SetBackend(config.MessageOnlyBackend)
}

// Connector defines a structure for starting and managing a cocoon (coode)
type Connector struct {
	waitCh            chan bool
	req               *Request
	connectorRPCAddr  string
	cocoonCodeRPCAddr string
	languages         []Language
	container         *docker.APIContainers
	containerRunning  bool
	monitor           *monitor.Monitor
	healthCheck       *HealthChecker
	ordererDiscovery  *orderer.Discovery
}

// NewConnector creates a new connector
func NewConnector(req *Request, waitCh chan bool) *Connector {
	return &Connector{
		req:              req,
		waitCh:           waitCh,
		monitor:          monitor.NewMonitor(),
		ordererDiscovery: orderer.NewDiscovery(),
	}
}

// Launch starts a cocoon code
func (cn *Connector) Launch(connectorRPCAddr, cocoonCodeRPCAddr string) {

	endpoint := "unix:///var/run/docker.sock"
	client, err := docker.NewClient(endpoint)
	if err != nil {
		log.Errorf("failed to create docker client. Is dockerd running locally?. %s", err)
		cn.Stop(true)
		return
	}

	dckClient = client
	cn.monitor.SetDockerClient(dckClient)
	cn.healthCheck = NewHealthChecker("127.0.0.1"+cn.cocoonCodeRPCAddr, cn.cocoonUnresponsive)
	go cn.ordererDiscovery.Discover()

	// No need downloading, building and starting a cocoon code
	// if DEV_COCOON_RPC_ADDR has been specified. This means a dev cocoon code
	// is running at that address. Just start the connector's client.
	if devCocoonCodeRPCAddr := os.Getenv("DEV_COCOON_RPC_ADDR"); len(devCocoonCodeRPCAddr) > 0 {
		cn.cocoonCodeRPCAddr = devCocoonCodeRPCAddr
		log.Infof("[Dev] Will interact with cocoon code at %s", devCocoonCodeRPCAddr)
		cn.healthCheck.Start()
		return
	}

	log.Info("Ready to install cocoon code")
	log.Debugf("Found ccode url=%s and lang=%s", cn.req.URL, cn.req.Lang)

	lang := cn.GetLanguage(cn.req.Lang)
	if lang == nil {
		log.Errorf("cocoon code language (%s) not supported", cn.req.Lang)
		cn.Stop(true)
		return
	}

	cocoonCodeContainer, err := cn.prepareContainer(lang)
	if err != nil {
		log.Error(err.Error())
		cn.Stop(true)
		return
	}

	lang.SetRunEnv(map[string]string{
		"COCOON_ID":          cn.req.ID,
		"CONNECTOR_RPC_ADDR": cn.connectorRPCAddr,
		"COCOON_RPC_ADDR":    cn.cocoonCodeRPCAddr, // cocoon code server will bind to the port of this address
		"COCOON_LINK":        cn.req.Link,          // the cocoon code id to link to natively
	})

	go cn.monitor.Monitor()

	go func() {
		if err = cn.run(cocoonCodeContainer, lang); err != nil {
			log.Error(err.Error())
			cn.Stop(true)
			return
		}
	}()
}

// GetOrdererDiscoverer returns the orderer discovery instance
func (cn *Connector) GetOrdererDiscoverer() *orderer.Discovery {
	return cn.ordererDiscovery
}

// cocoonUnresponsive is called when the cocoon code failed health check
func (cn *Connector) cocoonUnresponsive() {
	log.Info("Cocoon code has failed health check. Stopping cocoon code.")
	cn.Stop(true)
}

// SetAddrs sets the address of the connector and cocoon code RPC servers
func (cn *Connector) SetAddrs(connectorRPCAddr, cocoonCodeRPCAddr string) {
	cn.connectorRPCAddr = connectorRPCAddr
	cn.cocoonCodeRPCAddr = cocoonCodeRPCAddr
}

// GetRequest returns the current cocoon launch request
func (cn *Connector) GetRequest() *Request {
	return cn.req
}

// GetCocoonCodeRPCAddr returns the RPC address of the cocoon code
func (cn *Connector) GetCocoonCodeRPCAddr() string {
	return cn.cocoonCodeRPCAddr
}

// prepareContainer fetches the cocoon code source, creates a container,
// moves the source in to it, builds the source within the container (if required)
// and configures default firewall.
func (cn *Connector) prepareContainer(lang Language) (*docker.APIContainers, error) {

	var containerName = util.Env("COCOON_CONTAINER_NAME", "")
	if len(containerName) == 0 {
		return nil, fmt.Errorf("container name is unknown")
	}

	// ensure cocoon code isn't already launched on a container
	container, err := cn.getContainer(containerName)
	if err != nil {
		return nil, fmt.Errorf("failed to check whether cocoon code is already active. %s ", err.Error())
	} else if container != nil {
		return nil, fmt.Errorf("cocoon code already exists on a container")
	}

	_, err = cn.fetchSource(lang)
	if err != nil {
		return nil, err
	}

	cn.container = container
	cn.monitor.SetContainerID(cn.container.ID)
	cn.HookToMonitor(cn.req)

	if lang.RequiresBuild() {
		var buildParams map[string]interface{}

		if len(cn.req.BuildParams) == 0 {
			log.Info("No build parameters provided. Skipping build step")
		} else {
			log.Info("Parsing build parameters")
			cn.req.BuildParams, err = crypto.FromBase64(cn.req.BuildParams)
			if err != nil {
				return nil, fmt.Errorf("failed to decode build parameter. Expects a base 64 encoded string. %s", err)
			}
			if err = util.FromJSON([]byte(cn.req.BuildParams), &buildParams); err != nil {
				return nil, fmt.Errorf("failed to parse build parameter. Expects valid json string. %s", err)<commit_msg>return error if cocoon code container has not been started<commit_after>package connector

import (
	"fmt"
	"net"
	"os"
	"os/exec"

	"path"

	"time"

	"strings"

	humanize "github.com/dustin/go-humanize"
	"github.com/ellcrys/crypto"
	"github.com/ellcrys/util"
	cutil "github.com/ncodes/cocoon-util"
	"github.com/ncodes/cocoon/core/api/api"
	"github.com/ncodes/cocoon/core/common"
	"github.com/ncodes/cocoon/core/config"
	"github.com/ncodes/cocoon/core/connector/monitor"
	"github.com/ncodes/cocoon/core/orderer"
	docker "github.com/ncodes/go-dockerclient"
	logging "github.com/op/go-logging"
	context "golang.org/x/net/context"
)

var log = logging.MustGetLogger("connector")
var buildLog = logging.MustGetLogger("ccode.build")
var runLog = logging.MustGetLogger("ccode.run")
var configLog = logging.MustGetLogger("ccode.config")
var ccodeLog = logging.MustGetLogger("ccode")
var dckClient *docker.Client

var bridgeName = os.Getenv("BRIDGE_NAME")

func init() {
	runLog.SetBackend(config.MessageOnlyBackend)
}

// Connector defines a structure for starting and managing a cocoon (coode)
type Connector struct {
	waitCh            chan bool
	req               *Request
	connectorRPCAddr  string
	cocoonCodeRPCAddr string
	languages         []Language
	container         *docker.APIContainers
	containerRunning  bool
	monitor           *monitor.Monitor
	healthCheck       *HealthChecker
	ordererDiscovery  *orderer.Discovery
}

// NewConnector creates a new connector
func NewConnector(req *Request, waitCh chan bool) *Connector {
	return &Connector{
		req:              req,
		waitCh:           waitCh,
		monitor:          monitor.NewMonitor(),
		ordererDiscovery: orderer.NewDiscovery(),
	}
}

// Launch starts a cocoon code
func (cn *Connector) Launch(connectorRPCAddr, cocoonCodeRPCAddr string) {

	endpoint := "unix:///var/run/docker.sock"
	client, err := docker.NewClient(endpoint)
	if err != nil {
		log.Errorf("failed to create docker client. Is dockerd running locally?. %s", err)
		cn.Stop(true)
		return
	}

	dckClient = client
	cn.monitor.SetDockerClient(dckClient)
	cn.healthCheck = NewHealthChecker("127.0.0.1"+cn.cocoonCodeRPCAddr, cn.cocoonUnresponsive)
	go cn.ordererDiscovery.Discover()

	// No need downloading, building and starting a cocoon code
	// if DEV_COCOON_RPC_ADDR has been specified. This means a dev cocoon code
	// is running at that address. Just start the connector's client.
	if devCocoonCodeRPCAddr := os.Getenv("DEV_COCOON_RPC_ADDR"); len(devCocoonCodeRPCAddr) > 0 {
		cn.cocoonCodeRPCAddr = devCocoonCodeRPCAddr
		log.Infof("[Dev] Will interact with cocoon code at %s", devCocoonCodeRPCAddr)
		cn.healthCheck.Start()
		return
	}

	log.Info("Ready to install cocoon code")
	log.Debugf("Found ccode url=%s and lang=%s", cn.req.URL, cn.req.Lang)

	lang := cn.GetLanguage(cn.req.Lang)
	if lang == nil {
		log.Errorf("cocoon code language (%s) not supported", cn.req.Lang)
		cn.Stop(true)
		return
	}

	cocoonCodeContainer, err := cn.prepareContainer(lang)
	if err != nil {
		log.Error(err.Error())
		cn.Stop(true)
		return
	}

	lang.SetRunEnv(map[string]string{
		"COCOON_ID":          cn.req.ID,
		"CONNECTOR_RPC_ADDR": cn.connectorRPCAddr,
		"COCOON_RPC_ADDR":    cn.cocoonCodeRPCAddr, // cocoon code server will bind to the port of this address
		"COCOON_LINK":        cn.req.Link,          // the cocoon code id to link to natively
	})

	go cn.monitor.Monitor()

	go func() {
		if err = cn.run(cocoonCodeContainer, lang); err != nil {
			log.Error(err.Error())
			cn.Stop(true)
			return
		}
	}()
}

// GetOrdererDiscoverer returns the orderer discovery instance
func (cn *Connector) GetOrdererDiscoverer() *orderer.Discovery {
	return cn.ordererDiscovery
}

// cocoonUnresponsive is called when the cocoon code failed health check
func (cn *Connector) cocoonUnresponsive() {
	log.Info("Cocoon code has failed health check. Stopping cocoon code.")
	cn.Stop(true)
}

// SetAddrs sets the address of the connector and cocoon code RPC servers
func (cn *Connector) SetAddrs(connectorRPCAddr, cocoonCodeRPCAddr string) {
	cn.connectorRPCAddr = connectorRPCAddr
	cn.cocoonCodeRPCAddr = cocoonCodeRPCAddr
}

// GetRequest returns the current cocoon launch request
func (cn *Connector) GetRequest() *Request {
	return cn.req
}

// GetCocoonCodeRPCAddr returns the RPC address of the cocoon code
func (cn *Connector) GetCocoonCodeRPCAddr() string {
	return cn.cocoonCodeRPCAddr
}

// prepareContainer fetches the cocoon code source, creates a container,
// moves the source in to it, builds the source within the container (if required)
// and configures default firewall.
func (cn *Connector) prepareContainer(lang Language) (*docker.APIContainers, error) {

	var containerName = util.Env("COCOON_CONTAINER_NAME", "")
	if len(containerName) == 0 {
		return nil, fmt.Errorf("container name is unknown")
	}

	// ensure cocoon code isn't already launched on a container
	container, err := cn.getContainer(containerName)
	if err != nil {
		return nil, fmt.Errorf("failed to check whether cocoon code is already active. %s ", err.Error())
	} else if container == nil {
		return nil, fmt.Errorf("cocoon code container has not been started")
	}

	_, err = cn.fetchSource(lang)
	if err != nil {
		return nil, err
	}

	cn.container = container
	cn.monitor.SetContainerID(cn.container.ID)
	cn.HookToMonitor(cn.req)

	if lang.RequiresBuild() {
		var buildParams map[string]interface{}

		if len(cn.req.BuildParams) == 0 {
			log.Info("No build parameters provided. Skipping build step")
		} else {
			log.Info("Parsing build parameters")
			cn.req.BuildParams, err = crypto.FromBase64(cn.req.BuildParams)
			if err != nil {
				return nil, fmt.Errorf("failed to decode build parameter. Expects a base 64 encoded string. %s", err)
			}
			if err = util.FromJSON([]byte(cn.req.BuildParams), &buildParams); err != nil {
				return nil, fmt.Errorf("failed to parse build parameter. Expects valid json string. %s", err)
{'fraction_non_alphanumeric': 0.11220172618040278, 'fraction_numerical': 0.0022000338466745643, 'mean_word_length': 4.068181818181818, 'pattern_counts': {'":': 8, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5688888', 'n_tokens_mistral': 4135, 'n_tokens_neox': 4007, 'n_words': 1046}