<commit_before>// Copyright 2015 clair authors
//
// Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package detectors exposes functions to register and use container
// information extractors.
package detectors

import (
	"fmt"
	"io"
	"math"
	"net/http"
	"os"
	"strings"
	"sync"

	cerrors "github.com/coreos/clair/utils/errors"
	"github.com/coreos/pkg/capnslog"
)

// The DataDetector interface defines a way to detect the required data from input path
type DataDetector interface {
	//Support check if the input path and format are supported by the underling detector
	Supported(path string, format string) bool
	// Detect detects the required data from input path
	Detect(layerReader io.ReadCloser, toExtract []string, maxFileSize int64) (data map[string][]byte, err error)
}

var (
	dataDetectorsLock sync.Mutex
	dataDetectors     = make(map[string]DataDetector)

	log = capnslog.NewPackageLogger("github.com/coreos/clair", "detectors")
)

// RegisterDataDetector provides a way to dynamically register an implementation of a
// DataDetector.
//
// If RegisterDataDetector is called twice with the same name if DataDetector is nil,
// or if the name is blank, it panics.
func RegisterDataDetector(name string, f DataDetector) {
	if name == "" {
		panic("Could not register a DataDetector with an empty name")
	}
	if f == nil {
		panic("Could not register a nil DataDetector")
	}

	dataDetectorsLock.Lock()
	defer dataDetectorsLock.Unlock()

	if _, alreadyExists := dataDetectors[name]; alreadyExists {
		panic(fmt.Sprintf("Detector '%s' is already registered", name))
	}
	dataDetectors[name] = f
}

// DetectData finds the Data of the layer by using every registered DataDetector
func DetectData(path string, format string, toExtract []string, maxFileSize int64) (data map[string][]byte, err error) {
	var layerReader io.ReadCloser
	if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
		r, err := http.Get(path)
		if err != nil {
			log.Warningf("could not download layer: %s", err)
			return nil, cerrors.ErrCouldNotDownload
		}
		if math.Floor(float64(r.StatusCode/100)) != 2 {
			log.Warningf("could not download layer: got status code %d, expected 2XX", r.StatusCode)
			return nil, cerrors.ErrCouldNotDownload
		}
		if err != nil {
			log.Warningf("could not download layer: %s", err)
			return nil, cerrors.ErrCouldNotDownload
		if r.StatusCode == 404 {
			return nil, cerrors.ErrNotFound
		}
		if math.Floor(float64(r.StatusCode/100)) != 2 {
			log.Warningf("could not download layer: got status code %d, expected 2XX", r.StatusCode)
			return nil, cerrors.ErrCouldNotDownload
		}
		layerReader = r.Body
	} else {
		layerReader, err = os.Open(path)
		if err != nil {
			return nil, cerrors.ErrNotFound
		}
	}
	defer layerReader.Close()

	for _, detector := range dataDetectors {
		if detector.Supported(path, format) {
			data, err = detector.Detect(layerReader, toExtract, maxFileSize)
			if err != nil {
				return nil, err
			}
			return data, nil
		}
	}

	return nil, cerrors.NewBadRequestError(fmt.Sprintf("unsupported image format '%s'", format))
}
<commit_msg>worker: remove duplicated tests<commit_after>// Copyright 2015 clair authors
//
// Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package detectors exposes functions to register and use container
// information extractors.
package detectors

import (
	"fmt"
	"io"
	"math"
	"net/http"
	"os"
	"strings"
	"sync"

	cerrors "github.com/coreos/clair/utils/errors"
	"github.com/coreos/pkg/capnslog"
)

// The DataDetector interface defines a way to detect the required data from input path
type DataDetector interface {
	//Support check if the input path and format are supported by the underling detector
	Supported(path string, format string) bool
	// Detect detects the required data from input path
	Detect(layerReader io.ReadCloser, toExtract []string, maxFileSize int64) (data map[string][]byte, err error)
}

var (
	dataDetectorsLock sync.Mutex
	dataDetectors     = make(map[string]DataDetector)

	log = capnslog.NewPackageLogger("github.com/coreos/clair", "detectors")
)

// RegisterDataDetector provides a way to dynamically register an implementation of a
// DataDetector.
//
// If RegisterDataDetector is called twice with the same name if DataDetector is nil,
// or if the name is blank, it panics.
func RegisterDataDetector(name string, f DataDetector) {
	if name == "" {
		panic("Could not register a DataDetector with an empty name")
	}
	if f == nil {
		panic("Could not register a nil DataDetector")
	}

	dataDetectorsLock.Lock()
	defer dataDetectorsLock.Unlock()

	if _, alreadyExists := dataDetectors[name]; alreadyExists {
		panic(fmt.Sprintf("Detector '%s' is already registered", name))
	}
	dataDetectors[name] = f
}

// DetectData finds the Data of the layer by using every registered DataDetector
func DetectData(path string, format string, toExtract []string, maxFileSize int64) (data map[string][]byte, err error) {
	var layerReader io.ReadCloser
	if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
		r, err := http.Get(path)
		if err != nil {
			log.Warningf("could not download layer: %s", err)
			return nil, cerrors.ErrCouldNotDownload
		}
		if r.StatusCode == 404 {
			return nil, cerrors.ErrNotFound
		}
		if math.Floor(float64(r.StatusCode/100)) != 2 {
			log.Warningf("could not download layer: got status code %d, expected 2XX", r.StatusCode)
			return nil, cerrors.ErrCouldNotDownload
		}
		layerReader = r.Body
	} else {
		layerReader, err = os.Open(path)
		if err != nil {
			return nil, cerrors.ErrNotFound
		}
	}
	defer layerReader.Close()

	for _, detector := range dataDetectors {
		if detector.Supported(path, format) {
			data, err = detector.Detect(layerReader, toExtract, maxFileSize)
			if err != nil {
				return nil, err
			}
			return data, nil
		}
	}

	return nil, cerrors.NewBadRequestError(fmt.Sprintf("unsupported image format '%s'", format))
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"strings"
)

// StringList represents the "poor man's list" that terraform uses
// internally
type StringList string

// This is the delimiter used to recognize and split StringLists
//
// It plays two semantic roles:
//   * It introduces a list
//   * It terminates each element
//
// Example representations:
// []             => SLD
// [""]           => SLDSLD
// [" "]          => SLD SLD
// ["foo"]        => SLDfooSLD
// ["foo", "bar"] => SLDfooSLDbarSLD
// ["", ""]       => SLDSLDSLD
const stringListDelim = `B780FFEC-B661-4EB8-9236-A01737AD98B6`

// Takes a Stringlist and returns one without empty strings in it
func (sl StringList) Compact() StringList {
	parts := sl.Slice()

  newlist := []string{}
	// drop the empty strings
	for i := range parts {
		if parts[i] != "" {
			newlist = append(newlist,  parts[i])
		}
	}
	return NewStringList(newlist)
}

// Build a StringList from a slice
func NewStringList(parts []string) StringList {
	// We have to special case the empty list representation
	if len(parts) == 0 {
		return StringList(stringListDelim)
	}
	return StringList(fmt.Sprintf("%s%s%s",
		stringListDelim,
		strings.Join(parts, stringListDelim),
		stringListDelim,
	))
}

// Returns an element at the index, wrapping around the length of the string
// when index > list length
func (sl StringList) Element(index int) string {
	if sl.Length() == 0 {
		return ""
	}
	return sl.Slice()[index%sl.Length()]
}

// Returns the length of the StringList
func (sl StringList) Length() int {
	return len(sl.Slice())
}

// Returns a slice of strings as represented by this StringList
func (sl StringList) Slice() []string {
	parts := strings.Split(string(sl), stringListDelim)

	switch len(parts) {
	case 0, 1:
		return []string{}
	case 2:
		return []string{""}
	}

	// strip empty elements generated by leading and trailing delimiters
	return parts[1 : len(parts)-1]
}

func (sl StringList) String() string {
	return string(sl)
}

// Determines if a given string represents a StringList
func IsStringList(s string) bool {
	return strings.Contains(s, stringListDelim)
}
<commit_msg>Return an empty slice for empty string lists<commit_after>package config

import (
	"fmt"
	"strings"
)

// StringList represents the "poor man's list" that terraform uses
// internally
type StringList string

// This is the delimiter used to recognize and split StringLists
//
// It plays two semantic roles:
//   * It introduces a list
//   * It terminates each element
//
// Example representations:
// []             => SLD
// [""]           => SLDSLD
// [" "]          => SLD SLD
// ["foo"]        => SLDfooSLD
// ["foo", "bar"] => SLDfooSLDbarSLD
// ["", ""]       => SLDSLDSLD
const stringListDelim = `B780FFEC-B661-4EB8-9236-A01737AD98B6`

// Takes a Stringlist and returns one without empty strings in it
func (sl StringList) Compact() StringList {
	parts := sl.Slice()

  newlist := []string{}
	// drop the empty strings
	for i := range parts {
		if parts[i] != "" {
			newlist = append(newlist,  parts[i])
		}
	}
	return NewStringList(newlist)
}

// Build a StringList from a slice
func NewStringList(parts []string) StringList {
	// We have to special case the empty list representation
	if len(parts) == 0 {
		return StringList(stringListDelim)
	}
	return StringList(fmt.Sprintf("%s%s%s",
		stringListDelim,
		strings.Join(parts, stringListDelim),
		stringListDelim,
	))
}

// Returns an element at the index, wrapping around the length of the string
// when index > list length
func (sl StringList) Element(index int) string {
	if sl.Length() == 0 {
		return ""
	}
	return sl.Slice()[index%sl.Length()]
}

// Returns the length of the StringList
func (sl StringList) Length() int {
	return len(sl.Slice())
}

// Returns a slice of strings as represented by this StringList
func (sl StringList) Slice() []string {
	parts := strings.Split(string(sl), stringListDelim)

	// split on an empty StringList will have a length of 2, since there is
	// always at least one deliminator
	switch len(parts) {
	case 0, 1, 2:
		return []string{}
	}

	// strip empty elements generated by leading and trailing delimiters
	return parts[1 : len(parts)-1]
}

func (sl StringList) String() string {
	return string(sl)
}

// Determines if a given string represents a StringList
func IsStringList(s string) bool {
	return strings.Contains(s, stringListDelim)
}
<|endoftext|>


<commit_before>package consul

import (
	"fmt"
	"github.com/hashicorp/consul/testutil"
	"github.com/hashicorp/consul/consul/structs"
	"github.com/hashicorp/serf/serf"
	"os"
	"testing"
	"errors"
)

func TestLeader_RegisterMember(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, c1 := testClient(t)
	defer os.RemoveAll(dir2)
	defer c1.Shutdown()

	// Try to join
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := c1.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	client := rpcClient(t, s1)
	testutil.WaitForLeader(t, client.Call, "dc1")

	// Client should be registered
	state := s1.fsm.State()
	_, found, _ := state.GetNode(c1.config.NodeName)
	if !found {
		t.Fatalf("client not registered")
	}

	// Should have a check
	_, checks := state.NodeChecks(c1.config.NodeName)
	if len(checks) != 1 {
		t.Fatalf("client missing check")
	}
	if checks[0].CheckID != SerfCheckID {
		t.Fatalf("bad check: %v", checks[0])
	}
	if checks[0].Name != SerfCheckName {
		t.Fatalf("bad check: %v", checks[0])
	}
	if checks[0].Status != structs.HealthPassing {
		t.Fatalf("bad check: %v", checks[0])
	}

	// Server should be registered
	_, found, _ = state.GetNode(s1.config.NodeName)
	if !found {
		t.Fatalf("server not registered")
	}

	// Service should be registered
	_, services := state.NodeServices(s1.config.NodeName)
	if _, ok := services.Services["consul"]; !ok {
		t.Fatalf("consul service not registered: %v", services)
	}
}

func TestLeader_FailedMember(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, c1 := testClient(t)
	defer os.RemoveAll(dir2)
	defer c1.Shutdown()

	client := rpcClient(t, s1)
	testutil.WaitForLeader(t, client.Call, "dc1")

	// Try to join
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := c1.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Fail the member
	c1.Shutdown()

	// Should be registered
	state := s1.fsm.State()
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ := state.GetNode(c1.config.NodeName)
		return found == true, nil
	}, func(err error) {
		t.Fatalf("client not registered")
	})

	// Should have a check
	_, checks := state.NodeChecks(c1.config.NodeName)
	if len(checks) != 1 {
		t.Fatalf("client missing check")
	}
	if checks[0].CheckID != SerfCheckID {
		t.Fatalf("bad check: %v", checks[0])
	}
	if checks[0].Name != SerfCheckName {
		t.Fatalf("bad check: %v", checks[0])
	}

	testutil.WaitForResult(func() (bool, error) {
		_, checks = state.NodeChecks(c1.config.NodeName)
		return checks[0].Status == structs.HealthCritical, errors.New(checks[0].Status)
	}, func(err error) {
		t.Fatalf("check status is %v, should be critical", err)
	})
}

func TestLeader_LeftMember(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, c1 := testClient(t)
	defer os.RemoveAll(dir2)
	defer c1.Shutdown()

	// Try to join
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := c1.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	var found bool
	state := s1.fsm.State()

	// Should be registered
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ = state.GetNode(c1.config.NodeName)
		return found == true, nil
	}, func(err error) {
		t.Fatalf("client should be registered")
	})

	// Node should leave
	c1.Leave()
	c1.Shutdown()

	// Should be deregistered
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ = state.GetNode(c1.config.NodeName)
		return found == false, nil
	}, func(err error) {
		t.Fatalf("client should not be registered")
	})
}

func TestLeader_ReapMember(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, c1 := testClient(t)
	defer os.RemoveAll(dir2)
	defer c1.Shutdown()

	// Try to join
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := c1.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	var found bool
	state := s1.fsm.State()

	// Should be registered
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ = state.GetNode(c1.config.NodeName)
		return found == true, nil
	}, func(err error) {
		t.Fatalf("client should be registered")
	})

	// Simulate a node reaping
	mems := s1.LANMembers()
	var c1mem serf.Member
	for _, m := range mems {
		if m.Name == c1.config.NodeName {
			c1mem = m
			c1mem.Status = StatusReap
			break
		}
	}
	s1.reconcileCh <- c1mem

	// Should be deregistered
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ = state.GetNode(c1.config.NodeName)
		return found == false, nil
	}, func(err error) {
		t.Fatalf("client should not be registered")
	})
}

func TestLeader_Reconcile_ReapMember(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	client := rpcClient(t, s1)
	testutil.WaitForLeader(t, client.Call, "dc1")

	// Register a non-existing member
	dead := structs.RegisterRequest{
		Datacenter: s1.config.Datacenter,
		Node:       "no-longer-around",
		Address:    "127.1.1.1",
		Check: &structs.HealthCheck{
			Node:    "no-longer-around",
			CheckID: SerfCheckID,
			Name:    SerfCheckName,
			Status:  structs.HealthCritical,
		},
	}
	var out struct{}
	if err := s1.RPC("Catalog.Register", &dead, &out); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Force a reconciliation
	if err := s1.reconcile(); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Node should be gone
	state := s1.fsm.State()
	_, found, _ := state.GetNode("no-longer-around")
	if found {
		t.Fatalf("client registered")
	}
}

func TestLeader_Reconcile(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, c1 := testClient(t)
	defer os.RemoveAll(dir2)
	defer c1.Shutdown()

	// Join before we have a leader, this should cause a reconcile!
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := c1.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Should not be registered
	state := s1.fsm.State()
	_, found, _ := state.GetNode(c1.config.NodeName)
	if found {
		t.Fatalf("client registered")
	}

	// Should be registered
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ = state.GetNode(c1.config.NodeName)
		return found == true, nil
	}, func(err error) {
		t.Fatalf("client should be registered")
	})
}

func TestLeader_LeftServer(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, s2 := testServerDCBootstrap(t, "dc1", false)
	defer os.RemoveAll(dir2)
	defer s2.Shutdown()

	dir3, s3 := testServerDCBootstrap(t, "dc1", false)
	defer os.RemoveAll(dir3)
	defer s3.Shutdown()
	servers := []*Server{s1, s2, s3}

	// Try to join
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := s2.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}
	if _, err := s3.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	for _, s := range servers {
		testutil.WaitForResult(func() (bool, error) {
			peers, _ := s.raftPeers.Peers()
			return len(peers) == 3, nil
		}, func(err error) {
			t.Fatalf("should have 3 peers")
		})
	}

	// Kill any server
	servers[0].Shutdown()

	// Force remove the non-leader (transition to left state)
	if err := servers[1].RemoveFailedNode(servers[0].config.NodeName); err != nil {
		t.Fatalf("err: %v", err)
	}

	for _, s := range servers[1:] {
		testutil.WaitForResult(func() (bool, error) {
			peers, _ := s.raftPeers.Peers()
			return len(peers) == 2, nil
		}, func(err error) {
			t.Fatalf("should have 2 peers")
		})
	}
}

func TestLeader_MultiBootstrap(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, s2 := testServer(t)
	defer os.RemoveAll(dir2)
	defer s2.Shutdown()

	servers := []*Server{s1, s2}

	// Try to join
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := s2.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	for _, s := range servers {
		testutil.WaitForResult(func() (bool, error) {
			peers := s.serfLAN.Members()
			return len(peers) == 2, nil
		}, func(err error) {
			t.Fatalf("should have 2 peers")
		})
	}

	// Ensure we don't have multiple raft peers
	for _, s := range servers {
		peers, _ := s.raftPeers.Peers()
		if len(peers) != 1 {
			t.Fatalf("should only have 1 raft peer!")
		}
	}
}
<commit_msg>Fix race condition in `TestLeader_RegisterMember`<commit_after>package consul

import (
	"fmt"
	"github.com/hashicorp/consul/testutil"
	"github.com/hashicorp/consul/consul/structs"
	"github.com/hashicorp/serf/serf"
	"os"
	"testing"
	"errors"
)

func TestLeader_RegisterMember(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, c1 := testClient(t)
	defer os.RemoveAll(dir2)
	defer c1.Shutdown()

	// Try to join
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := c1.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	client := rpcClient(t, s1)
	testutil.WaitForLeader(t, client.Call, "dc1")

	// Client should be registered
	state := s1.fsm.State()
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ := state.GetNode(c1.config.NodeName)
		return found == true, nil
	}, func(err error) {
		t.Fatalf("client not registered")
	})

	// Should have a check
	_, checks := state.NodeChecks(c1.config.NodeName)
	if len(checks) != 1 {
		t.Fatalf("client missing check")
	}
	if checks[0].CheckID != SerfCheckID {
		t.Fatalf("bad check: %v", checks[0])
	}
	if checks[0].Name != SerfCheckName {
		t.Fatalf("bad check: %v", checks[0])
	}
	if checks[0].Status != structs.HealthPassing {
		t.Fatalf("bad check: %v", checks[0])
	}

	// Server should be registered
	_, found, _ := state.GetNode(s1.config.NodeName)
	if !found {
		t.Fatalf("server not registered")
	}

	// Service should be registered
	_, services := state.NodeServices(s1.config.NodeName)
	if _, ok := services.Services["consul"]; !ok {
		t.Fatalf("consul service not registered: %v", services)
	}
}

func TestLeader_FailedMember(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, c1 := testClient(t)
	defer os.RemoveAll(dir2)
	defer c1.Shutdown()

	client := rpcClient(t, s1)
	testutil.WaitForLeader(t, client.Call, "dc1")

	// Try to join
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := c1.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Fail the member
	c1.Shutdown()

	// Should be registered
	state := s1.fsm.State()
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ := state.GetNode(c1.config.NodeName)
		return found == true, nil
	}, func(err error) {
		t.Fatalf("client not registered")
	})

	// Should have a check
	_, checks := state.NodeChecks(c1.config.NodeName)
	if len(checks) != 1 {
		t.Fatalf("client missing check")
	}
	if checks[0].CheckID != SerfCheckID {
		t.Fatalf("bad check: %v", checks[0])
	}
	if checks[0].Name != SerfCheckName {
		t.Fatalf("bad check: %v", checks[0])
	}

	testutil.WaitForResult(func() (bool, error) {
		_, checks = state.NodeChecks(c1.config.NodeName)
		return checks[0].Status == structs.HealthCritical, errors.New(checks[0].Status)
	}, func(err error) {
		t.Fatalf("check status is %v, should be critical", err)
	})
}

func TestLeader_LeftMember(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, c1 := testClient(t)
	defer os.RemoveAll(dir2)
	defer c1.Shutdown()

	// Try to join
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := c1.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	var found bool
	state := s1.fsm.State()

	// Should be registered
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ = state.GetNode(c1.config.NodeName)
		return found == true, nil
	}, func(err error) {
		t.Fatalf("client should be registered")
	})

	// Node should leave
	c1.Leave()
	c1.Shutdown()

	// Should be deregistered
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ = state.GetNode(c1.config.NodeName)
		return found == false, nil
	}, func(err error) {
		t.Fatalf("client should not be registered")
	})
}

func TestLeader_ReapMember(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, c1 := testClient(t)
	defer os.RemoveAll(dir2)
	defer c1.Shutdown()

	// Try to join
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := c1.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	var found bool
	state := s1.fsm.State()

	// Should be registered
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ = state.GetNode(c1.config.NodeName)
		return found == true, nil
	}, func(err error) {
		t.Fatalf("client should be registered")
	})

	// Simulate a node reaping
	mems := s1.LANMembers()
	var c1mem serf.Member
	for _, m := range mems {
		if m.Name == c1.config.NodeName {
			c1mem = m
			c1mem.Status = StatusReap
			break
		}
	}
	s1.reconcileCh <- c1mem

	// Should be deregistered
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ = state.GetNode(c1.config.NodeName)
		return found == false, nil
	}, func(err error) {
		t.Fatalf("client should not be registered")
	})
}

func TestLeader_Reconcile_ReapMember(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	client := rpcClient(t, s1)
	testutil.WaitForLeader(t, client.Call, "dc1")

	// Register a non-existing member
	dead := structs.RegisterRequest{
		Datacenter: s1.config.Datacenter,
		Node:       "no-longer-around",
		Address:    "127.1.1.1",
		Check: &structs.HealthCheck{
			Node:    "no-longer-around",
			CheckID: SerfCheckID,
			Name:    SerfCheckName,
			Status:  structs.HealthCritical,
		},
	}
	var out struct{}
	if err := s1.RPC("Catalog.Register", &dead, &out); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Force a reconciliation
	if err := s1.reconcile(); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Node should be gone
	state := s1.fsm.State()
	_, found, _ := state.GetNode("no-longer-around")
	if found {
		t.Fatalf("client registered")
	}
}

func TestLeader_Reconcile(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, c1 := testClient(t)
	defer os.RemoveAll(dir2)
	defer c1.Shutdown()

	// Join before we have a leader, this should cause a reconcile!
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := c1.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Should not be registered
	state := s1.fsm.State()
	_, found, _ := state.GetNode(c1.config.NodeName)
	if found {
		t.Fatalf("client registered")
	}

	// Should be registered
	testutil.WaitForResult(func() (bool, error) {
		_, found, _ = state.GetNode(c1.config.NodeName)
		return found == true, nil
	}, func(err error) {
		t.Fatalf("client should be registered")
	})
}

func TestLeader_LeftServer(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, s2 := testServerDCBootstrap(t, "dc1", false)
	defer os.RemoveAll(dir2)
	defer s2.Shutdown()

	dir3, s3 := testServerDCBootstrap(t, "dc1", false)
	defer os.RemoveAll(dir3)
	defer s3.Shutdown()
	servers := []*Server{s1, s2, s3}

	// Try to join
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := s2.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}
	if _, err := s3.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	for _, s := range servers {
		testutil.WaitForResult(func() (bool, error) {
			peers, _ := s.raftPeers.Peers()
			return len(peers) == 3, nil
		}, func(err error) {
			t.Fatalf("should have 3 peers")
		})
	}

	// Kill any server
	servers[0].Shutdown()

	// Force remove the non-leader (transition to left state)
	if err := servers[1].RemoveFailedNode(servers[0].config.NodeName); err != nil {
		t.Fatalf("err: %v", err)
	}

	for _, s := range servers[1:] {
		testutil.WaitForResult(func() (bool, error) {
			peers, _ := s.raftPeers.Peers()
			return len(peers) == 2, nil
		}, func(err error) {
			t.Fatalf("should have 2 peers")
		})
	}
}

func TestLeader_MultiBootstrap(t *testing.T) {
	dir1, s1 := testServer(t)
	defer os.RemoveAll(dir1)
	defer s1.Shutdown()

	dir2, s2 := testServer(t)
	defer os.RemoveAll(dir2)
	defer s2.Shutdown()

	servers := []*Server{s1, s2}

	// Try to join
	addr := fmt.Sprintf("127.0.0.1:%d",
		s1.config.SerfLANConfig.MemberlistConfig.BindPort)
	if _, err := s2.JoinLAN([]string{addr}); err != nil {
		t.Fatalf("err: %v", err)
	}

	for _, s := range servers {
		testutil.WaitForResult(func() (bool, error) {
			peers := s.serfLAN.Members()
			return len(peers) == 2, nil
		}, func(err error) {
			t.Fatalf("should have 2 peers")
		})
	}

	// Ensure we don't have multiple raft peers
	for _, s := range servers {
		peers, _ := s.raftPeers.Peers()
		if len(peers) != 1 {
			t.Fatalf("should only have 1 raft peer!")
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package scanner

import (
	"fmt"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
)

// tokenType identifies the type of lexical tokens.
type tokenType int

// String returns a string representation of the token type.
func (t tokenType) String() string {
	return tokenNames[t]
}

// Token represents a token and the corresponding string.
type Token struct {
	Type   tokenType
	Value  string
	Line   int
	Column int
}

// String returns a string representation of the token.
func (t *Token) String() string {
	if len(t.Value) > 10 {
		return fmt.Sprintf("%s (line: %d, column: %d): %.10q...",
			t.Type, t.Line, t.Column, t.Value)
	}
	return fmt.Sprintf("%s (line: %d, column: %d): %q",
		t.Type, t.Line, t.Column, t.Value)
}

// All tokens -----------------------------------------------------------------

// The complete list of tokens in CSS3.
const (
	// Scanner flags.
	TokenError tokenType = iota
	TokenEOF
	// From now on, only tokens from the CSS specification.
	TokenIdent
	TokenAtKeyword
	TokenString
	TokenHash
	TokenNumber
	TokenPercentage
	TokenDimension
	TokenURI
	TokenUnicodeRange
	TokenCDO
	TokenCDC
	TokenS
	TokenComment
	TokenFunction
	TokenIncludes
	TokenDashMatch
	TokenPrefixMatch
	TokenSuffixMatch
	TokenSubstringMatch
	TokenChar
	TokenBOM
)

// tokenNames maps tokenType's to their names. Used for conversion to string.
var tokenNames = map[tokenType]string {
	TokenError:          "error",
	TokenEOF:            "EOF",
	TokenIdent:          "IDENT",
	TokenAtKeyword:      "ATKEYWORD",
	TokenString:         "STRING",
	TokenHash:           "HASH",
	TokenNumber:         "NUMBER",
	TokenPercentage:     "PERCENTAGE",
	TokenDimension:      "DIMENSION",
	TokenURI:            "URI",
	TokenUnicodeRange:   "UNICODE-RANGE",
	TokenCDO:            "CDO",
	TokenCDC:            "CDC",
	TokenS:              "S",
	TokenComment:        "COMMENT",
	TokenFunction:       "FUNCTION",
	TokenIncludes:       "INCLUDES",
	TokenDashMatch:      "DASHMATCH",
	TokenPrefixMatch:    "PREFIXMATCH",
	TokenSuffixMatch:    "SUFFIXMATCH",
	TokenSubstringMatch: "SUBSTRINGMATCH",
	TokenChar:           "CHAR",
	TokenBOM:            "BOM",
}

// Macros and productions -----------------------------------------------------
// http://www.w3.org/TR/css3-syntax/#tokenization

var macroRegexp = regexp.MustCompile(`\{[a-z]+\}`)

// macros maps macro names to patterns to be expanded.
var macros = map[string]string {
	// must be escaped: `\.+*?()|[]{}^$`
	"ident":      `-?{nmstart}{nmchar}*`,
	"name":       `{nmchar}+`,
	"nmstart":    `[a-zA-Z_]|{nonascii}|{escape}`,
	"nonascii":   "[\u0080-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]",
	"unicode":    `\\[0-9a-fA-F]{1,6}{wc}?`,
	"escape":     "{unicode}|\\[\u0020-\u007E\u0080-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]",
	"nmchar":     `[a-zA-Z0-9_-]|{nonascii}|{escape}`,
	"num":	      `[0-9]+|[0-9]*\.[0-9]+`,
	"string":	  `"(?:{stringchar}|')*"|'(?:{stringchar}|")*'`,
	"stringchar": `{urlchar}|[ ]|\\{nl}`,
	"urlchar":    "[\u0009\u0021\u0023-\u0026\u0027-\u007E]|{nonascii}|{escape}",
	"nl":	      `[\n\r\f]|\r\n`,
	"w":	      `{wc}*`,
	"wc":	      `[\t\n\f\r ]`,
}

// productions maps the list of tokens to patterns to be expanded.
var productions = map[tokenType]string {
	TokenIdent:          `{ident}`,
	TokenAtKeyword:      `@{ident}`,
	TokenString:         `{string}`,
	TokenHash:           `#{name}`,
	TokenNumber:         `{num}`,
	TokenPercentage:     `{num}%`,
	TokenDimension:      `{num}{ident}`,
	TokenURI:            `url\({w}(?:{string}|{urlchar}*){w}\)`,
	TokenUnicodeRange:   `U\+[0-9A-F\?]{1,6}(?:-[0-9A-F]{1,6})?`,
	TokenCDO:            `<!--`,
	TokenCDC:            `-->`,
	TokenS:              `{wc}+`,
	TokenComment:        `/\*[^\*]*[\*]+(?:[^/][^\*]*[\*]+)*/`,
	TokenFunction:       `{ident}\(`,
	TokenIncludes:       `~=`,
	TokenDashMatch:      `\|=`,
	TokenPrefixMatch:    `\^=`,
	TokenSuffixMatch:    `\$=`,
	TokenSubstringMatch: `\*=`,
	TokenChar:           `[^"']`,
	TokenBOM:            "\uFEFF",
}

// matchers maps the list of tokens to compiled regular expressions.
//
// The map is filled on init() using the macros and productions defined in
// the CSS specification.
var matchers = map[tokenType]*regexp.Regexp {
}

var matchOrder = []tokenType {
	// The ones scanned using first-char shortcut are commented out.
	//TokenS,
	TokenURI,
	TokenFunction,
	TokenUnicodeRange,
	TokenIdent,
	TokenDimension,
	TokenPercentage,
	TokenNumber,
	//TokenHash,
	TokenComment,
	//TokenString,
	TokenAtKeyword,
	//TokenIncludes,
	//TokenDashMatch,
	//TokenPrefixMatch,
	//TokenSuffixMatch,
	//TokenSubstringMatch,
	//TokenCDO,
	TokenCDC,
	TokenChar,
	//TokenBOM
}

func init() {
	// replace macros and compile regexps for productions.
	replaceMacro := func(s string) string {
		return "(?:" + macros[s[1:len(s)-1]] + ")"
	}
	for t, s := range productions {
		for macroRegexp.MatchString(s) {
			s = macroRegexp.ReplaceAllStringFunc(s, replaceMacro)
		}
		matchers[t] = regexp.MustCompile("^(?:" + s + ")")
	}
}

// Scanner --------------------------------------------------------------------

// New returns a new CSS scanner for the given input.
func New(input string) *Scanner {
	// Normalize newlines.
	input = strings.Replace(input, "\r\n", "\n", -1)
	return &Scanner{
		input:  input,
		line:   1,
		column: 1,
	}
}

// Scanner scans an input and emits tokens following the CSS3 specification.
type Scanner struct {
	input  string
	pos    int
	line   int
	column int
	last   *Token
}

// Next returns the next token from the input.
//
// At the end of the input the token type is TokenEOF.
//
// If the input can't be tokenized the token type is TokenError. This occurs
// in case of unclosed quotation marks or comments.
func (s *Scanner) Next() *Token {
	if s.last != nil {
		return s.last
	}
	if s.pos >= len(s.input) {
		s.last = &Token{TokenEOF, "", -1, -1}
		return s.last
	}
	input := s.input[s.pos:]
	if s.pos == 0 {
		// Test BOM only at the beginning of the file.
		if strings.HasPrefix(input, "\uFEFF") {
			return s.emitToken(TokenBOM, "\uFEFF")
		}
	}
	// There's a lot we can guess based on the current rune so we'll take this
	// shortcut before testing multiple regexp's.
	r := input[0]
	switch r {
	case '\t', '\n', '\f', '\r', ' ':
		// Whitespace.
		return s.emitToken(TokenS, matchers[TokenS].FindString(input))
	case '.':
		// Dot is too common to not have a quick check.
		// We'll test if this is a Char; if it is followed by a number it is a
		// dimension/percentage/number, and this will be matched later.
		if len(input) > 1 && !unicode.IsDigit(rune(input[1])) {
			return s.emitToken(TokenChar, ".")
		}
	case '#':
		// Hash is also a common one. If the regexp doesn't match it is a Char.
		if match := matchers[TokenHash].FindString(input); match != "" {
			return s.emitToken(TokenHash, match)
		}
		return s.emitToken(TokenChar, "#")
	case ':', ',', ';', '%', '&', '+', '=', '>', '(', ')', '[', ']', '{', '}':
		// Other common chars.
		return s.emitToken(TokenChar, string(r))
	case '"', '\'':
		// String or error.
		match := matchers[TokenString].FindString(input)
		if match != "" {
			return s.emitToken(TokenString, match)
		} else {
			s.last = s.emitToken(TokenError, "unclosed quotation mark")
			return s.last
		}
	case '~':
		// Includes or Char.
		return s.emitPrefixOrChar(TokenIncludes, "~=")
	case '|':
		// DashMatch or Char.
		return s.emitPrefixOrChar(TokenDashMatch, "|=")
	case '^':
		// PrefixMatch or Char.
		return s.emitPrefixOrChar(TokenPrefixMatch, "^=")
	case '$':
		// SuffixMatch or Char.
		return s.emitPrefixOrChar(TokenSuffixMatch, "$=")
	case '*':
		// SubstringMatch or Char.
		return s.emitPrefixOrChar(TokenSubstringMatch, "*=")
	case '<':
		// CDO or Char.
		return s.emitPrefixOrChar(TokenCDO, "<!--")
	}
	// Test all regexps, in order.
	for _, token := range matchOrder {
		if match := matchers[token].FindString(input); match != "" {
			return s.emitToken(token, match)
		}
	}
	s.last = s.emitToken(TokenError, "impossible to tokenize")
	return s.last
}

// updatePosition updates input coordinates based on the consumed text.
func (s *Scanner) updatePosition(text string) {
	count := utf8.RuneCountInString(text)
	lines := strings.Count(text, "\n")
	s.line += lines
	if lines == 0 {
		s.column += count
	} else {
		s.column = utf8.RuneCountInString(text[strings.LastIndex(text, "\n"):])
	}
	s.pos += count
}

// emitToken returns a Token for the string v and updates the scanner position.
func (s *Scanner) emitToken(t tokenType, v string) *Token {
	token := &Token{t, v, s.line, s.column}
	s.updatePosition(v)
	return token
}

// emitPrefixOrChar returns a Token for type t if the current position
// matches the given prefix. Otherwise it returns a Char token using the
// first character from the prefix.
func (s *Scanner) emitPrefixOrChar(t tokenType, prefix string) *Token {
	if strings.HasPrefix(s.input[s.pos:], prefix) {
		return s.emitToken(t, prefix)
	}
	return s.emitToken(TokenChar, string(prefix[0]))
}
<commit_msg>go fmt<commit_after>// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package scanner

import (
	"fmt"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
)

// tokenType identifies the type of lexical tokens.
type tokenType int

// String returns a string representation of the token type.
func (t tokenType) String() string {
	return tokenNames[t]
}

// Token represents a token and the corresponding string.
type Token struct {
	Type   tokenType
	Value  string
	Line   int
	Column int
}

// String returns a string representation of the token.
func (t *Token) String() string {
	if len(t.Value) > 10 {
		return fmt.Sprintf("%s (line: %d, column: %d): %.10q...",
			t.Type, t.Line, t.Column, t.Value)
	}
	return fmt.Sprintf("%s (line: %d, column: %d): %q",
		t.Type, t.Line, t.Column, t.Value)
}

// All tokens -----------------------------------------------------------------

// The complete list of tokens in CSS3.
const (
	// Scanner flags.
	TokenError tokenType = iota
	TokenEOF
	// From now on, only tokens from the CSS specification.
	TokenIdent
	TokenAtKeyword
	TokenString
	TokenHash
	TokenNumber
	TokenPercentage
	TokenDimension
	TokenURI
	TokenUnicodeRange
	TokenCDO
	TokenCDC
	TokenS
	TokenComment
	TokenFunction
	TokenIncludes
	TokenDashMatch
	TokenPrefixMatch
	TokenSuffixMatch
	TokenSubstringMatch
	TokenChar
	TokenBOM
)

// tokenNames maps tokenType's to their names. Used for conversion to string.
var tokenNames = map[tokenType]string{
	TokenError:          "error",
	TokenEOF:            "EOF",
	TokenIdent:          "IDENT",
	TokenAtKeyword:      "ATKEYWORD",
	TokenString:         "STRING",
	TokenHash:           "HASH",
	TokenNumber:         "NUMBER",
	TokenPercentage:     "PERCENTAGE",
	TokenDimension:      "DIMENSION",
	TokenURI:            "URI",
	TokenUnicodeRange:   "UNICODE-RANGE",
	TokenCDO:            "CDO",
	TokenCDC:            "CDC",
	TokenS:              "S",
	TokenComment:        "COMMENT",
	TokenFunction:       "FUNCTION",
	TokenIncludes:       "INCLUDES",
	TokenDashMatch:      "DASHMATCH",
	TokenPrefixMatch:    "PREFIXMATCH",
	TokenSuffixMatch:    "SUFFIXMATCH",
	TokenSubstringMatch: "SUBSTRINGMATCH",
	TokenChar:           "CHAR",
	TokenBOM:            "BOM",
}

// Macros and productions -----------------------------------------------------
// http://www.w3.org/TR/css3-syntax/#tokenization

var macroRegexp = regexp.MustCompile(`\{[a-z]+\}`)

// macros maps macro names to patterns to be expanded.
var macros = map[string]string{
	// must be escaped: `\.+*?()|[]{}^$`
	"ident":      `-?{nmstart}{nmchar}*`,
	"name":       `{nmchar}+`,
	"nmstart":    `[a-zA-Z_]|{nonascii}|{escape}`,
	"nonascii":   "[\u0080-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]",
	"unicode":    `\\[0-9a-fA-F]{1,6}{wc}?`,
	"escape":     "{unicode}|\\[\u0020-\u007E\u0080-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]",
	"nmchar":     `[a-zA-Z0-9_-]|{nonascii}|{escape}`,
	"num":        `[0-9]+|[0-9]*\.[0-9]+`,
	"string":     `"(?:{stringchar}|')*"|'(?:{stringchar}|")*'`,
	"stringchar": `{urlchar}|[ ]|\\{nl}`,
	"urlchar":    "[\u0009\u0021\u0023-\u0026\u0027-\u007E]|{nonascii}|{escape}",
	"nl":         `[\n\r\f]|\r\n`,
	"w":          `{wc}*`,
	"wc":         `[\t\n\f\r ]`,
}

// productions maps the list of tokens to patterns to be expanded.
var productions = map[tokenType]string{
	TokenIdent:          `{ident}`,
	TokenAtKeyword:      `@{ident}`,
	TokenString:         `{string}`,
	TokenHash:           `#{name}`,
	TokenNumber:         `{num}`,
	TokenPercentage:     `{num}%`,
	TokenDimension:      `{num}{ident}`,
	TokenURI:            `url\({w}(?:{string}|{urlchar}*){w}\)`,
	TokenUnicodeRange:   `U\+[0-9A-F\?]{1,6}(?:-[0-9A-F]{1,6})?`,
	TokenCDO:            `<!--`,
	TokenCDC:            `-->`,
	TokenS:              `{wc}+`,
	TokenComment:        `/\*[^\*]*[\*]+(?:[^/][^\*]*[\*]+)*/`,
	TokenFunction:       `{ident}\(`,
	TokenIncludes:       `~=`,
	TokenDashMatch:      `\|=`,
	TokenPrefixMatch:    `\^=`,
	TokenSuffixMatch:    `\$=`,
	TokenSubstringMatch: `\*=`,
	TokenChar:           `[^"']`,
	TokenBOM:            "\uFEFF",
}

// matchers maps the list of tokens to compiled regular expressions.
//
// The map is filled on init() using the macros and productions defined in
// the CSS specification.
var matchers = map[tokenType]*regexp.Regexp{}

var matchOrder = []tokenType{
	// The ones scanned using first-char shortcut are commented out.
	//TokenS,
	TokenURI,
	TokenFunction,
	TokenUnicodeRange,
	TokenIdent,
	TokenDimension,
	TokenPercentage,
	TokenNumber,
	//TokenHash,
	TokenComment,
	//TokenString,
	TokenAtKeyword,
	//TokenIncludes,
	//TokenDashMatch,
	//TokenPrefixMatch,
	//TokenSuffixMatch,
	//TokenSubstringMatch,
	//TokenCDO,
	TokenCDC,
	TokenChar,
	//TokenBOM
}

func init() {
	// replace macros and compile regexps for productions.
	replaceMacro := func(s string) string {
		return "(?:" + macros[s[1:len(s)-1]] + ")"
	}
	for t, s := range productions {
		for macroRegexp.MatchString(s) {
			s = macroRegexp.ReplaceAllStringFunc(s, replaceMacro)
		}
		matchers[t] = regexp.MustCompile("^(?:" + s + ")")
	}
}

// Scanner --------------------------------------------------------------------

// New returns a new CSS scanner for the given input.
func New(input string) *Scanner {
	// Normalize newlines.
	input = strings.Replace(input, "\r\n", "\n", -1)
	return &Scanner{
		input:  input,
		line:   1,
		column: 1,
	}
}

// Scanner scans an input and emits tokens following the CSS3 specification.
type Scanner struct {
	input  string
	pos    int
	line   int
	column int
	last   *Token
}

// Next returns the next token from the input.
//
// At the end of the input the token type is TokenEOF.
//
// If the input can't be tokenized the token type is TokenError. This occurs
// in case of unclosed quotation marks or comments.
func (s *Scanner) Next() *Token {
	if s.last != nil {
		return s.last
	}
	if s.pos >= len(s.input) {
		s.last = &Token{TokenEOF, "", -1, -1}
		return s.last
	}
	input := s.input[s.pos:]
	if s.pos == 0 {
		// Test BOM only at the beginning of the file.
		if strings.HasPrefix(input, "\uFEFF") {
			return s.emitToken(TokenBOM, "\uFEFF")
		}
	}
	// There's a lot we can guess based on the current rune so we'll take this
	// shortcut before testing multiple regexp's.
	r := input[0]
	switch r {
	case '\t', '\n', '\f', '\r', ' ':
		// Whitespace.
		return s.emitToken(TokenS, matchers[TokenS].FindString(input))
	case '.':
		// Dot is too common to not have a quick check.
		// We'll test if this is a Char; if it is followed by a number it is a
		// dimension/percentage/number, and this will be matched later.
		if len(input) > 1 && !unicode.IsDigit(rune(input[1])) {
			return s.emitToken(TokenChar, ".")
		}
	case '#':
		// Hash is also a common one. If the regexp doesn't match it is a Char.
		if match := matchers[TokenHash].FindString(input); match != "" {
			return s.emitToken(TokenHash, match)
		}
		return s.emitToken(TokenChar, "#")
	case ':', ',', ';', '%', '&', '+', '=', '>', '(', ')', '[', ']', '{', '}':
		// Other common chars.
		return s.emitToken(TokenChar, string(r))
	case '"', '\'':
		// String or error.
		match := matchers[TokenString].FindString(input)
		if match != "" {
			return s.emitToken(TokenString, match)
		} else {
			s.last = s.emitToken(TokenError, "unclosed quotation mark")
			return s.last
		}
	case '~':
		// Includes or Char.
		return s.emitPrefixOrChar(TokenIncludes, "~=")
	case '|':
		// DashMatch or Char.
		return s.emitPrefixOrChar(TokenDashMatch, "|=")
	case '^':
		// PrefixMatch or Char.
		return s.emitPrefixOrChar(TokenPrefixMatch, "^=")
	case '$':
		// SuffixMatch or Char.
		return s.emitPrefixOrChar(TokenSuffixMatch, "$=")
	case '*':
		// SubstringMatch or Char.
		return s.emitPrefixOrChar(TokenSubstringMatch, "*=")
	case '<':
		// CDO or Char.
		return s.emitPrefixOrChar(TokenCDO, "<!--")
	}
	// Test all regexps, in order.
	for _, token := range matchOrder {
		if match := matchers[token].FindString(input); match != "" {
			return s.emitToken(token, match)
		}
	}
	s.last = s.emitToken(TokenError, "impossible to tokenize")
	return s.last
}

// updatePosition updates input coordinates based on the consumed text.
func (s *Scanner) updatePosition(text string) {
	count := utf8.RuneCountInString(text)
	lines := strings.Count(text, "\n")
	s.line += lines
	if lines == 0 {
		s.column += count
	} else {
		s.column = utf8.RuneCountInString(text[strings.LastIndex(text, "\n"):])
	}
	s.pos += count
}

// emitToken returns a Token for the string v and updates the scanner position.
func (s *Scanner) emitToken(t tokenType, v string) *Token {
	token := &Token{t, v, s.line, s.column}
	s.updatePosition(v)
	return token
}

// emitPrefixOrChar returns a Token for type t if the current position
// matches the given prefix. Otherwise it returns a Char token using the
// first character from the prefix.
func (s *Scanner) emitPrefixOrChar(t tokenType, prefix string) *Token {
	if strings.HasPrefix(s.input[s.pos:], prefix) {
		return s.emitToken(t, prefix)
	}
	return s.emitToken(TokenChar, string(prefix[0]))
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"
	"os/signal"
	"strconv"
	"syscall"

	"net"

	"github.com/ellcrys/util"
	"github.com/ncodes/cocoon/core/common"
	"github.com/ncodes/cocoon/core/config"
	"github.com/ncodes/cocoon/core/connector"
	"github.com/ncodes/cocoon/core/connector/server"
	"github.com/ncodes/cocoon/core/scheduler"
	logging "github.com/op/go-logging"
	"github.com/spf13/cobra"
)

var (
	log = logging.MustGetLogger("connector")

	// connector RPC API
	defaultConnectorRPCAPI = util.Env("DEV_ADDR_CONNECTOR_RPC", ":8002")

	// Signals channel
	sigs = make(chan os.Signal, 1)
)

func init() {
	config.ConfigureLogger()
}

// creates a deployment request with argument
// fetched from the environment.
func getRequest() (*connector.Request, error) {

	// get cocoon code github link and language
	ccID := os.Getenv("COCOON_ID")
	ccURL := os.Getenv("COCOON_CODE_URL")
	ccTag := os.Getenv("COCOON_CODE_TAG")
	ccLang := os.Getenv("COCOON_CODE_LANG")
	diskLimit := util.Env("COCOON_DISK_LIMIT", "300")
	buildParam := os.Getenv("COCOON_BUILD_PARAMS")
	ccLink := os.Getenv("COCOON_LINK")
	memoryMB, _ := strconv.Atoi(util.Env("ALLOC_MEMORY", "4"))
	cpuShare, _ := strconv.Atoi(util.Env("ALLOC_CPU_SHARE", "100"))

	if ccID == "" {
		return nil, fmt.Errorf("Cocoon code id not set @ $COCOON_ID")
	} else if ccURL == "" {
		return nil, fmt.Errorf("Cocoon code url not set @ $COCOON_CODE_URL")
	} else if ccLang == "" {
		return nil, fmt.Errorf("Cocoon code url not set @ $COCOON_CODE_LANG")
	}

	return &connector.Request{
		ID:          ccID,
		URL:         ccURL,
		Tag:         ccTag,
		Lang:        ccLang,
		DiskLimit:   common.MBToByte(util.ToInt64(diskLimit)),
		BuildParams: buildParam,
		Link:        ccLink,
		Memory:      int64(memoryMB),
		CPUShares:   int64(cpuShare),
	}, nil
}

// onTerminate calls a function when a terminate or interrupt signal is received.
func onTerminate(f func(s os.Signal)) {
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		s := <-sigs
		f(s)
	}()
}

// connectorCmd represents the connector command
var connectorCmd = &cobra.Command{
	Use:   "connector",
	Short: "Start the connector",
	Long:  `Starts the connector and launches a cocoon code.`,
	Run: func(cmd *cobra.Command, args []string) {

		waitCh := make(chan bool, 1)
		serverStartedCh := make(chan bool)

		log.Info("Connector has started")

		// get request
		log.Info("Collecting and validating launch request")
		req, err := getRequest()
		if err != nil {
			log.Error(err.Error())
			return
		}
		log.Infof("Ready to launch cocoon code with id = %s", req.ID)

		connectorRPCAddr := scheduler.Getenv("ADDR_CONNECTOR_RPC", defaultConnectorRPCAPI)
		cocoonCodeRPCAddr := net.JoinHostPort("", scheduler.Getenv("PORT_COCOON_RPC", "8004"))

		cn := connector.NewConnector(req, waitCh)
		cn.SetAddrs(connectorRPCAddr, cocoonCodeRPCAddr)
		cn.AddLanguage(connector.NewGo(req))
		onTerminate(func(s os.Signal) {
			log.Info("Terminate signal received. Stopping connector")
			cn.Stop(false)
		})

		// start grpc API server
		rpcServer := server.NewRPCServer(cn)
		go rpcServer.Start(connectorRPCAddr, serverStartedCh)

		// wait for rpc server to start then launch the cocoon code
		<-serverStartedCh
		go cn.Launch(connectorRPCAddr, cocoonCodeRPCAddr)

		if <-waitCh {
			rpcServer.Stop(1)
		} else {
			rpcServer.Stop(0)
		}

		log.Info("Connector stopped")
	},
}

func init() {
	RootCmd.AddCommand(connectorCmd)
}
<commit_msg>Correctly get the connector and cocoon code container allocated address and port.<commit_after>package cmd

import (
	"fmt"
	"os"
	"os/signal"
	"strconv"
	"syscall"

	"net"

	"github.com/ellcrys/util"
	"github.com/ncodes/cocoon/core/common"
	"github.com/ncodes/cocoon/core/config"
	"github.com/ncodes/cocoon/core/connector"
	"github.com/ncodes/cocoon/core/connector/server"
	"github.com/ncodes/cocoon/core/scheduler"
	logging "github.com/op/go-logging"
	"github.com/spf13/cobra"
)

var (
	log = logging.MustGetLogger("connector")

	// connector RPC API
	defaultConnectorRPCAPI = util.Env("DEV_ADDR_CONNECTOR_RPC", ":8002")

	// Signals channel
	sigs = make(chan os.Signal, 1)
)

func init() {
	config.ConfigureLogger()
}

// creates a deployment request with argument
// fetched from the environment.
func getRequest() (*connector.Request, error) {

	// get cocoon code github link and language
	ccID := os.Getenv("COCOON_ID")
	ccURL := os.Getenv("COCOON_CODE_URL")
	ccTag := os.Getenv("COCOON_CODE_TAG")
	ccLang := os.Getenv("COCOON_CODE_LANG")
	diskLimit := util.Env("COCOON_DISK_LIMIT", "300")
	buildParam := os.Getenv("COCOON_BUILD_PARAMS")
	ccLink := os.Getenv("COCOON_LINK")
	memoryMB, _ := strconv.Atoi(util.Env("ALLOC_MEMORY", "4"))
	cpuShare, _ := strconv.Atoi(util.Env("ALLOC_CPU_SHARE", "100"))

	if ccID == "" {
		return nil, fmt.Errorf("Cocoon code id not set @ $COCOON_ID")
	} else if ccURL == "" {
		return nil, fmt.Errorf("Cocoon code url not set @ $COCOON_CODE_URL")
	} else if ccLang == "" {
		return nil, fmt.Errorf("Cocoon code url not set @ $COCOON_CODE_LANG")
	}

	return &connector.Request{
		ID:          ccID,
		URL:         ccURL,
		Tag:         ccTag,
		Lang:        ccLang,
		DiskLimit:   common.MBToByte(util.ToInt64(diskLimit)),
		BuildParams: buildParam,
		Link:        ccLink,
		Memory:      int64(memoryMB),
		CPUShares:   int64(cpuShare),
	}, nil
}

// onTerminate calls a function when a terminate or interrupt signal is received.
func onTerminate(f func(s os.Signal)) {
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		s := <-sigs
		f(s)
	}()
}

// connectorCmd represents the connector command
var connectorCmd = &cobra.Command{
	Use:   "connector",
	Short: "Start the connector",
	Long:  `Starts the connector and launches a cocoon code.`,
	Run: func(cmd *cobra.Command, args []string) {

		waitCh := make(chan bool, 1)
		serverStartedCh := make(chan bool)

		log.Info("Connector has started")

		// get request
		log.Info("Collecting and validating launch request")
		req, err := getRequest()
		if err != nil {
			log.Error(err.Error())
			return
		}
		log.Infof("Ready to launch cocoon code with id = %s", req.ID)

		connectorRPCAddr := scheduler.Getenv("ADDR_RPC", defaultConnectorRPCAPI)
		cocoonCodeRPCAddr := net.JoinHostPort("", scheduler.Getenv("PORT_code_RPC", "8004"))

		cn := connector.NewConnector(req, waitCh)
		cn.SetAddrs(connectorRPCAddr, cocoonCodeRPCAddr)
		cn.AddLanguage(connector.NewGo(req))
		onTerminate(func(s os.Signal) {
			log.Info("Terminate signal received. Stopping connector")
			cn.Stop(false)
		})

		// start grpc API server
		rpcServer := server.NewRPCServer(cn)
		go rpcServer.Start(connectorRPCAddr, serverStartedCh)

		// wait for rpc server to start then launch the cocoon code
		<-serverStartedCh
		go cn.Launch(connectorRPCAddr, cocoonCodeRPCAddr)

		if <-waitCh {
			rpcServer.Stop(1)
		} else {
			rpcServer.Stop(0)
		}

		log.Info("Connector stopped")
	},
}

func init() {
	RootCmd.AddCommand(connectorCmd)
}
<|endoftext|>


<commit_before>package crankshaft

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"encoding/json"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"strconv"
	"strings"
	"time"
)

var config Config

func MonitorClusters(conf Config) {
	config = conf

	log.Println(config)

	// Get individual clusters, make channels
	clusterList := config.Clusters
	//channels := make([]chan TurbineEvent, len(clusterList))
	eventChannel := make(EventChannel)

	// Start goroutines for each cluster
	for i := range clusterList {
		go turbine(eventChannel, strings.TrimSpace(clusterList[i]))
	}

	// Get client (Influx or StatsD)
	client := provideStatWriter()

	// Consume events
	for event := range eventChannel {
		client.WriteEvent(event)
	}
}

func turbine(c EventChannel, clusterName string) {
	defer close(c)

	for {
		err := attachToTurbine(clusterName, c)
		if err != nil {
			log.Println("Turbine session ended with error", err, "restarting...")
			time.Sleep(3 * time.Second) // wait
		}
	}
}

func attachToTurbine(clusterName string, c EventChannel) error {
	log.Println("Opening Turbine connection for", clusterName)

	// TODO: urlencode
	req, err := http.NewRequest("GET", "/turbine.stream?cluster="+clusterName, nil)

	if err != nil {
		log.Println("Error creating HTTP request", err)
		return err
	}

	conn, err := connectToTurbineServer()
	if err != nil {
		log.Println("Error opening TCP socket", err)
		return err
	}
	defer conn.Close()

	conn.SetDeadline(time.Now().Add(10 * time.Second))
	clientConn := httputil.NewClientConn(conn, nil)
	resp, err := clientConn.Do(req)

	if err != nil {
		log.Println("Error sending HTTP request", err)
		return err
	}

	reader := bufio.NewReader(resp.Body)
	for {
		line, err := reader.ReadBytes('\n')
		if err != nil {
			log.Println("Error reading bytes from stream", err)
			return err
		}

		conn.SetDeadline(time.Now().Add(10 * time.Second))
		line = bytes.TrimSpace(line)
		s := string(line[:])

		if strings.HasPrefix(s, "data: ") {
			s = strings.TrimPrefix(s, "data: ")

			data, err := unmarshalJson(s)

			if err != nil {
				log.Println("Error decoding JSON", err)
				return err
			}

			c <- &TurbineEvent{clusterName, data}
		}
	}
}

func connectToTurbineServer() (net.Conn, error) {
	turbineUrl := config.Host + ":" + strconv.Itoa(config.Port)
	log.Println("Opening Turbine connection host:", turbineUrl)
	conn, err := net.Dial("tcp", turbineUrl)

	if config.TLSEnabled {
		conn = tls.Client(conn, &tls.Config{})
	}

	return conn, err
}

func unmarshalJson(payload string) (map[string]interface{}, error) {
	var f map[string]interface{}
	err := json.Unmarshal([]byte(payload), &f)

	return f, err
}

func provideStatWriter() StatWriter {
	var statClient StatWriter

	switch config.BackendType {
	default:
		log.Fatal("Error:", config.BackendType, "is not a valid backend type")
	case "statsd":
		statClient = GetStatsClient()
	}

	return statClient
}
<commit_msg>Add host header to turbine requests<commit_after>package crankshaft

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"encoding/json"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"strconv"
	"strings"
	"time"
)

var config Config

func MonitorClusters(conf Config) {
	config = conf

	log.Println(config)

	// Get individual clusters, make channels
	clusterList := config.Clusters
	//channels := make([]chan TurbineEvent, len(clusterList))
	eventChannel := make(EventChannel)

	// Start goroutines for each cluster
	for i := range clusterList {
		go turbine(eventChannel, strings.TrimSpace(clusterList[i]))
	}

	// Get client (Influx or StatsD)
	client := provideStatWriter()

	// Consume events
	for event := range eventChannel {
		client.WriteEvent(event)
	}
}

func turbine(c EventChannel, clusterName string) {
	defer close(c)

	for {
		err := attachToTurbine(clusterName, c)
		if err != nil {
			log.Println("Turbine session ended with error", err, "restarting...")
			time.Sleep(3 * time.Second) // wait
		}
	}
}

func attachToTurbine(clusterName string, c EventChannel) error {
	log.Println("Opening Turbine connection for", clusterName)

	// TODO: urlencode
	req, err := http.NewRequest("GET", "/turbine.stream?cluster="+clusterName, nil)

	if err != nil {
		log.Println("Error creating HTTP request", err)
		return err
	}

	req.Host = config.Host

	conn, err := connectToTurbineServer()
	if err != nil {
		log.Println("Error opening TCP socket", err)
		return err
	}
	defer conn.Close()

	conn.SetDeadline(time.Now().Add(10 * time.Second))
	clientConn := httputil.NewClientConn(conn, nil)
	resp, err := clientConn.Do(req)

	if err != nil {
		log.Println("Error sending HTTP request", err)
		return err
	}

	reader := bufio.NewReader(resp.Body)
	for {
		line, err := reader.ReadBytes('\n')
		if err != nil {
			log.Println("Error reading bytes from stream", err)
			return err
		}

		conn.SetDeadline(time.Now().Add(10 * time.Second))
		line = bytes.TrimSpace(line)
		s := string(line[:])

		if strings.HasPrefix(s, "data: ") {
			s = strings.TrimPrefix(s, "data: ")

			data, err := unmarshalJson(s)

			if err != nil {
				log.Println("Error decoding JSON", err)
				return err
			}

			c <- &TurbineEvent{clusterName, data}
		}
	}
}

func connectToTurbineServer() (net.Conn, error) {
	turbineUrl := config.Host + ":" + strconv.Itoa(config.Port)
	log.Println("Opening Turbine connection host:", turbineUrl)
	conn, err := net.Dial("tcp", turbineUrl)

	if config.TLSEnabled {
		conn = tls.Client(conn, &tls.Config{})
	}

	return conn, err
}

func unmarshalJson(payload string) (map[string]interface{}, error) {
	var f map[string]interface{}
	err := json.Unmarshal([]byte(payload), &f)

	return f, err
}

func provideStatWriter() StatWriter {
	var statClient StatWriter

	switch config.BackendType {
	default:
		log.Fatal("Error:", config.BackendType, "is not a valid backend type")
	case "statsd":
		statClient = GetStatsClient()
	}

	return statClient
}
<|endoftext|>


<commit_before>// Copyright 2018 The CUE Authors
//
// Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package build

import (
	"fmt"
	pathpkg "path"
	"path/filepath"
	"strings"
	"unicode"

	"cuelang.org/go/cue/ast"
	"cuelang.org/go/cue/ast/astutil"
	"cuelang.org/go/cue/errors"
	"cuelang.org/go/cue/parser"
	"cuelang.org/go/cue/token"
	"cuelang.org/go/internal"
)

// An Instance describes the collection of files, and its imports, necessary
// to build a CUE instance.
//
// A typical way to create an Instance is to use the cue/load package.
type Instance struct {
	ctxt *Context

	BuildFiles    []*File // files to be inclduded in the build
	IgnoredFiles  []*File // files excluded for this build
	OrphanedFiles []*File // recognized file formats not part of any build
	InvalidFiles  []*File // could not parse these files
	UnknownFiles  []*File // unknown file types

	// Files contains the AST for all files part of this instance.
	// TODO: the intent is to deprecate this in favor of BuildFiles.
	Files []*ast.File

	loadFunc LoadFunc
	done     bool

	// Scope is another instance that may be used to resolve any unresolved
	// reference of this instance. For instance, tool and test instances
	// may refer to top-level fields in their package scope.
	Scope *Instance

	// PkgName is the name specified in the package clause.
	PkgName string
	hasName bool

	// ImportPath returns the unique path to identify an imported instance.
	//
	// Instances created with NewInstance do not have an import path.
	ImportPath string

	// Imports lists the instances of all direct imports of this instance.
	Imports []*Instance

	// The Err for loading this package or nil on success. This does not
	// include any errors of dependencies. Incomplete will be set if there
	// were any errors in dependencies.
	Err errors.Error

	// Incomplete reports whether any dependencies had an error.
	Incomplete bool

	parent *Instance // TODO: for cycle detection

	// The following fields are for informative purposes and are not used by
	// the cue package to create an instance.

	// ImportComment is the path in the import comment on the package statement.
	ImportComment string

	// DisplayPath is a user-friendly version of the package or import path.
	DisplayPath string

	// Dir is the package directory. Note that a package may also include files
	// from ancestor directories, up to the module file.
	Dir string

	// Module defines the module name of a package. It must be defined if
	// the packages within the directory structure of the module are to be
	// imported by other packages, including those within the module.
	Module string

	// Root is the root of the directory hierarchy, it may be "" if this an
	// instance has no imports.
	// If Module != "", this corresponds to the module root.
	// Root/pkg is the directory that holds third-party packages.
	Root string // root directory of hierarchy ("" if unknown)

	// AllTags are the build tags that can influence file selection in this
	// directory.
	AllTags []string

	Standard bool // Is a builtin package
	User     bool // True if package was created from individual files.

	// Deprecated: use BuildFiles
	CUEFiles []string // .cue source files
	// Deprecated: use BuildFiles and OrphanedFiles
	DataFiles []string // recognized data files (.json, .yaml, etc.)

	// The intent is to also deprecate the following fields in favor of
	// IgnoredFiles and UnknownFiles.
	TestCUEFiles    []string // .cue test files (_test.cue)
	ToolCUEFiles    []string // .cue tool files (_tool.cue)
	IgnoredCUEFiles []string // .cue source files ignored for this build
	InvalidCUEFiles []string // .cue source files with detected problems (parse error, wrong package name, and so on)

	// Dependencies
	ImportPaths []string
	ImportPos   map[string][]token.Pos // line information for Imports

	Deps       []string
	DepsErrors []error
	Match      []string
}

// ID returns the package ID unique for this module.
func (inst *Instance) ID() string {
	if inst.PkgName == "" {
		return "_"
	}
	if s := inst.ImportPath; s != "" {
		return s
	}
	s := fmt.Sprintf("%s:%s", inst.Module, inst.PkgName)
	return s
}

// Dependencies reports all Instances on which this instance depends.
func (inst *Instance) Dependencies() []*Instance {
	// TODO: as cyclic dependencies are not allowed, we could just not check.
	// Do for safety now and remove later if needed.
	return appendDependencies(nil, inst, map[*Instance]bool{})
}

func appendDependencies(a []*Instance, inst *Instance, done map[*Instance]bool) []*Instance {
	for _, d := range inst.Imports {
		if done[d] {
			continue
		}
		a = append(a, d)
		done[d] = true
		a = appendDependencies(a, d, done)
	}
	return a
}

// Abs converts relative path used in the one of the file fields to an
// absolute one.
func (inst *Instance) Abs(path string) string {
	if filepath.IsAbs(path) {
		return path
	}
	return filepath.Join(inst.Root, path)
}

func (inst *Instance) setPkg(pkg string) bool {
	if !inst.hasName {
		inst.hasName = true
		inst.PkgName = pkg
		return true
	}
	return false
}

// ReportError reports an error processing this instance.
func (inst *Instance) ReportError(err errors.Error) {
	inst.Err = errors.Append(inst.Err, err)
}

// Context defines the build context for this instance. All files defined
// in Syntax as well as all imported instances must be created using the
// same build context.
func (inst *Instance) Context() *Context {
	return inst.ctxt
}

func (inst *Instance) parse(name string, src interface{}) (*ast.File, error) {
	if inst.ctxt != nil && inst.ctxt.parseFunc != nil {
		return inst.ctxt.parseFunc(name, src)
	}
	return parser.ParseFile(name, src, parser.ParseComments)
}

// LookupImport defines a mapping from an ImportSpec's ImportPath to Instance.
func (inst *Instance) LookupImport(path string) *Instance {
	path = inst.expandPath(path)
	for _, inst := range inst.Imports {
		if inst.ImportPath == path {
			return inst
		}
	}
	return nil
}

func (inst *Instance) addImport(imp *Instance) {
	for _, inst := range inst.Imports {
		if inst.ImportPath == imp.ImportPath {
			if inst != imp {
				panic("import added multiple times with different instances")
			}
			return
		}
	}
	inst.Imports = append(inst.Imports, imp)
}

// AddFile adds the file with the given name to the list of files for this
// instance. The file may be loaded from the cache of the instance's context.
// It does not process the file's imports. The package name of the file must
// match the package name of the instance.
func (inst *Instance) AddFile(filename string, src interface{}) error {
	file, err := inst.parse(filename, src)
	if err != nil {
		// should always be an errors.List, but just in case.
		err := errors.Promote(err, "error adding file")
		inst.ReportError(err)
		return err
	}

	return inst.AddSyntax(file)
}

// AddSyntax adds the given file to list of files for this instance. The package
// name of the file must match the package name of the instance.
func (inst *Instance) AddSyntax(file *ast.File) errors.Error {
	astutil.Resolve(file, func(pos token.Pos, msg string, args ...interface{}) {
		inst.Err = errors.Append(inst.Err, errors.Newf(pos, msg, args...))
	})
	_, pkg, pos := internal.PackageInfo(file)
	if pkg != "" && pkg != "_" && !inst.setPkg(pkg) && pkg != inst.PkgName {
		err := errors.Newf(pos,
			"package name %q conflicts with previous package name %q",
			pkg, inst.PkgName)
		inst.ReportError(err)
		return err
	}
	inst.Files = append(inst.Files, file)
	return nil
}

func (inst *Instance) expandPath(path string) string {
	isLocal := IsLocalImport(path)
	if isLocal {
		path = dirToImportPath(filepath.Join(inst.Dir, path))
	}
	return path
}

// dirToImportPath returns the pseudo-import path we use for a package
// outside the CUE path. It begins with _/ and then contains the full path
// to the directory. If the package lives in c:\home\gopher\my\pkg then
// the pseudo-import path is _/c_/home/gopher/my/pkg.
// Using a pseudo-import path like this makes the ./ imports no longer
// a special case, so that all the code to deal with ordinary imports works
// automatically.
func dirToImportPath(dir string) string {
	return pathpkg.Join("_", strings.Map(makeImportValid, filepath.ToSlash(dir)))
}

func makeImportValid(r rune) rune {
	// Should match Go spec, compilers, and ../../go/parser/parser.go:/isValidImport.
	const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
	if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
		return '_'
	}
	return r
}

// IsLocalImport reports whether the import path is
// a local import path, like ".", "..", "./foo", or "../foo".
func IsLocalImport(path string) bool {
	return path == "." || path == ".." ||
		strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
}
<commit_msg>cue/build: remove unused Scope field<commit_after>// Copyright 2018 The CUE Authors
//
// Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package build

import (
	"fmt"
	pathpkg "path"
	"path/filepath"
	"strings"
	"unicode"

	"cuelang.org/go/cue/ast"
	"cuelang.org/go/cue/ast/astutil"
	"cuelang.org/go/cue/errors"
	"cuelang.org/go/cue/parser"
	"cuelang.org/go/cue/token"
	"cuelang.org/go/internal"
)

// An Instance describes the collection of files, and its imports, necessary
// to build a CUE instance.
//
// A typical way to create an Instance is to use the cue/load package.
type Instance struct {
	ctxt *Context

	BuildFiles    []*File // files to be inclduded in the build
	IgnoredFiles  []*File // files excluded for this build
	OrphanedFiles []*File // recognized file formats not part of any build
	InvalidFiles  []*File // could not parse these files
	UnknownFiles  []*File // unknown file types

	// Files contains the AST for all files part of this instance.
	// TODO: the intent is to deprecate this in favor of BuildFiles.
	Files []*ast.File

	loadFunc LoadFunc
	done     bool

	// PkgName is the name specified in the package clause.
	PkgName string
	hasName bool

	// ImportPath returns the unique path to identify an imported instance.
	//
	// Instances created with NewInstance do not have an import path.
	ImportPath string

	// Imports lists the instances of all direct imports of this instance.
	Imports []*Instance

	// The Err for loading this package or nil on success. This does not
	// include any errors of dependencies. Incomplete will be set if there
	// were any errors in dependencies.
	Err errors.Error

	// Incomplete reports whether any dependencies had an error.
	Incomplete bool

	parent *Instance // TODO: for cycle detection

	// The following fields are for informative purposes and are not used by
	// the cue package to create an instance.

	// ImportComment is the path in the import comment on the package statement.
	ImportComment string

	// DisplayPath is a user-friendly version of the package or import path.
	DisplayPath string

	// Dir is the package directory. Note that a package may also include files
	// from ancestor directories, up to the module file.
	Dir string

	// Module defines the module name of a package. It must be defined if
	// the packages within the directory structure of the module are to be
	// imported by other packages, including those within the module.
	Module string

	// Root is the root of the directory hierarchy, it may be "" if this an
	// instance has no imports.
	// If Module != "", this corresponds to the module root.
	// Root/pkg is the directory that holds third-party packages.
	Root string // root directory of hierarchy ("" if unknown)

	// AllTags are the build tags that can influence file selection in this
	// directory.
	AllTags []string

	Standard bool // Is a builtin package
	User     bool // True if package was created from individual files.

	// Deprecated: use BuildFiles
	CUEFiles []string // .cue source files
	// Deprecated: use BuildFiles and OrphanedFiles
	DataFiles []string // recognized data files (.json, .yaml, etc.)

	// The intent is to also deprecate the following fields in favor of
	// IgnoredFiles and UnknownFiles.
	TestCUEFiles    []string // .cue test files (_test.cue)
	ToolCUEFiles    []string // .cue tool files (_tool.cue)
	IgnoredCUEFiles []string // .cue source files ignored for this build
	InvalidCUEFiles []string // .cue source files with detected problems (parse error, wrong package name, and so on)

	// Dependencies
	ImportPaths []string
	ImportPos   map[string][]token.Pos // line information for Imports

	Deps       []string
	DepsErrors []error
	Match      []string
}

// ID returns the package ID unique for this module.
func (inst *Instance) ID() string {
	if inst.PkgName == "" {
		return "_"
	}
	if s := inst.ImportPath; s != "" {
		return s
	}
	s := fmt.Sprintf("%s:%s", inst.Module, inst.PkgName)
	return s
}

// Dependencies reports all Instances on which this instance depends.
func (inst *Instance) Dependencies() []*Instance {
	// TODO: as cyclic dependencies are not allowed, we could just not check.
	// Do for safety now and remove later if needed.
	return appendDependencies(nil, inst, map[*Instance]bool{})
}

func appendDependencies(a []*Instance, inst *Instance, done map[*Instance]bool) []*Instance {
	for _, d := range inst.Imports {
		if done[d] {
			continue
		}
		a = append(a, d)
		done[d] = true
		a = appendDependencies(a, d, done)
	}
	return a
}

// Abs converts relative path used in the one of the file fields to an
// absolute one.
func (inst *Instance) Abs(path string) string {
	if filepath.IsAbs(path) {
		return path
	}
	return filepath.Join(inst.Root, path)
}

func (inst *Instance) setPkg(pkg string) bool {
	if !inst.hasName {
		inst.hasName = true
		inst.PkgName = pkg
		return true
	}
	return false
}

// ReportError reports an error processing this instance.
func (inst *Instance) ReportError(err errors.Error) {
	inst.Err = errors.Append(inst.Err, err)
}

// Context defines the build context for this instance. All files defined
// in Syntax as well as all imported instances must be created using the
// same build context.
func (inst *Instance) Context() *Context {
	return inst.ctxt
}

func (inst *Instance) parse(name string, src interface{}) (*ast.File, error) {
	if inst.ctxt != nil && inst.ctxt.parseFunc != nil {
		return inst.ctxt.parseFunc(name, src)
	}
	return parser.ParseFile(name, src, parser.ParseComments)
}

// LookupImport defines a mapping from an ImportSpec's ImportPath to Instance.
func (inst *Instance) LookupImport(path string) *Instance {
	path = inst.expandPath(path)
	for _, inst := range inst.Imports {
		if inst.ImportPath == path {
			return inst
		}
	}
	return nil
}

func (inst *Instance) addImport(imp *Instance) {
	for _, inst := range inst.Imports {
		if inst.ImportPath == imp.ImportPath {
			if inst != imp {
				panic("import added multiple times with different instances")
			}
			return
		}
	}
	inst.Imports = append(inst.Imports, imp)
}

// AddFile adds the file with the given name to the list of files for this
// instance. The file may be loaded from the cache of the instance's context.
// It does not process the file's imports. The package name of the file must
// match the package name of the instance.
func (inst *Instance) AddFile(filename string, src interface{}) error {
	file, err := inst.parse(filename, src)
	if err != nil {
		// should always be an errors.List, but just in case.
		err := errors.Promote(err, "error adding file")
		inst.ReportError(err)
		return err
	}

	return inst.AddSyntax(file)
}

// AddSyntax adds the given file to list of files for this instance. The package
// name of the file must match the package name of the instance.
func (inst *Instance) AddSyntax(file *ast.File) errors.Error {
	astutil.Resolve(file, func(pos token.Pos, msg string, args ...interface{}) {
		inst.Err = errors.Append(inst.Err, errors.Newf(pos, msg, args...))
	})
	_, pkg, pos := internal.PackageInfo(file)
	if pkg != "" && pkg != "_" && !inst.setPkg(pkg) && pkg != inst.PkgName {
		err := errors.Newf(pos,
			"package name %q conflicts with previous package name %q",
			pkg, inst.PkgName)
		inst.ReportError(err)
		return err
	}
	inst.Files = append(inst.Files, file)
	return nil
}

func (inst *Instance) expandPath(path string) string {
	isLocal := IsLocalImport(path)
	if isLocal {
		path = dirToImportPath(filepath.Join(inst.Dir, path))
	}
	return path
}

// dirToImportPath returns the pseudo-import path we use for a package
// outside the CUE path. It begins with _/ and then contains the full path
// to the directory. If the package lives in c:\home\gopher\my\pkg then
// the pseudo-import path is _/c_/home/gopher/my/pkg.
// Using a pseudo-import path like this makes the ./ imports no longer
// a special case, so that all the code to deal with ordinary imports works
// automatically.
func dirToImportPath(dir string) string {
	return pathpkg.Join("_", strings.Map(makeImportValid, filepath.ToSlash(dir)))
}

func makeImportValid(r rune) rune {
	// Should match Go spec, compilers, and ../../go/parser/parser.go:/isValidImport.
	const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
	if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
		return '_'
	}
	return r
}

// IsLocalImport reports whether the import path is
// a local import path, like ".", "..", "./foo", or "../foo".
func IsLocalImport(path string) bool {
	return path == "." || path == ".." ||
		strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
}
<|endoftext|>


<commit_before>package daemon

import (
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go/service/ec2"
	"testing"
)

type FakeMetadataFetcher struct {
	FAvailable bool
	Meta       map[string]string
}

func (m FakeMetadataFetcher) Available() bool {
	return m.FAvailable
}

func (m FakeMetadataFetcher) GetMetadata(key string) (string, error) {
	v, ok := m.Meta[key]
	if ok {
		return v, nil
	}
	return v, errors.New(fmt.Sprintf("Key %s unknown"))
}

type FakeRouteTableFetcher struct{}

func (r FakeRouteTableFetcher) GetRouteTables() ([]*ec2.RouteTable, error) {
	return []*ec2.RouteTable{}, nil
}

func getD(a bool) Daemon {
	d := Daemon{
		ConfigFile: "../tests/awsnycast.yaml",
	}
	fakeM := FakeMetadataFetcher{
		FAvailable: a,
	}
	fakeR := FakeRouteTableFetcher{}
	fakeM.Meta = make(map[string]string)
	fakeM.Meta["placement/availability-zone"] = "us-west-1a"
	d.MetadataFetcher = fakeM
	d.RouteTableFetcher = fakeR
	return d
}

func TestSetupUnavailable(t *testing.T) {
	d := getD(false)
	err := d.Setup()
	if err != nil {
		t.Log(err)
		t.Fail()
	}
	if d.MetadataFetcher.Available() {
		t.Fail()
	}
}

func TestSetupAvailable(t *testing.T) {
	d := getD(true)
	err := d.Setup()
	if err != nil {
		t.Fail()
	}
	if !d.MetadataFetcher.Available() {
		t.Fail()
	}
}

func TestSetupHealthChecks(t *testing.T) {
	d := getD(true)
	err := d.Setup()
	if err != nil {
		t.Fail()
	}
	if d.Config.Healthchecks["public"].IsRunning() {
		t.Log("HealthChecks already running")
		t.Fail()
	}
	d.runHealthChecks()
	if !d.Config.Healthchecks["public"].IsRunning() {
		t.Log("HealthChecks did not start running")
		t.Fail()
	}
}

func TestGetSubnetIdMacFail(t *testing.T) {
	d := getD(true)
	_, err := d.GetSubnetId()
	if err == nil {
		t.Fail()
	}
}

func TestGetSubnetIdMacFail2(t *testing.T) {
	d := getD(true)
	d.MetadataFetcher.(FakeMetadataFetcher).Meta["mac"] = "06:1d:ea:6f:8c:6e"
	_, err := d.GetSubnetId()
	if err == nil {
		t.Fail()
	}
}

func TestGetSubnetIdMacOk(t *testing.T) {
	d := getD(true)
	d.MetadataFetcher.(FakeMetadataFetcher).Meta["mac"] = "06:1d:ea:6f:8c:6e"
	d.MetadataFetcher.(FakeMetadataFetcher).Meta["network/interfaces/macs/06:1d:ea:6f:8c:6e/subnet-id"] = "subnet-28b0e940"
	val, err := d.GetSubnetId()
	if err != nil {
		t.Fail()
	}
	if val != "subnet-28b0e940" {
		t.Fail()
	}
}
<commit_msg>Test getting the region<commit_after>package daemon

import (
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go/service/ec2"
	"testing"
)

type FakeMetadataFetcher struct {
	FAvailable bool
	Meta       map[string]string
}

func (m FakeMetadataFetcher) Available() bool {
	return m.FAvailable
}

func (m FakeMetadataFetcher) GetMetadata(key string) (string, error) {
	v, ok := m.Meta[key]
	if ok {
		return v, nil
	}
	return v, errors.New(fmt.Sprintf("Key %s unknown"))
}

type FakeRouteTableFetcher struct{}

func (r FakeRouteTableFetcher) GetRouteTables() ([]*ec2.RouteTable, error) {
	return []*ec2.RouteTable{}, nil
}

func getD(a bool) Daemon {
	d := Daemon{
		ConfigFile: "../tests/awsnycast.yaml",
	}
	fakeM := FakeMetadataFetcher{
		FAvailable: a,
	}
	fakeR := FakeRouteTableFetcher{}
	fakeM.Meta = make(map[string]string)
	fakeM.Meta["placement/availability-zone"] = "us-west-1a"
	d.MetadataFetcher = fakeM
	d.RouteTableFetcher = fakeR
	return d
}

func TestSetupUnavailable(t *testing.T) {
	d := getD(false)
	err := d.Setup()
	if err != nil {
		t.Log(err)
		t.Fail()
	}
	if d.MetadataFetcher.Available() {
		t.Fail()
	}
}

func TestSetupAvailable(t *testing.T) {
	d := getD(true)
	err := d.Setup()
	if err != nil {
		t.Fail()
	}
	if !d.MetadataFetcher.Available() {
		t.Fail()
	}
}

func TestSetupRegionFromAZ(t *testing.T) {
	d := getD(true)
	err := d.Setup()
	if err != nil {
		t.Fail()
	}
	if d.Region != "us-west-1" {
		t.Fail()
	}
}

func TestSetupHealthChecks(t *testing.T) {
	d := getD(true)
	err := d.Setup()
	if err != nil {
		t.Fail()
	}
	if d.Config.Healthchecks["public"].IsRunning() {
		t.Log("HealthChecks already running")
		t.Fail()
	}
	d.runHealthChecks()
	if !d.Config.Healthchecks["public"].IsRunning() {
		t.Log("HealthChecks did not start running")
		t.Fail()
	}
}

func TestGetSubnetIdMacFail(t *testing.T) {
	d := getD(true)
	_, err := d.GetSubnetId()
	if err == nil {
		t.Fail()
	}
}

func TestGetSubnetIdMacFail2(t *testing.T) {
	d := getD(true)
	d.MetadataFetcher.(FakeMetadataFetcher).Meta["mac"] = "06:1d:ea:6f:8c:6e"
	_, err := d.GetSubnetId()
	if err == nil {
		t.Fail()
	}
}

func TestGetSubnetIdMacOk(t *testing.T) {
	d := getD(true)
	d.MetadataFetcher.(FakeMetadataFetcher).Meta["mac"] = "06:1d:ea:6f:8c:6e"
	d.MetadataFetcher.(FakeMetadataFetcher).Meta["network/interfaces/macs/06:1d:ea:6f:8c:6e/subnet-id"] = "subnet-28b0e940"
	val, err := d.GetSubnetId()
	if err != nil {
		t.Fail()
	}
	if val != "subnet-28b0e940" {
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"log"
	"strconv"
	"strings"

	"github.com/aws/aws-sdk-go/service/ecs"
)

type scanner struct {
	ec2 *ec2Client
	ecs *ecsClient

	cluster string

	idAddressMap map[string]string

	nameNetworkBindingsMap map[string][]*ecs.NetworkBinding
}

func newScanner(cluster string, ec2 *ec2Client, ecs *ecsClient) *scanner {
	return &scanner{ec2: ec2, ecs: ecs, cluster: cluster}
}

func (s *scanner) scan() ([]*container, error) {
	log.Println("updating config")
	clusterInfo, err := s.ecs.describeCluster(s.cluster)
	if err != nil {
		return nil, err
	}
	if *clusterInfo.Status != "ACTIVE" {
		return nil, errClusterNotActive
	}
	s.idAddressMap, err = s.makeIDAddressMap()
	if err != nil {
		return nil, err
	}
	return s.extractContainers()
}

func (s *scanner) makeIDAddressMap() (map[string]string, error) {
	instances := make(map[string]string)
	arns, err := s.ecs.listContainerInstances(s.cluster)
	if err != nil {
		return nil, err
	}
	containerInstances, err := s.ecs.describeContainerInstances(s.cluster, arns)
	if err != nil {
		return nil, err
	}
	for i := range containerInstances {
		instance, err := s.ec2.describeInstance(containerInstances[i].Ec2InstanceId)
		if err != nil {
			return nil, err
		}
		instances[*containerInstances[i].ContainerInstanceArn] = *instance.PrivateIpAddress
	}
	return instances, nil
}

func (s *scanner) getTasks() ([]*ecs.Task, error) {
	arns, err := s.ecs.listTasks(s.cluster)
	if err != nil {
		return nil, err
	}
	return s.ecs.describeTasks(s.cluster, arns)
}

func (s *scanner) extractContainers() ([]*container, error) {
	tasks, err := s.getTasks()
	if err != nil {
		return nil, err
	}
	containers := make([]*container, 0, 10)
	for _, t := range tasks {
		s.nameNetworkBindingsMap = s.makeNameNetworkBindingsMap(t.Containers)
		taskDefinition, err := s.ecs.describeTaskDefinition(t.TaskDefinitionArn)
		if err != nil {
			return nil, err
		}
		for _, cd := range taskDefinition.ContainerDefinitions {
			container, err := s.extractContainer(t, cd)
			if err != nil {
				log.Println(err)
				continue
			}
			containers = append(containers, container)
		}
	}
	return containers, nil
}

func (s *scanner) makeNameNetworkBindingsMap(containers []*ecs.Container) map[string][]*ecs.NetworkBinding {
	networkBindings := make(map[string][]*ecs.NetworkBinding)
	for _, c := range containers {
		networkBindings[*c.Name] = c.NetworkBindings
	}
	return networkBindings
}

func (s *scanner) extractContainer(t *ecs.Task, cd *ecs.ContainerDefinition) (*container, error) {
	if strings.ToLower(*cd.Name) == *taskName {
		return nil, errors.New("container is own container. skipping")
	}
	if len(s.nameNetworkBindingsMap[*cd.Name]) == 0 {
		return nil, errors.New("container has no network bindings. skipping")
	}
	virtualHost, virtualPort := extractVars(cd.Environment)
	if virtualHost == "" {
		return nil, errors.New("[" + *cd.Name + "] VIRTUAL_HOST environment variable not found. skipping")
	}
	port := ""
	if len(s.nameNetworkBindingsMap[*cd.Name]) == 1 {
		port = strconv.FormatInt(*s.nameNetworkBindingsMap[*cd.Name][0].HostPort, 10)
	} else if virtualPort != "" {
		port = extractHostPort(virtualPort, s.nameNetworkBindingsMap[*cd.Name])
	}
	if port == "" {
		return nil, errors.New("[" + *cd.Name + "] no valid port configuration found. skipping")
	}
	return &container{
		Host:    virtualHost,
		Port:    port,
		Address: s.idAddressMap[*t.ContainerInstanceArn],
	}, nil
}

func extractHostPort(virtualPort string, nbs []*ecs.NetworkBinding) string {
	for _, nb := range nbs {
		if strconv.FormatInt(*nb.ContainerPort, 10) == virtualPort {
			return strconv.FormatInt(*nb.HostPort, 10)
		}
	}
	return ""
}

func extractVars(env []*ecs.KeyValuePair) (string, string) {
	virtualHost := ""
	virtualPort := ""
	for _, e := range env {
		if strings.ToLower(*e.Name) == "virtual_host" {
			virtualHost = *e.Value
		} else if strings.ToLower(*e.Name) == "virtual_port" {
			virtualPort = *e.Value
		}
	}
	return virtualHost, virtualPort
}
<commit_msg>Ignore terminated containers with no private ips<commit_after>package main

import (
	"errors"
	"log"
	"strconv"
	"strings"

	"github.com/aws/aws-sdk-go/service/ecs"
)

type scanner struct {
	ec2 *ec2Client
	ecs *ecsClient

	cluster string

	idAddressMap map[string]string

	nameNetworkBindingsMap map[string][]*ecs.NetworkBinding
}

func newScanner(cluster string, ec2 *ec2Client, ecs *ecsClient) *scanner {
	return &scanner{ec2: ec2, ecs: ecs, cluster: cluster}
}

func (s *scanner) scan() ([]*container, error) {
	log.Println("updating config")
	clusterInfo, err := s.ecs.describeCluster(s.cluster)
	if err != nil {
		return nil, err
	}
	if *clusterInfo.Status != "ACTIVE" {
		return nil, errClusterNotActive
	}
	s.idAddressMap, err = s.makeIDAddressMap()
	if err != nil {
		return nil, err
	}
	return s.extractContainers()
}

func (s *scanner) makeIDAddressMap() (map[string]string, error) {
	instances := make(map[string]string)
	arns, err := s.ecs.listContainerInstances(s.cluster)
	if err != nil {
		return nil, err
	}
	containerInstances, err := s.ecs.describeContainerInstances(s.cluster, arns)
	if err != nil {
		return nil, err
	}
	for i := range containerInstances {
		instance, err := s.ec2.describeInstance(containerInstances[i].Ec2InstanceId)
		if err != nil {
			return nil, err
		}
		if instance.PrivateIpAddress != nil {
			instances[*containerInstances[i].ContainerInstanceArn] = *instance.PrivateIpAddress
		}
	}
	return instances, nil
}

func (s *scanner) getTasks() ([]*ecs.Task, error) {
	arns, err := s.ecs.listTasks(s.cluster)
	if err != nil {
		return nil, err
	}
	return s.ecs.describeTasks(s.cluster, arns)
}

func (s *scanner) extractContainers() ([]*container, error) {
	tasks, err := s.getTasks()
	if err != nil {
		return nil, err
	}
	containers := make([]*container, 0, 10)
	for _, t := range tasks {
		s.nameNetworkBindingsMap = s.makeNameNetworkBindingsMap(t.Containers)
		taskDefinition, err := s.ecs.describeTaskDefinition(t.TaskDefinitionArn)
		if err != nil {
			return nil, err
		}
		for _, cd := range taskDefinition.ContainerDefinitions {
			container, err := s.extractContainer(t, cd)
			if err != nil {
				log.Println(err)
				continue
			}
			containers = append(containers, container)
		}
	}
	return containers, nil
}

func (s *scanner) makeNameNetworkBindingsMap(containers []*ecs.Container) map[string][]*ecs.NetworkBinding {
	networkBindings := make(map[string][]*ecs.NetworkBinding)
	for _, c := range containers {
		networkBindings[*c.Name] = c.NetworkBindings
	}
	return networkBindings
}

func (s *scanner) extractContainer(t *ecs.Task, cd *ecs.ContainerDefinition) (*container, error) {
	if strings.ToLower(*cd.Name) == *taskName {
		return nil, errors.New("container is own container. skipping")
	}
	if len(s.nameNetworkBindingsMap[*cd.Name]) == 0 {
		return nil, errors.New("container has no network bindings. skipping")
	}
	virtualHost, virtualPort := extractVars(cd.Environment)
	if virtualHost == "" {
		return nil, errors.New("[" + *cd.Name + "] VIRTUAL_HOST environment variable not found. skipping")
	}
	port := ""
	if len(s.nameNetworkBindingsMap[*cd.Name]) == 1 {
		port = strconv.FormatInt(*s.nameNetworkBindingsMap[*cd.Name][0].HostPort, 10)
	} else if virtualPort != "" {
		port = extractHostPort(virtualPort, s.nameNetworkBindingsMap[*cd.Name])
	}
	if port == "" {
		return nil, errors.New("[" + *cd.Name + "] no valid port configuration found. skipping")
	}
	return &container{
		Host:    virtualHost,
		Port:    port,
		Address: s.idAddressMap[*t.ContainerInstanceArn],
	}, nil
}

func extractHostPort(virtualPort string, nbs []*ecs.NetworkBinding) string {
	for _, nb := range nbs {
		if strconv.FormatInt(*nb.ContainerPort, 10) == virtualPort {
			return strconv.FormatInt(*nb.HostPort, 10)
		}
	}
	return ""
}

func extractVars(env []*ecs.KeyValuePair) (string, string) {
	virtualHost := ""
	virtualPort := ""
	for _, e := range env {
		if strings.ToLower(*e.Name) == "virtual_host" {
			virtualHost = *e.Value
		} else if strings.ToLower(*e.Name) == "virtual_port" {
			virtualPort = *e.Value
		}
	}
	return virtualHost, virtualPort
}
<|endoftext|>


<commit_before>package cloudwatch

import (
	"os"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
)

// CloudCloudWatch provides a Session to the CloudWatch API
type CloudWatch struct {
	Session *cloudwatch.CloudWatch
}

// DefaultSessionConfig creates a Session configuration based on our custom
// defaults
func DefaultSessionConfig() *aws.Config {
	config := &aws.Config{
		Region: aws.String("us-east-1"),
	}

	awsDefaultRegion := os.Getenv("AWS_DEFAULT_REGION")
	if awsDefaultRegion != "" {
		config.Region = &awsDefaultRegion
	}

	awsRegion := os.Getenv("AWS_REGION")
	if awsRegion != "" {
		config.Region = &awsRegion
	}
	return config
}

// NewSession creates and returns a cloudwatch Session session
func NewSession(config *aws.Config) *CloudWatch {
	currentSession := session.New(config)
	session := cloudwatch.New(currentSession)
	return &CloudWatch{Session: session}
}
<commit_msg>Avoid intermediate states<commit_after>package cloudwatch

import (
	"os"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
)

// CloudCloudWatch provides a Session to the CloudWatch API
type CloudWatch struct {
	Session *cloudwatch.CloudWatch
}

// DefaultSessionConfig creates a Session configuration based on our custom
// defaults
func DefaultSessionConfig() *aws.Config {
	config := &aws.Config{
		Region: aws.String("us-east-1"),
	}

	awsDefaultRegion := os.Getenv("AWS_DEFAULT_REGION")
	if awsDefaultRegion != "" {
		config.Region = &awsDefaultRegion
	}

	awsRegion := os.Getenv("AWS_REGION")
	if awsRegion != "" {
		config.Region = &awsRegion
	}
	return config
}

// NewSession creates and returns a cloudwatch Session session
func NewSession(config *aws.Config) *CloudWatch {
	return &CloudWatch{Session: cloudwatch.New(session.New(config))}
}
<|endoftext|>