<commit_before>package main

import "fmt"
import "strings"
import "net"  //now look at this net that I just found
import "time" //time is a tool you can put on the wall or wear it on your wrist
//the past is far behind us, the future doesn't exist
import "./players"

func getPlayerOfIp(ip string) players.NetworkPlayer {
	for _, plr := range networkPlayerList {
		plrIp := plr.GetIp()
		if plrIp == ip[:strings.Index(ip, ":")] {
			return plr
		}
	}
	//for now, add a player to the list and return it
	networkPlayerList=append(networkPlayerList,players.NewNetworkPlayer("uwe",3,ip))
	return networkPlayerList[len(networkPlayerList)-1]
}

func handleConn1(conn net.Conn) {
	fmt.Println("conn 1")
	conn.Write([]byte{65}])
	/*fmt.Println(conn.RemoteAddr().String())
	plr := getPlayerOfIp(conn.RemoteAddr().String())
	for len(plr.GetDataToBeSent()) == 0 {
		time.Sleep(time.Second / 2)
	}
	_, err := conn.Write(plr.GetDataToBeSent()[0])
	if err == nil {
		plr.RemoveASentData()
	}*/
}

func handleConn2(conn net.Conn) {
	fmt.Println("conn 2")
	fmt.Println(conn.RemoteAddr().String())
	var chat []byte
	_, err := conn.Read(chat)
	fmt.Println(chat,err)
	if err != nil {
		return
	}
	chat = append([]byte("CHAT"), chat...)
	plr := getPlayerOfIp(conn.RemoteAddr().String())
	for _, plr2 := range networkPlayerList {
		if plr2 != plr {
			plr2.SendData(chat)
		}
	}
}

func checkLoop1(quitChan chan struct{}) {
	ln, _ := net.Listen("tcp", ":5252") //event listeners
	for {
		select {
		case <-quitChan:
			fmt.Println("65")
			return
		default:
			conn, _ := ln.Accept()
			go handleConn1(conn)
		}
	}
}
func checkLoop2(quitChan chan struct{}) {
	ln, _ := net.Listen("tcp", ":6565") //chat
	for {
		select {
		case <-quitChan:
			fmt.Println("65")
			return
		default:
			conn, _ := ln.Accept()
			go handleConn2(conn)
		}
	}
}

func setupServer(quitChan chan struct{}) {
	go checkLoop1(quitChan)
	go checkLoop2(quitChan)
	fmt.Println("server loaded")
}
<commit_msg>are u KIDDING MEEEROIJGSIJ<commit_after>package main

import "fmt"
import "strings"
import "net"  //now look at this net that I just found
import "time" //time is a tool you can put on the wall or wear it on your wrist
//the past is far behind us, the future doesn't exist
import "./players"

func getPlayerOfIp(ip string) players.NetworkPlayer {
	for _, plr := range networkPlayerList {
		plrIp := plr.GetIp()
		if plrIp == ip[:strings.Index(ip, ":")] {
			return plr
		}
	}
	//for now, add a player to the list and return it
	networkPlayerList=append(networkPlayerList,players.NewNetworkPlayer("uwe",3,ip))
	return networkPlayerList[len(networkPlayerList)-1]
}

func handleConn1(conn net.Conn) {
	fmt.Println("conn 1")
	conn.Write([]byte{65})
	/*fmt.Println(conn.RemoteAddr().String())
	plr := getPlayerOfIp(conn.RemoteAddr().String())
	for len(plr.GetDataToBeSent()) == 0 {
		time.Sleep(time.Second / 2)
	}
	_, err := conn.Write(plr.GetDataToBeSent()[0])
	if err == nil {
		plr.RemoveASentData()
	}*/
}

func handleConn2(conn net.Conn) {
	fmt.Println("conn 2")
	fmt.Println(conn.RemoteAddr().String())
	var chat []byte
	_, err := conn.Read(chat)
	fmt.Println(chat,err)
	if err != nil {
		return
	}
	chat = append([]byte("CHAT"), chat...)
	plr := getPlayerOfIp(conn.RemoteAddr().String())
	for _, plr2 := range networkPlayerList {
		if plr2 != plr {
			plr2.SendData(chat)
		}
	}
}

func checkLoop1(quitChan chan struct{}) {
	ln, _ := net.Listen("tcp", ":5252") //event listeners
	for {
		select {
		case <-quitChan:
			fmt.Println("65")
			return
		default:
			conn, _ := ln.Accept()
			go handleConn1(conn)
		}
	}
}
func checkLoop2(quitChan chan struct{}) {
	ln, _ := net.Listen("tcp", ":6565") //chat
	for {
		select {
		case <-quitChan:
			fmt.Println("65")
			return
		default:
			conn, _ := ln.Accept()
			go handleConn2(conn)
		}
	}
}

func setupServer(quitChan chan struct{}) {
	go checkLoop1(quitChan)
	go checkLoop2(quitChan)
	fmt.Println("server loaded")
}
<|endoftext|>


<commit_before>// Package disco handles Terraform's remote service discovery protocol.
//
// This protocol allows mapping from a service hostname, as produced by the
// svchost package, to a set of services supported by that host and the
// endpoint information for each supported service.
package disco

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"time"

	cleanhttp "github.com/hashicorp/go-cleanhttp"
	"github.com/hashicorp/terraform/svchost"
	"github.com/hashicorp/terraform/svchost/auth"
	"github.com/hashicorp/terraform/version"
)

const (
	discoPath        = "/.well-known/terraform.json"
	maxRedirects     = 3                // arbitrary-but-small number to prevent runaway redirect loops
	discoTimeout     = 11 * time.Second // arbitrary-but-small time limit to prevent UI "hangs" during discovery
	maxDiscoDocBytes = 1 * 1024 * 1024  // 1MB - to prevent abusive services from using loads of our memory
)

var userAgent = fmt.Sprintf("Terraform/%s (service discovery)", version.String())
var httpTransport = cleanhttp.DefaultPooledTransport() // overridden during tests, to skip TLS verification

// Disco is the main type in this package, which allows discovery on given
// hostnames and caches the results by hostname to avoid repeated requests
// for the same information.
type Disco struct {
	hostCache map[svchost.Hostname]Host
	credsSrc  auth.CredentialsSource

	// Transport is a custom http.RoundTripper to use.
	// A package default is used if this is nil.
	Transport http.RoundTripper
}

func NewDisco() *Disco {
	return &Disco{}
}

// SetCredentialsSource provides a credentials source that will be used to
// add credentials to outgoing discovery requests, where available.
//
// If this method is never called, no outgoing discovery requests will have
// credentials.
func (d *Disco) SetCredentialsSource(src auth.CredentialsSource) {
	d.credsSrc = src
}

// ForceHostServices provides a pre-defined set of services for a given
// host, which prevents the receiver from attempting network-based discovery
// for the given host. Instead, the given services map will be returned
// verbatim.
//
// When providing "forced" services, any relative URLs are resolved against
// the initial discovery URL that would have been used for network-based
// discovery, yielding the same results as if the given map were published
// at the host's default discovery URL, though using absolute URLs is strongly
// recommended to make the configured behavior more explicit.
func (d *Disco) ForceHostServices(host svchost.Hostname, services map[string]interface{}) {
	if d.hostCache == nil {
		d.hostCache = map[svchost.Hostname]Host{}
	}
	if services == nil {
		services = map[string]interface{}{}
	}
	d.hostCache[host] = Host{
		discoURL: &url.URL{
			Scheme: "https",
			Host:   string(host),
			Path:   discoPath,
		},
		services: services,
	}
}

// Discover runs the discovery protocol against the given hostname (which must
// already have been validated and prepared with svchost.ForComparison) and
// returns an object describing the services available at that host.
//
// If a given hostname supports no Terraform services at all, a non-nil but
// empty Host object is returned. When giving feedback to the end user about
// such situations, we say e.g. "the host <name> doesn't provide a module
// registry", regardless of whether that is due to that service specifically
// being absent or due to the host not providing Terraform services at all,
// since we don't wish to expose the detail of whole-host discovery to an
// end-user.
func (d *Disco) Discover(host svchost.Hostname) Host {
	if d.hostCache == nil {
		d.hostCache = map[svchost.Hostname]Host{}
	}
	if cache, cached := d.hostCache[host]; cached {
		return cache
	}

	ret := d.discover(host)
	d.hostCache[host] = ret
	return ret
}

// DiscoverServiceURL is a convenience wrapper for discovery on a given
// hostname and then looking up a particular service in the result.
func (d *Disco) DiscoverServiceURL(host svchost.Hostname, serviceID string) *url.URL {
	return d.Discover(host).ServiceURL(serviceID)
}

// discover implements the actual discovery process, with its result cached
// by the public-facing Discover method.
func (d *Disco) discover(host svchost.Hostname) Host {
	discoURL := &url.URL{
		Scheme: "https",
		Host:   string(host),
		Path:   discoPath,
	}

	t := d.Transport
	if t == nil {
		t = httpTransport
	}

	client := &http.Client{
		Transport: t,
		Timeout:   discoTimeout,

		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			log.Printf("[DEBUG] Service discovery redirected to %s", req.URL)
			if len(via) > maxRedirects {
				return errors.New("too many redirects") // (this error message will never actually be seen)
			}
			return nil
		},
	}

	var header = http.Header{}
	header.Set("User-Agent", userAgent)

	req := &http.Request{
		Method: "GET",
		URL:    discoURL,
		Header: header,
	}

	if d.credsSrc != nil {
		creds, err := d.credsSrc.ForHost(host)
		if err == nil {
			if creds != nil {
				creds.PrepareRequest(req) // alters req to include credentials
			}
		} else {
			log.Printf("[WARN] Failed to get credentials for %s: %s (ignoring)", host, err)
		}
	}

	log.Printf("[DEBUG] Service discovery for %s at %s", host, discoURL)

	ret := Host{
		discoURL: discoURL,
	}

	resp, err := client.Do(req)
	if err != nil {
		log.Printf("[WARN] Failed to request discovery document: %s", err)
		return ret // empty
	}
	if resp.StatusCode != 200 {
		log.Printf("[WARN] Failed to request discovery document: %s", resp.Status)
		return ret // empty
	}

	// If the client followed any redirects, we will have a new URL to use
	// as our base for relative resolution.
	ret.discoURL = resp.Request.URL

	contentType := resp.Header.Get("Content-Type")
	mediaType, _, err := mime.ParseMediaType(contentType)
	if err != nil {
		log.Printf("[WARN] Discovery URL has malformed Content-Type %q", contentType)
		return ret // empty
	}
	if mediaType != "application/json" {
		log.Printf("[DEBUG] Discovery URL returned Content-Type %q, rather than application/json", mediaType)
		return ret // empty
	}

	// (this doesn't catch chunked encoding, because ContentLength is -1 in that case...)
	if resp.ContentLength > maxDiscoDocBytes {
		// Size limit here is not a contractual requirement and so we may
		// adjust it over time if we find a different limit is warranted.
		log.Printf("[WARN] Discovery doc response is too large (got %d bytes; limit %d)", resp.ContentLength, maxDiscoDocBytes)
		return ret // empty
	}

	// If the response is using chunked encoding then we can't predict
	// its size, but we'll at least prevent reading the entire thing into
	// memory.
	lr := io.LimitReader(resp.Body, maxDiscoDocBytes)

	servicesBytes, err := ioutil.ReadAll(lr)
	if err != nil {
		log.Printf("[WARN] Error reading discovery document body: %s", err)
		return ret // empty
	}

	var services map[string]interface{}
	err = json.Unmarshal(servicesBytes, &services)
	if err != nil {
		log.Printf("[WARN] Failed to decode discovery document as a JSON object: %s", err)
		return ret // empty
	}

	ret.services = services
	return ret
}

// Forget invalidates any cached record of the given hostname. If the host
// has no cache entry then this is a no-op.
func (d *Disco) Forget(host svchost.Hostname) {
	delete(d.hostCache, host)
}

// ForgetAll is like Forget, but for all of the hostnames that have cache entries.
func (d *Disco) ForgetAll() {
	d.hostCache = nil
}
<commit_msg>Standardize http.Client creation with User-Agent<commit_after>// Package disco handles Terraform's remote service discovery protocol.
//
// This protocol allows mapping from a service hostname, as produced by the
// svchost package, to a set of services supported by that host and the
// endpoint information for each supported service.
package disco

import (
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"time"

	cleanhttp "github.com/hashicorp/go-cleanhttp"
	"github.com/hashicorp/terraform/svchost"
	"github.com/hashicorp/terraform/svchost/auth"
)

const (
	discoPath        = "/.well-known/terraform.json"
	maxRedirects     = 3                // arbitrary-but-small number to prevent runaway redirect loops
	discoTimeout     = 11 * time.Second // arbitrary-but-small time limit to prevent UI "hangs" during discovery
	maxDiscoDocBytes = 1 * 1024 * 1024  // 1MB - to prevent abusive services from using loads of our memory
)

var httpTransport = cleanhttp.DefaultPooledTransport() // overridden during tests, to skip TLS verification

// Disco is the main type in this package, which allows discovery on given
// hostnames and caches the results by hostname to avoid repeated requests
// for the same information.
type Disco struct {
	hostCache map[svchost.Hostname]Host
	credsSrc  auth.CredentialsSource

	// Transport is a custom http.RoundTripper to use.
	// A package default is used if this is nil.
	Transport http.RoundTripper
}

func NewDisco() *Disco {
	return &Disco{}
}

// SetCredentialsSource provides a credentials source that will be used to
// add credentials to outgoing discovery requests, where available.
//
// If this method is never called, no outgoing discovery requests will have
// credentials.
func (d *Disco) SetCredentialsSource(src auth.CredentialsSource) {
	d.credsSrc = src
}

// ForceHostServices provides a pre-defined set of services for a given
// host, which prevents the receiver from attempting network-based discovery
// for the given host. Instead, the given services map will be returned
// verbatim.
//
// When providing "forced" services, any relative URLs are resolved against
// the initial discovery URL that would have been used for network-based
// discovery, yielding the same results as if the given map were published
// at the host's default discovery URL, though using absolute URLs is strongly
// recommended to make the configured behavior more explicit.
func (d *Disco) ForceHostServices(host svchost.Hostname, services map[string]interface{}) {
	if d.hostCache == nil {
		d.hostCache = map[svchost.Hostname]Host{}
	}
	if services == nil {
		services = map[string]interface{}{}
	}
	d.hostCache[host] = Host{
		discoURL: &url.URL{
			Scheme: "https",
			Host:   string(host),
			Path:   discoPath,
		},
		services: services,
	}
}

// Discover runs the discovery protocol against the given hostname (which must
// already have been validated and prepared with svchost.ForComparison) and
// returns an object describing the services available at that host.
//
// If a given hostname supports no Terraform services at all, a non-nil but
// empty Host object is returned. When giving feedback to the end user about
// such situations, we say e.g. "the host <name> doesn't provide a module
// registry", regardless of whether that is due to that service specifically
// being absent or due to the host not providing Terraform services at all,
// since we don't wish to expose the detail of whole-host discovery to an
// end-user.
func (d *Disco) Discover(host svchost.Hostname) Host {
	if d.hostCache == nil {
		d.hostCache = map[svchost.Hostname]Host{}
	}
	if cache, cached := d.hostCache[host]; cached {
		return cache
	}

	ret := d.discover(host)
	d.hostCache[host] = ret
	return ret
}

// DiscoverServiceURL is a convenience wrapper for discovery on a given
// hostname and then looking up a particular service in the result.
func (d *Disco) DiscoverServiceURL(host svchost.Hostname, serviceID string) *url.URL {
	return d.Discover(host).ServiceURL(serviceID)
}

// discover implements the actual discovery process, with its result cached
// by the public-facing Discover method.
func (d *Disco) discover(host svchost.Hostname) Host {
	discoURL := &url.URL{
		Scheme: "https",
		Host:   string(host),
		Path:   discoPath,
	}

	t := d.Transport
	if t == nil {
		t = httpTransport
	}

	client := &http.Client{
		Transport: t,
		Timeout:   discoTimeout,

		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			log.Printf("[DEBUG] Service discovery redirected to %s", req.URL)
			if len(via) > maxRedirects {
				return errors.New("too many redirects") // (this error message will never actually be seen)
			}
			return nil
		},
	}

	req := &http.Request{
		Method: "GET",
		URL:    discoURL,
	}

	if d.credsSrc != nil {
		creds, err := d.credsSrc.ForHost(host)
		if err == nil {
			if creds != nil {
				creds.PrepareRequest(req) // alters req to include credentials
			}
		} else {
			log.Printf("[WARN] Failed to get credentials for %s: %s (ignoring)", host, err)
		}
	}

	log.Printf("[DEBUG] Service discovery for %s at %s", host, discoURL)

	ret := Host{
		discoURL: discoURL,
	}

	resp, err := client.Do(req)
	if err != nil {
		log.Printf("[WARN] Failed to request discovery document: %s", err)
		return ret // empty
	}
	if resp.StatusCode != 200 {
		log.Printf("[WARN] Failed to request discovery document: %s", resp.Status)
		return ret // empty
	}

	// If the client followed any redirects, we will have a new URL to use
	// as our base for relative resolution.
	ret.discoURL = resp.Request.URL

	contentType := resp.Header.Get("Content-Type")
	mediaType, _, err := mime.ParseMediaType(contentType)
	if err != nil {
		log.Printf("[WARN] Discovery URL has malformed Content-Type %q", contentType)
		return ret // empty
	}
	if mediaType != "application/json" {
		log.Printf("[DEBUG] Discovery URL returned Content-Type %q, rather than application/json", mediaType)
		return ret // empty
	}

	// (this doesn't catch chunked encoding, because ContentLength is -1 in that case...)
	if resp.ContentLength > maxDiscoDocBytes {
		// Size limit here is not a contractual requirement and so we may
		// adjust it over time if we find a different limit is warranted.
		log.Printf("[WARN] Discovery doc response is too large (got %d bytes; limit %d)", resp.ContentLength, maxDiscoDocBytes)
		return ret // empty
	}

	// If the response is using chunked encoding then we can't predict
	// its size, but we'll at least prevent reading the entire thing into
	// memory.
	lr := io.LimitReader(resp.Body, maxDiscoDocBytes)

	servicesBytes, err := ioutil.ReadAll(lr)
	if err != nil {
		log.Printf("[WARN] Error reading discovery document body: %s", err)
		return ret // empty
	}

	var services map[string]interface{}
	err = json.Unmarshal(servicesBytes, &services)
	if err != nil {
		log.Printf("[WARN] Failed to decode discovery document as a JSON object: %s", err)
		return ret // empty
	}

	ret.services = services
	return ret
}

// Forget invalidates any cached record of the given hostname. If the host
// has no cache entry then this is a no-op.
func (d *Disco) Forget(host svchost.Hostname) {
	delete(d.hostCache, host)
}

// ForgetAll is like Forget, but for all of the hostnames that have cache entries.
func (d *Disco) ForgetAll() {
	d.hostCache = nil
}
<|endoftext|>


<commit_before>// Package disco handles Terraform's remote service discovery protocol.
//
// This protocol allows mapping from a service hostname, as produced by the
// svchost package, to a set of services supported by that host and the
// endpoint information for each supported service.
package disco

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"time"

	cleanhttp "github.com/hashicorp/go-cleanhttp"
	"github.com/hashicorp/terraform/httpclient"
	"github.com/hashicorp/terraform/svchost"
	"github.com/hashicorp/terraform/svchost/auth"
)

const (
	// Fixed path to the discovery manifest.
	discoPath = "/.well-known/terraform.json"

	// Arbitrary-but-small number to prevent runaway redirect loops.
	maxRedirects = 3

	// Arbitrary-but-small time limit to prevent UI "hangs" during discovery.
	discoTimeout = 11 * time.Second

	// 1MB - to prevent abusive services from using loads of our memory.
	maxDiscoDocBytes = 1 * 1024 * 1024
)

// httpTransport is overridden during tests, to skip TLS verification.
var httpTransport = cleanhttp.DefaultPooledTransport()

// Disco is the main type in this package, which allows discovery on given
// hostnames and caches the results by hostname to avoid repeated requests
// for the same information.
type Disco struct {
	hostCache map[svchost.Hostname]*Host
	credsSrc  auth.CredentialsSource

	// Transport is a custom http.RoundTripper to use.
	// A package default is used if this is nil.
	Transport http.RoundTripper
}

// New returns a new initialized discovery object.
func New() *Disco {
	return NewWithCredentialsSource(nil)
}

// NewWithCredentialsSource returns a new discovery object initialized with
// the given credentials source.
func NewWithCredentialsSource(credsSrc auth.CredentialsSource) *Disco {
	return &Disco{
		hostCache: make(map[svchost.Hostname]*Host),
		credsSrc:  credsSrc,
	}
}

// SetCredentialsSource provides a credentials source that will be used to
// add credentials to outgoing discovery requests, where available.
//
// If this method is never called, no outgoing discovery requests will have
// credentials.
func (d *Disco) SetCredentialsSource(src auth.CredentialsSource) {
	d.credsSrc = src
}

// CredentialsForHost returns a non-nil HostCredentials if the embedded source has
// credentials available for the host, and a nil HostCredentials if it does not.
func (d *Disco) CredentialsForHost(hostname svchost.Hostname) (auth.HostCredentials, error) {
	if d.credsSrc == nil {
		return nil, nil
	}
	return d.credsSrc.ForHost(hostname)
}

// ForceHostServices provides a pre-defined set of services for a given
// host, which prevents the receiver from attempting network-based discovery
// for the given host. Instead, the given services map will be returned
// verbatim.
//
// When providing "forced" services, any relative URLs are resolved against
// the initial discovery URL that would have been used for network-based
// discovery, yielding the same results as if the given map were published
// at the host's default discovery URL, though using absolute URLs is strongly
// recommended to make the configured behavior more explicit.
func (d *Disco) ForceHostServices(hostname svchost.Hostname, services map[string]interface{}) {
	if services == nil {
		services = map[string]interface{}{}
	}
	transport := d.Transport
	if transport == nil {
		transport = httpTransport
	}
	d.hostCache[hostname] = &Host{
		discoURL: &url.URL{
			Scheme: "https",
			Host:   string(hostname),
			Path:   discoPath,
		},
		hostname:  hostname.ForDisplay(),
		services:  services,
		transport: transport,
	}
}

// Discover runs the discovery protocol against the given hostname (which must
// already have been validated and prepared with svchost.ForComparison) and
// returns an object describing the services available at that host.
//
// If a given hostname supports no Terraform services at all, a non-nil but
// empty Host object is returned. When giving feedback to the end user about
// such situations, we say "host <name> does not provide a <service> service",
// regardless of whether that is due to that service specifically being absent
// or due to the host not providing Terraform services at all, since we don't
// wish to expose the detail of whole-host discovery to an end-user.
func (d *Disco) Discover(hostname svchost.Hostname) (*Host, error) {
	if host, cached := d.hostCache[hostname]; cached {
		return host, nil
	}

	host, err := d.discover(hostname)
	if err != nil {
		return nil, err
	}
	d.hostCache[hostname] = host

	return host, nil
}

// DiscoverServiceURL is a convenience wrapper for discovery on a given
// hostname and then looking up a particular service in the result.
func (d *Disco) DiscoverServiceURL(hostname svchost.Hostname, serviceID string) (*url.URL, error) {
	host, err := d.Discover(hostname)
	if err != nil {
		return nil, err
	}
	return host.ServiceURL(serviceID)
}

// discover implements the actual discovery process, with its result cached
// by the public-facing Discover method.
func (d *Disco) discover(hostname svchost.Hostname) (*Host, error) {
	discoURL := &url.URL{
		Scheme: "https",
		Host:   hostname.String(),
		Path:   discoPath,
	}

	transport := d.Transport
	if transport == nil {
		transport = httpTransport
	}

	client := &http.Client{
		Transport: transport,
		Timeout:   discoTimeout,

		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			log.Printf("[DEBUG] Service discovery redirected to %s", req.URL)
			if len(via) > maxRedirects {
				return errors.New("too many redirects") // this error will never actually be seen
			}
			return nil
		},
	}

	req := &http.Request{
		Header: make(http.Header),
		Method: "GET",
		URL:    discoURL,
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("User-Agent", httpclient.UserAgentString())

	creds, err := d.CredentialsForHost(hostname)
	if err != nil {
		log.Printf("[WARN] Failed to get credentials for %s: %s (ignoring)", hostname, err)
	}
	if creds != nil {
		// Update the request to include credentials.
		creds.PrepareRequest(req)
	}

	log.Printf("[DEBUG] Service discovery for %s at %s", hostname, discoURL)

	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("Failed to request discovery document: %v", err)
	}
	defer resp.Body.Close()

	host := &Host{
		// Use the discovery URL from resp.Request in
		// case the client followed any redirects.
		discoURL:  resp.Request.URL,
		hostname:  hostname.ForDisplay(),
		transport: transport,
	}

	// Return the host without any services.
	if resp.StatusCode == 404 {
		return host, nil
	}

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("Failed to request discovery document: %s", resp.Status)
	}

	contentType := resp.Header.Get("Content-Type")
	mediaType, _, err := mime.ParseMediaType(contentType)
	if err != nil {
		return nil, fmt.Errorf("Discovery URL has a malformed Content-Type %q", contentType)
	}
	if mediaType != "application/json" {
		return nil, fmt.Errorf("Discovery URL returned an unsupported Content-Type %q", mediaType)
	}

	// This doesn't catch chunked encoding, because ContentLength is -1 in that case.
	if resp.ContentLength > maxDiscoDocBytes {
		// Size limit here is not a contractual requirement and so we may
		// adjust it over time if we find a different limit is warranted.
		return nil, fmt.Errorf(
			"Discovery doc response is too large (got %d bytes; limit %d)",
			resp.ContentLength, maxDiscoDocBytes,
		)
	}

	// If the response is using chunked encoding then we can't predict its
	// size, but we'll at least prevent reading the entire thing into memory.
	lr := io.LimitReader(resp.Body, maxDiscoDocBytes)

	servicesBytes, err := ioutil.ReadAll(lr)
	if err != nil {
		return nil, fmt.Errorf("Error reading discovery document body: %v", err)
	}

	var services map[string]interface{}
	err = json.Unmarshal(servicesBytes, &services)
	if err != nil {
		return nil, fmt.Errorf("Failed to decode discovery document as a JSON object: %v", err)
	}
	host.services = services

	return host, nil
}

// Forget invalidates any cached record of the given hostname. If the host
// has no cache entry then this is a no-op.
func (d *Disco) Forget(hostname svchost.Hostname) {
	delete(d.hostCache, hostname)
}

// ForgetAll is like Forget, but for all of the hostnames that have cache entries.
func (d *Disco) ForgetAll() {
	d.hostCache = make(map[svchost.Hostname]*Host)
}
<commit_msg>disco: Set transport early (during initialization)<commit_after>// Package disco handles Terraform's remote service discovery protocol.
//
// This protocol allows mapping from a service hostname, as produced by the
// svchost package, to a set of services supported by that host and the
// endpoint information for each supported service.
package disco

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"time"

	cleanhttp "github.com/hashicorp/go-cleanhttp"
	"github.com/hashicorp/terraform/httpclient"
	"github.com/hashicorp/terraform/svchost"
	"github.com/hashicorp/terraform/svchost/auth"
)

const (
	// Fixed path to the discovery manifest.
	discoPath = "/.well-known/terraform.json"

	// Arbitrary-but-small number to prevent runaway redirect loops.
	maxRedirects = 3

	// Arbitrary-but-small time limit to prevent UI "hangs" during discovery.
	discoTimeout = 11 * time.Second

	// 1MB - to prevent abusive services from using loads of our memory.
	maxDiscoDocBytes = 1 * 1024 * 1024
)

// httpTransport is overridden during tests, to skip TLS verification.
var httpTransport = cleanhttp.DefaultPooledTransport()

// Disco is the main type in this package, which allows discovery on given
// hostnames and caches the results by hostname to avoid repeated requests
// for the same information.
type Disco struct {
	hostCache map[svchost.Hostname]*Host
	credsSrc  auth.CredentialsSource

	// Transport is a custom http.RoundTripper to use.
	Transport http.RoundTripper
}

// New returns a new initialized discovery object.
func New() *Disco {
	return NewWithCredentialsSource(nil)
}

// NewWithCredentialsSource returns a new discovery object initialized with
// the given credentials source.
func NewWithCredentialsSource(credsSrc auth.CredentialsSource) *Disco {
	return &Disco{
		hostCache: make(map[svchost.Hostname]*Host),
		credsSrc:  credsSrc,
		Transport: httpTransport,
	}
}

// SetCredentialsSource provides a credentials source that will be used to
// add credentials to outgoing discovery requests, where available.
//
// If this method is never called, no outgoing discovery requests will have
// credentials.
func (d *Disco) SetCredentialsSource(src auth.CredentialsSource) {
	d.credsSrc = src
}

// CredentialsForHost returns a non-nil HostCredentials if the embedded source has
// credentials available for the host, and a nil HostCredentials if it does not.
func (d *Disco) CredentialsForHost(hostname svchost.Hostname) (auth.HostCredentials, error) {
	if d.credsSrc == nil {
		return nil, nil
	}
	return d.credsSrc.ForHost(hostname)
}

// ForceHostServices provides a pre-defined set of services for a given
// host, which prevents the receiver from attempting network-based discovery
// for the given host. Instead, the given services map will be returned
// verbatim.
//
// When providing "forced" services, any relative URLs are resolved against
// the initial discovery URL that would have been used for network-based
// discovery, yielding the same results as if the given map were published
// at the host's default discovery URL, though using absolute URLs is strongly
// recommended to make the configured behavior more explicit.
func (d *Disco) ForceHostServices(hostname svchost.Hostname, services map[string]interface{}) {
	if services == nil {
		services = map[string]interface{}{}
	}

	d.hostCache[hostname] = &Host{
		discoURL: &url.URL{
			Scheme: "https",
			Host:   string(hostname),
			Path:   discoPath,
		},
		hostname:  hostname.ForDisplay(),
		services:  services,
		transport: d.Transport,
	}
}

// Discover runs the discovery protocol against the given hostname (which must
// already have been validated and prepared with svchost.ForComparison) and
// returns an object describing the services available at that host.
//
// If a given hostname supports no Terraform services at all, a non-nil but
// empty Host object is returned. When giving feedback to the end user about
// such situations, we say "host <name> does not provide a <service> service",
// regardless of whether that is due to that service specifically being absent
// or due to the host not providing Terraform services at all, since we don't
// wish to expose the detail of whole-host discovery to an end-user.
func (d *Disco) Discover(hostname svchost.Hostname) (*Host, error) {
	if host, cached := d.hostCache[hostname]; cached {
		return host, nil
	}

	host, err := d.discover(hostname)
	if err != nil {
		return nil, err
	}
	d.hostCache[hostname] = host

	return host, nil
}

// DiscoverServiceURL is a convenience wrapper for discovery on a given
// hostname and then looking up a particular service in the result.
func (d *Disco) DiscoverServiceURL(hostname svchost.Hostname, serviceID string) (*url.URL, error) {
	host, err := d.Discover(hostname)
	if err != nil {
		return nil, err
	}
	return host.ServiceURL(serviceID)
}

// discover implements the actual discovery process, with its result cached
// by the public-facing Discover method.
func (d *Disco) discover(hostname svchost.Hostname) (*Host, error) {
	discoURL := &url.URL{
		Scheme: "https",
		Host:   hostname.String(),
		Path:   discoPath,
	}

	client := &http.Client{
		Transport: d.Transport,
		Timeout:   discoTimeout,

		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			log.Printf("[DEBUG] Service discovery redirected to %s", req.URL)
			if len(via) > maxRedirects {
				return errors.New("too many redirects") // this error will never actually be seen
			}
			return nil
		},
	}

	req := &http.Request{
		Header: make(http.Header),
		Method: "GET",
		URL:    discoURL,
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("User-Agent", httpclient.UserAgentString())

	creds, err := d.CredentialsForHost(hostname)
	if err != nil {
		log.Printf("[WARN] Failed to get credentials for %s: %s (ignoring)", hostname, err)
	}
	if creds != nil {
		// Update the request to include credentials.
		creds.PrepareRequest(req)
	}

	log.Printf("[DEBUG] Service discovery for %s at %s", hostname, discoURL)

	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("Failed to request discovery document: %v", err)
	}
	defer resp.Body.Close()

	host := &Host{
		// Use the discovery URL from resp.Request in
		// case the client followed any redirects.
		discoURL:  resp.Request.URL,
		hostname:  hostname.ForDisplay(),
		transport: d.Transport,
	}

	// Return the host without any services.
	if resp.StatusCode == 404 {
		return host, nil
	}

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("Failed to request discovery document: %s", resp.Status)
	}

	contentType := resp.Header.Get("Content-Type")
	mediaType, _, err := mime.ParseMediaType(contentType)
	if err != nil {
		return nil, fmt.Errorf("Discovery URL has a malformed Content-Type %q", contentType)
	}
	if mediaType != "application/json" {
		return nil, fmt.Errorf("Discovery URL returned an unsupported Content-Type %q", mediaType)
	}

	// This doesn't catch chunked encoding, because ContentLength is -1 in that case.
	if resp.ContentLength > maxDiscoDocBytes {
		// Size limit here is not a contractual requirement and so we may
		// adjust it over time if we find a different limit is warranted.
		return nil, fmt.Errorf(
			"Discovery doc response is too large (got %d bytes; limit %d)",
			resp.ContentLength, maxDiscoDocBytes,
		)
	}

	// If the response is using chunked encoding then we can't predict its
	// size, but we'll at least prevent reading the entire thing into memory.
	lr := io.LimitReader(resp.Body, maxDiscoDocBytes)

	servicesBytes, err := ioutil.ReadAll(lr)
	if err != nil {
		return nil, fmt.Errorf("Error reading discovery document body: %v", err)
	}

	var services map[string]interface{}
	err = json.Unmarshal(servicesBytes, &services)
	if err != nil {
		return nil, fmt.Errorf("Failed to decode discovery document as a JSON object: %v", err)
	}
	host.services = services

	return host, nil
}

// Forget invalidates any cached record of the given hostname. If the host
// has no cache entry then this is a no-op.
func (d *Disco) Forget(hostname svchost.Hostname) {
	delete(d.hostCache, hostname)
}

// ForgetAll is like Forget, but for all of the hostnames that have cache entries.
func (d *Disco) ForgetAll() {
	d.hostCache = make(map[svchost.Hostname]*Host)
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"time"

	"github.com/99designs/aws-vault/vault"
)

const (
	metadataBind    = "169.254.169.254:80"
	awsTimeFormat   = "2006-01-02T15:04:05Z"
	localServerUrl  = "http://127.0.0.1:9099"
	localServerBind = "127.0.0.1:9099"
)

func StartMetadataServer() error {
	if _, err := installNetworkAlias(); err != nil {
		return err
	}

	router := http.NewServeMux()
	router.HandleFunc("/latest/meta-data/iam/security-credentials/", indexHandler)
	router.HandleFunc("/latest/meta-data/iam/security-credentials/local-credentials", credentialsHandler)
	// The AWS Go SDK checks the instance-id endpoint to validate the existence of EC2 Metadata
	router.HandleFunc("/latest/meta-data/instance-id/", instanceIdHandler)

	l, err := net.Listen("tcp", metadataBind)
	if err != nil {
		return err
	}

	log.Printf("Local instance role server running on %s", l.Addr())
	return http.Serve(l, router)
}

type metadataHandler struct {
	http.Handler
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "local-credentials")
}

func credentialsHandler(w http.ResponseWriter, r *http.Request) {
	resp, err := http.Get(localServerUrl)
	if err != nil {
		http.Error(w, err.Error(), http.StatusGatewayTimeout)
		return
	}
	defer resp.Body.Close()

	log.Printf("Fetched credentials from %s", localServerUrl)

	w.Header().Set("Content-Type", "text/plain")
	w.WriteHeader(http.StatusOK)

	io.Copy(w, resp.Body)
}

func instanceIdHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "aws-vault")
}

func checkServerRunning(bind string) bool {
	_, err := net.DialTimeout("tcp", bind, time.Millisecond*10)
	return err == nil
}

func StartCredentialProxyOnWindows() error {
	log.Printf("Starting `aws-vault server` in the background")
	cmd := exec.Command(os.Args[0], "server")
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Start(); err != nil {
		return err
	}
	time.Sleep(time.Second * 1)
	if !checkServerRunning(metadataBind) {
		return errors.New("The credential proxy server isn't running. Run aws-vault server as Administrator in the background and then try this command again")
	}
	return nil
}

func StartCredentialProxyWithSudo() error {
	log.Printf("Starting `aws-vault server` as root in the background")
	cmd := exec.Command("sudo", "-b", os.Args[0], "server")
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func StartCredentialProxy() error {
	if runtime.GOOS == "windows" {
		return StartCredentialProxyOnWindows()
	}
	return StartCredentialProxyWithSudo()
}

func StartCredentialsServer(creds *vault.VaultCredentials) error {
	if !checkServerRunning(metadataBind) {
		if err := StartCredentialProxy(); err != nil {
			return err
		}
	}

	l, err := net.Listen("tcp", localServerBind)
	if err != nil {
		return err
	}

	log.Printf("Local instance role server running on %s", l.Addr())
	go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ip, _, err := net.SplitHostPort(r.RemoteAddr)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		// Must make sure the remote ip is from the loopback, otherwise clients on the same network segment could
		// potentially route traffic via 169.254.169.254:80
		// See https://developer.apple.com/library/content/qa/qa1357/_index.html
		if !net.ParseIP(ip).IsLoopback() {
			http.Error(w, "Access denied from non-localhost address", http.StatusUnauthorized)
			return
		}

		log.Printf("RemoteAddr = %v", r.RemoteAddr)
		log.Printf("Credentials.IsExpired() = %#v", creds.IsExpired())

		val, err := creds.Get()
		if err != nil {
			http.Error(w, err.Error(), http.StatusGatewayTimeout)
			return
		}

		log.Printf("Serving credentials via http ****************%s, expiration of %s (%s)",
			val.AccessKeyID[len(val.AccessKeyID)-4:],
			creds.Expires().Format(awsTimeFormat),
			creds.Expires().Sub(time.Now()).String())

		json.NewEncoder(w).Encode(map[string]interface{}{
			"Code":            "Success",
			"LastUpdated":     time.Now().Format(awsTimeFormat),
			"Type":            "AWS-HMAC",
			"AccessKeyId":     val.AccessKeyID,
			"SecretAccessKey": val.SecretAccessKey,
			"Token":           val.SessionToken,
			"Expiration":      creds.Expires().Format(awsTimeFormat),
		})
	}))

	return nil
}
<commit_msg>Add handler for iam/info<commit_after>package server

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"time"

	"github.com/99designs/aws-vault/vault"
)

const (
	metadataBind    = "169.254.169.254:80"
	awsTimeFormat   = "2006-01-02T15:04:05Z"
	localServerUrl  = "http://127.0.0.1:9099"
	localServerBind = "127.0.0.1:9099"
)

func StartMetadataServer() error {
	if _, err := installNetworkAlias(); err != nil {
		return err
	}

	router := http.NewServeMux()
	// The AWS .NET SDK checks this endpoint during obtaining credentials/refreshing them
	router.HandleFunc("/latest/meta-data/iam/info/", infoHandler)
	router.HandleFunc("/latest/meta-data/iam/security-credentials/", indexHandler)
	router.HandleFunc("/latest/meta-data/iam/security-credentials/local-credentials", credentialsHandler)
	// The AWS Go SDK checks the instance-id endpoint to validate the existence of EC2 Metadata
	router.HandleFunc("/latest/meta-data/instance-id/", instanceIdHandler)

	l, err := net.Listen("tcp", metadataBind)
	if err != nil {
		return err
	}

	log.Printf("Local instance role server running on %s", l.Addr())
	return http.Serve(l, router)
}

type metadataHandler struct {
	http.Handler
}

func infoHandler(w http.ResponseWriter, r *http.Request) {
	var jsonStr = []byte(`{"Code" : "Success"}`)
	fmt.Fprintf(w, string(jsonStr))
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "local-credentials")
}

func credentialsHandler(w http.ResponseWriter, r *http.Request) {
	resp, err := http.Get(localServerUrl)
	if err != nil {
		http.Error(w, err.Error(), http.StatusGatewayTimeout)
		return
	}
	defer resp.Body.Close()

	log.Printf("Fetched credentials from %s", localServerUrl)

	w.Header().Set("Content-Type", "text/plain")
	w.WriteHeader(http.StatusOK)

	io.Copy(w, resp.Body)
}

func instanceIdHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "aws-vault")
}

func checkServerRunning(bind string) bool {
	_, err := net.DialTimeout("tcp", bind, time.Millisecond*10)
	return err == nil
}

func StartCredentialProxyOnWindows() error {
	log.Printf("Starting `aws-vault server` in the background")
	cmd := exec.Command(os.Args[0], "server")
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Start(); err != nil {
		return err
	}
	time.Sleep(time.Second * 1)
	if !checkServerRunning(metadataBind) {
		return errors.New("The credential proxy server isn't running. Run aws-vault server as Administrator in the background and then try this command again")
	}
	return nil
}

func StartCredentialProxyWithSudo() error {
	log.Printf("Starting `aws-vault server` as root in the background")
	cmd := exec.Command("sudo", "-b", os.Args[0], "server")
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func StartCredentialProxy() error {
	if runtime.GOOS == "windows" {
		return StartCredentialProxyOnWindows()
	}
	return StartCredentialProxyWithSudo()
}

func StartCredentialsServer(creds *vault.VaultCredentials) error {
	if !checkServerRunning(metadataBind) {
		if err := StartCredentialProxy(); err != nil {
			return err
		}
	}

	l, err := net.Listen("tcp", localServerBind)
	if err != nil {
		return err
	}

	log.Printf("Local instance role server running on %s", l.Addr())
	go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ip, _, err := net.SplitHostPort(r.RemoteAddr)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		// Must make sure the remote ip is from the loopback, otherwise clients on the same network segment could
		// potentially route traffic via 169.254.169.254:80
		// See https://developer.apple.com/library/content/qa/qa1357/_index.html
		if !net.ParseIP(ip).IsLoopback() {
			http.Error(w, "Access denied from non-localhost address", http.StatusUnauthorized)
			return
		}

		log.Printf("RemoteAddr = %v", r.RemoteAddr)
		log.Printf("Credentials.IsExpired() = %#v", creds.IsExpired())

		val, err := creds.Get()
		if err != nil {
			http.Error(w, err.Error(), http.StatusGatewayTimeout)
			return
		}

		log.Printf("Serving credentials via http ****************%s, expiration of %s (%s)",
			val.AccessKeyID[len(val.AccessKeyID)-4:],
			creds.Expires().Format(awsTimeFormat),
			creds.Expires().Sub(time.Now()).String())

		json.NewEncoder(w).Encode(map[string]interface{}{
			"Code":            "Success",
			"LastUpdated":     time.Now().Format(awsTimeFormat),
			"Type":            "AWS-HMAC",
			"AccessKeyId":     val.AccessKeyID,
			"SecretAccessKey": val.SecretAccessKey,
			"Token":           val.SessionToken,
			"Expiration":      creds.Expires().Format(awsTimeFormat),
		})
	}))

	return nil
}
<|endoftext|>


<commit_before>package skim

import (
	"reflect"
	"testing"

	"go.spiff.io/skim/internal/debug"
)

func TestList(t *testing.T) {
	type testcase struct {
		Args []Atom
		Want Atom
	}
	cases := []testcase{
		{
			Args: nil,
			Want: &Cons{},
		},
		{
			Args: []Atom{Int(1)},
			Want: &Cons{Int(1), nil},
		},
		{
			Args: []Atom{Int(1), Bool(false)},
			Want: &Cons{Int(1), &Cons{Bool(false), nil}},
		},
	}

	for _, c := range cases {
		c := c
		t.Run(c.Want.String(), func(t *testing.T) {
			debug.SetLoggerf(t.Logf)
			got := List(c.Args...)
			if !reflect.DeepEqual(got, c.Want) {
				t.Fatalf("List(%v) = %v; want %v", c.Args, got, c.Want)
			}
		})
	}
}

func TestCadr(t *testing.T) {
	seq := List(Int(1), Int(2), Int(3), Int(4), Int(5))
	nestl1 := List(List(Int(1)), List(Int(2)), List(Int(3)), List(Int(4)))
	nestr1 := &Cons{List(Int(1), Int(2), Int(3), Int(4)), Int(5)}

	type testCase struct {
		fn   func(Atom) (Atom, error)
		in   Atom
		want Atom
		err  error
	}

	cases := map[string]testCase{
		// If the following work, the rest should follow
		"Car":    {fn: Car, in: nestl1, want: List(Int(1))},
		"Cadr":   {fn: Cadr, in: nestl1, want: List(Int(2))},
		"Caddr":  {fn: Caddr, in: nestl1, want: List(Int(3))},
		"Cadddr": {fn: Cadddr, in: nestl1, want: List(Int(4))},

		"Cdr":    {fn: Cdr, in: nestr1, want: Int(5)},
		"Cdar":   {fn: Cdar, in: nestr1, want: List(Int(2), Int(3), Int(4))},
		"Cddar":  {fn: Cddar, in: nestr1, want: List(Int(3), Int(4))},
		"Cdddar": {fn: Cdddar, in: nestr1, want: List(Int(4))},

		"Cddr":   {fn: Cddr, in: seq, want: List(Int(3), Int(4), Int(5))},
		"Cdddr":  {fn: Cdddr, in: seq, want: List(Int(4), Int(5))},
		"Cddddr": {fn: Cddddr, in: seq, want: List(Int(5))},

		"CarError":  {fn: Car, in: Int(1), err: &CadrError{op: "car", typ: reflect.TypeOf(Int(0))}},
		"CdrError":  {fn: Cdr, in: Int(1), err: &CadrError{op: "cdr", typ: reflect.TypeOf(Int(0))}},
		"CadrError": {fn: Cadr, in: &Cons{Int(1), Int(2)}, err: &CadrError{op: "car", typ: reflect.TypeOf(Int(0))}},
		"CddrError": {fn: Cddr, in: &Cons{Int(1), Int(2)}, err: &CadrError{op: "cdr", typ: reflect.TypeOf(Int(0))}},
	}

	for name, c := range cases {
		name, c := name, c
		t.Run(name, func(t *testing.T) {
			debug.SetLoggerf(t.Logf)
			got, err := c.fn(c.in)
			switch {
			case !reflect.DeepEqual(c.err, err):
				t.Fatalf("%s(..) err = %v; want %v", name, err, c.err)
			case c.err != nil && err != nil && c.err.Error() != err.Error():
				t.Fatalf("%s(..) err = %v; want %v", name, err, c.err)
			case !reflect.DeepEqual(c.want, got):
				t.Fatalf("%s(..) = %v; want %v", name, got, c.want)
			}
		})
	}
}
<commit_msg>Add cadr() benchmark<commit_after>package skim

import (
	"reflect"
	"testing"

	"go.spiff.io/skim/internal/debug"
)

func TestList(t *testing.T) {
	type testcase struct {
		Args []Atom
		Want Atom
	}
	cases := []testcase{
		{
			Args: nil,
			Want: &Cons{},
		},
		{
			Args: []Atom{Int(1)},
			Want: &Cons{Int(1), nil},
		},
		{
			Args: []Atom{Int(1), Bool(false)},
			Want: &Cons{Int(1), &Cons{Bool(false), nil}},
		},
	}

	for _, c := range cases {
		c := c
		t.Run(c.Want.String(), func(t *testing.T) {
			debug.SetLoggerf(t.Logf)
			got := List(c.Args...)
			if !reflect.DeepEqual(got, c.Want) {
				t.Fatalf("List(%v) = %v; want %v", c.Args, got, c.Want)
			}
		})
	}
}

func TestCadr(t *testing.T) {
	seq := List(Int(1), Int(2), Int(3), Int(4), Int(5))
	nestl1 := List(List(Int(1)), List(Int(2)), List(Int(3)), List(Int(4)))
	nestr1 := &Cons{List(Int(1), Int(2), Int(3), Int(4)), Int(5)}

	type testCase struct {
		fn   func(Atom) (Atom, error)
		in   Atom
		want Atom
		err  error
	}

	cases := map[string]testCase{
		// If the following work, the rest should follow
		"Car":    {fn: Car, in: nestl1, want: List(Int(1))},
		"Cadr":   {fn: Cadr, in: nestl1, want: List(Int(2))},
		"Caddr":  {fn: Caddr, in: nestl1, want: List(Int(3))},
		"Cadddr": {fn: Cadddr, in: nestl1, want: List(Int(4))},

		"Cdr":    {fn: Cdr, in: nestr1, want: Int(5)},
		"Cdar":   {fn: Cdar, in: nestr1, want: List(Int(2), Int(3), Int(4))},
		"Cddar":  {fn: Cddar, in: nestr1, want: List(Int(3), Int(4))},
		"Cdddar": {fn: Cdddar, in: nestr1, want: List(Int(4))},

		"Cddr":   {fn: Cddr, in: seq, want: List(Int(3), Int(4), Int(5))},
		"Cdddr":  {fn: Cdddr, in: seq, want: List(Int(4), Int(5))},
		"Cddddr": {fn: Cddddr, in: seq, want: List(Int(5))},

		"CarError":  {fn: Car, in: Int(1), err: &CadrError{op: "car", typ: reflect.TypeOf(Int(0))}},
		"CdrError":  {fn: Cdr, in: Int(1), err: &CadrError{op: "cdr", typ: reflect.TypeOf(Int(0))}},
		"CadrError": {fn: Cadr, in: &Cons{Int(1), Int(2)}, err: &CadrError{op: "car", typ: reflect.TypeOf(Int(0))}},
		"CddrError": {fn: Cddr, in: &Cons{Int(1), Int(2)}, err: &CadrError{op: "cdr", typ: reflect.TypeOf(Int(0))}},
	}

	for name, c := range cases {
		name, c := name, c
		t.Run(name, func(t *testing.T) {
			debug.SetLoggerf(t.Logf)
			got, err := c.fn(c.in)
			switch {
			case !reflect.DeepEqual(c.err, err):
				t.Fatalf("%s(..) err = %v; want %v", name, err, c.err)
			case c.err != nil && err != nil && c.err.Error() != err.Error():
				t.Fatalf("%s(..) err = %v; want %v", name, err, c.err)
			case !reflect.DeepEqual(c.want, got):
				t.Fatalf("%s(..) = %v; want %v", name, got, c.want)
			}
		})
	}
}

func BenchmarkCadrs(b *testing.B) {
	seq := List(Int(0), Int(0), Int(0), Int(0))
	seq = List(seq, seq, seq, seq, seq)
	seq = List(seq, seq, seq, seq, seq)
	seq = List(seq, seq, seq, seq, seq)

	cases := map[string]func(Atom) (Atom, error){
		"Car":    Car,
		"Cdr":    Cdr,
		"Caar":   Caar,
		"Cadr":   Cadr,
		"Cdar":   Cdar,
		"Cddr":   Cddr,
		"Caaar":  Caaar,
		"Caadr":  Caadr,
		"Cadar":  Cadar,
		"Caddr":  Caddr,
		"Cdaar":  Cdaar,
		"Cdadr":  Cdadr,
		"Cddar":  Cddar,
		"Cdddr":  Cdddr,
		"Caaaar": Caaaar,
		"Caaadr": Caaadr,
		"Caadar": Caadar,
		"Caaddr": Caaddr,
		"Cadaar": Cadaar,
		"Cadadr": Cadadr,
		"Caddar": Caddar,
		"Cadddr": Cadddr,
		"Cdaaar": Cdaaar,
		"Cdaadr": Cdaadr,
		"Cdadar": Cdadar,
		"Cdaddr": Cdaddr,
		"Cddaar": Cddaar,
		"Cddadr": Cddadr,
		"Cdddar": Cdddar,
		"Cddddr": Cddddr,
	}

	b.ResetTimer()

	for name, fn := range cases {
		b.Run(name, func(b *testing.B) {
			for i := b.N; i > 0; i-- {
				if a, err := fn(seq); a == nil || err != nil {
					b.FailNow()
				}
			}
		})
	}
}
<|endoftext|>


<commit_before>package server

import (
	"crypto/subtle"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/braintree/manners"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/meatballhat/negroni-logrus"
	"github.com/pkg/errors"
	"github.com/travis-ci/jupiter-brain"
	"github.com/travis-ci/jupiter-brain/metrics"
	"github.com/travis-ci/jupiter-brain/server/jsonapi"
	"github.com/travis-ci/jupiter-brain/server/negroniraven"
	"golang.org/x/net/context"
)

type server struct {
	addr, authToken, sentryDSN string

	log *logrus.Logger

	i jupiterbrain.InstanceManager

	n *negroni.Negroni
	r *mux.Router
	s *manners.GracefulServer

	db       database
	bootTime time.Time
}

func newServer(cfg *Config) (*server, error) {
	log := logrus.New()
	if cfg.Debug {
		log.Level = logrus.DebugLevel
	}

	log.Formatter = &logrus.TextFormatter{DisableColors: true}

	u, err := url.Parse(cfg.VSphereURL)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse vsphere url")
	}

	if !u.IsAbs() {
		return nil, errors.Errorf("vSphere API URL must be absolute")
	}

	db, err := newPGDatabase(cfg.DatabaseURL)
	if err != nil {
		return nil, errors.Wrap(err, "failed to create postgres database")
	}

	paths := jupiterbrain.VSpherePaths{
		BasePath:    cfg.VSphereBasePath,
		VMPath:      cfg.VSphereVMPath,
		ClusterPath: cfg.VSphereClusterPath,
	}

	srv := &server{
		addr:      cfg.Addr,
		authToken: cfg.AuthToken,
		sentryDSN: cfg.SentryDSN,

		log: log,

		i: jupiterbrain.NewVSphereInstanceManager(log, u, paths),

		n: negroni.New(),
		r: mux.NewRouter(),
		s: manners.NewServer(),

		db:       db,
		bootTime: time.Now().UTC(),
	}

	return srv, nil
}

func (srv *server) Setup() {
	srv.setupRoutes()
	srv.setupMiddleware()
	go srv.signalHandler()
}

func (srv *server) Run() {
	srv.log.WithField("addr", srv.addr).Info("Listening")
	srv.s.Addr = srv.addr
	srv.s.Handler = srv.n
	err := srv.s.ListenAndServe()
	if err != nil {
		srv.log.WithField("err", err).Error("ListenAndServe failed")
	}
}

func (srv *server) setupRoutes() {
	srv.r.HandleFunc(`/instances`, srv.handleInstancesList).Methods("GET").Name("instances-list")
	srv.r.HandleFunc(`/instances`, srv.handleInstancesCreate).Methods("POST").Name("instances-create")
	srv.r.HandleFunc(`/instances/{id}`, srv.handleInstanceByIDFetch).Methods("GET").Name("instance-by-id")
	srv.r.HandleFunc(`/instances/{id}`, srv.handleInstanceByIDTerminate).Methods("DELETE").Name("instance-by-id-terminate")
	srv.r.HandleFunc(`/instance-syncs`, srv.handleInstanceSync).Methods("POST").Name("instance-syncs-create")
}

func (srv *server) setupMiddleware() {
	srv.n.Use(negroni.NewRecovery())
	srv.n.Use(negronilogrus.NewCustomMiddleware(srv.log.Level, srv.log.Formatter, "web"))
	srv.n.Use(negroni.HandlerFunc(srv.authMiddleware))
	nr, err := negroniraven.NewMiddleware(srv.sentryDSN)
	if err != nil {
		panic(err)
	}
	srv.n.Use(nr)
	srv.n.UseHandler(srv.r)
}

func (srv *server) authMiddleware(w http.ResponseWriter, req *http.Request, f http.HandlerFunc) {
	authHeader := req.Header.Get("Authorization")
	srv.log.WithField("authorization", authHeader).Debug("raw authorization header")

	if authHeader == "" {
		w.Header().Set("WWW-Authenticate", "token")
		srv.log.WithField("request_id", req.Header.Get("X-Request-ID")).Debug("responding 401 due to empty Authorization header")

		jsonapi.Error(w, errors.New("token is required"), http.StatusUnauthorized)
		return
	}

	if strings.HasPrefix(authHeader, "token ") && subtle.ConstantTimeCompare([]byte("token "+srv.authToken), []byte(authHeader)) == 1 {
		f(w, req)
		return
	} else if strings.HasPrefix(authHeader, "token=") && subtle.ConstantTimeCompare([]byte("token="+srv.authToken), []byte(authHeader)) == 1 {
		f(w, req)
		return
	}

	jsonapi.Error(w, errors.New("incorrect token"), http.StatusUnauthorized)
}

func (srv *server) handleInstancesList(w http.ResponseWriter, req *http.Request) {
	defer metrics.TimeSince("travis.jupiter-brain.endpoints.instances-list", time.Now())

	instances, err := srv.i.List(context.TODO())
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	dbInstanceIDs := []string{}
	dbInstanceIDCreatedMap := map[string]time.Time{}
	applyDBFilter := false

	if req.FormValue("min_age") != "" {
		dur, err := time.ParseDuration(req.FormValue("min_age"))
		if err != nil {
			jsonapi.Error(w, err, http.StatusBadRequest)
			return
		}

		res, err := srv.db.FetchInstances(&databaseQuery{MinAge: dur})
		if err != nil {
			jsonapi.Error(w, err, http.StatusBadRequest)
			return
		}

		srv.log.WithFields(logrus.Fields{
			"n": len(res),
		}).Debug("retrieved instances from database")

		for _, r := range res {
			dbInstanceIDCreatedMap[r.ID] = r.CreatedAt
			dbInstanceIDs = append(dbInstanceIDs, r.ID)
		}

		applyDBFilter = true
	}

	response := map[string][]interface{}{
		"data": make([]interface{}, 0),
	}

	if applyDBFilter {
		keptInstances := []*jupiterbrain.Instance{}
		for _, instance := range instances {
			for _, instID := range dbInstanceIDs {
				if instID == instance.ID {
					instance.CreatedAt = dbInstanceIDCreatedMap[instID]
					keptInstances = append(keptInstances, instance)
				}
			}
		}

		srv.log.WithFields(logrus.Fields{
			"pre_filter":  len(instances),
			"post_filter": len(keptInstances),
		}).Debug("applying known instance filter")

		instances = keptInstances
	}

	for _, instance := range instances {
		response["data"] = append(response["data"], MarshalInstance(instance))
	}

	b, err := json.MarshalIndent(response, "", "  ")
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, string(b)+"\n")
}

func (srv *server) handleInstancesCreate(w http.ResponseWriter, req *http.Request) {
	defer metrics.TimeSince("travis.jupiter-brain.endpoints.instances-create", time.Now())

	var requestBody map[string]map[string]string

	err := json.NewDecoder(req.Body).Decode(&requestBody)
	if err != nil {
		jsonapi.Error(w, err, http.StatusBadRequest)
		return
	}

	if requestBody["data"] == nil {
		jsonapi.Error(w, &jsonapi.JSONError{Status: "422", Code: "missing-field", Title: "root object must have data field"}, 422)
		return
	}

	if requestBody["data"]["type"] != "instances" {
		jsonapi.Error(w, &jsonapi.JSONError{Status: "409", Code: "incorrect-type", Title: "data must be of type instances"}, http.StatusConflict)
		return
	}

	if requestBody["data"]["base-image"] == "" {
		jsonapi.Error(w, &jsonapi.JSONError{Status: "422", Code: "missing-field", Title: "instance must have base-image field"}, 422)
		return
	}

	instance, err := srv.i.Start(context.TODO(), requestBody["data"]["base-image"])
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	recoverDelete := false
	defer func() {
		if recoverDelete && instance != nil {
			go func() {
				srv.s.StartRoutine()
				defer srv.s.FinishRoutine()
				srv.i.Terminate(context.TODO(), instance.ID)
			}()
		}
	}()

	instance.CreatedAt = time.Now().UTC()
	err = srv.db.SaveInstance(instance)
	if err != nil {
		recoverDelete = true
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	response := map[string][]interface{}{
		"data": {MarshalInstance(instance)},
	}

	b, err := json.MarshalIndent(response, "", "  ")
	if err != nil {
		recoverDelete = true
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.Header().Set("Location", fmt.Sprintf("/instances/%s", instance.ID))
	w.WriteHeader(http.StatusCreated)
	fmt.Fprintf(w, string(b)+"\n")
}

func (srv *server) handleInstanceByIDFetch(w http.ResponseWriter, req *http.Request) {
	defer metrics.TimeSince("travis.jupiter-brain.endpoints.instance-by-id-fetch", time.Now())

	vars := mux.Vars(req)
	instance, err := srv.i.Fetch(context.TODO(), vars["id"])
	if err != nil {
		switch err.(type) {
		case jupiterbrain.VirtualMachineNotFoundError:
			jsonapi.Error(w, err, http.StatusNotFound)
			return
		default:
			srv.log.WithFields(logrus.Fields{
				"err": err,
				"id":  vars["id"],
			}).Error("failed to fetch instance")
			jsonapi.Error(w, err, http.StatusInternalServerError)
			return
		}
	}

	response := map[string][]interface{}{
		"data": {MarshalInstance(instance)},
	}

	b, err := json.MarshalIndent(response, "", "  ")
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, string(b)+"\n")
}

func (srv *server) handleInstanceByIDTerminate(w http.ResponseWriter, req *http.Request) {
	defer metrics.TimeSince("travis.jupiter-brain.endpoints.instance-by-id-terminate", time.Now())

	vars := mux.Vars(req)
	err := srv.i.Terminate(context.TODO(), vars["id"])
	if err != nil {
		switch err.(type) {
		case jupiterbrain.VirtualMachineNotFoundError:
			jsonapi.Error(w, err, http.StatusNotFound)
			return
		default:
			srv.log.WithFields(logrus.Fields{
				"err": err,
				"id":  vars["id"],
			}).Error("failed to terminate instance")
			jsonapi.Error(w, err, http.StatusInternalServerError)
			return
		}
	}

	err = srv.db.DestroyInstance(vars["id"])
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (srv *server) handleInstanceSync(w http.ResponseWriter, req *http.Request) {
	defer metrics.TimeSince("travis.jupiter-brain.endpoints.instance-sync", time.Now())
	instances, err := srv.i.List(context.TODO())
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	for _, instance := range instances {
		instance.CreatedAt = time.Now().UTC()
		err = srv.db.SaveInstance(instance)
		if err != nil {
			srv.log.WithFields(logrus.Fields{
				"err": err,
				"id":  instance.ID,
			}).Warn("failed to save instance")
			continue
		}

		srv.log.WithField("id", instance.ID).Debug("synced instance")
	}

	w.WriteHeader(http.StatusNoContent)
}

func (srv *server) signalHandler() {
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGINT, syscall.SIGUSR1)
	for {
		select {
		case sig := <-signalChan:
			switch sig {
			case syscall.SIGTERM:
				srv.log.Info("Received SIGTERM, shutting down now.")
				srv.s.Close()
			case syscall.SIGINT:
				srv.log.Info("Received SIGINT, shutting down now.")
				srv.s.Close()
			case syscall.SIGUSR1:
				srv.log.WithFields(logrus.Fields{
					"version":   os.Getenv("VERSION"),
					"revision":  os.Getenv("REVISION"),
					"boot_time": srv.bootTime,
					"uptime":    time.Since(srv.bootTime),
				}).Info("Received SIGUSR1.")
			default:
				log.Print("ignoring unknown signal")
			}
		default:
			time.Sleep(time.Second)
		}
	}
}
<commit_msg>server: use the http request contexts instead of TODO<commit_after>package server

import (
	"context"
	"crypto/subtle"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/braintree/manners"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/meatballhat/negroni-logrus"
	"github.com/pkg/errors"
	"github.com/travis-ci/jupiter-brain"
	"github.com/travis-ci/jupiter-brain/metrics"
	"github.com/travis-ci/jupiter-brain/server/jsonapi"
	"github.com/travis-ci/jupiter-brain/server/negroniraven"
)

type server struct {
	addr, authToken, sentryDSN string

	log *logrus.Logger

	i jupiterbrain.InstanceManager

	n *negroni.Negroni
	r *mux.Router
	s *manners.GracefulServer

	db       database
	bootTime time.Time
}

func newServer(cfg *Config) (*server, error) {
	log := logrus.New()
	if cfg.Debug {
		log.Level = logrus.DebugLevel
	}

	log.Formatter = &logrus.TextFormatter{DisableColors: true}

	u, err := url.Parse(cfg.VSphereURL)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse vsphere url")
	}

	if !u.IsAbs() {
		return nil, errors.Errorf("vSphere API URL must be absolute")
	}

	db, err := newPGDatabase(cfg.DatabaseURL)
	if err != nil {
		return nil, errors.Wrap(err, "failed to create postgres database")
	}

	paths := jupiterbrain.VSpherePaths{
		BasePath:    cfg.VSphereBasePath,
		VMPath:      cfg.VSphereVMPath,
		ClusterPath: cfg.VSphereClusterPath,
	}

	srv := &server{
		addr:      cfg.Addr,
		authToken: cfg.AuthToken,
		sentryDSN: cfg.SentryDSN,

		log: log,

		i: jupiterbrain.NewVSphereInstanceManager(log, u, paths),

		n: negroni.New(),
		r: mux.NewRouter(),
		s: manners.NewServer(),

		db:       db,
		bootTime: time.Now().UTC(),
	}

	return srv, nil
}

func (srv *server) Setup() {
	srv.setupRoutes()
	srv.setupMiddleware()
	go srv.signalHandler()
}

func (srv *server) Run() {
	srv.log.WithField("addr", srv.addr).Info("Listening")
	srv.s.Addr = srv.addr
	srv.s.Handler = srv.n
	err := srv.s.ListenAndServe()
	if err != nil {
		srv.log.WithField("err", err).Error("ListenAndServe failed")
	}
}

func (srv *server) setupRoutes() {
	srv.r.HandleFunc(`/instances`, srv.handleInstancesList).Methods("GET").Name("instances-list")
	srv.r.HandleFunc(`/instances`, srv.handleInstancesCreate).Methods("POST").Name("instances-create")
	srv.r.HandleFunc(`/instances/{id}`, srv.handleInstanceByIDFetch).Methods("GET").Name("instance-by-id")
	srv.r.HandleFunc(`/instances/{id}`, srv.handleInstanceByIDTerminate).Methods("DELETE").Name("instance-by-id-terminate")
	srv.r.HandleFunc(`/instance-syncs`, srv.handleInstanceSync).Methods("POST").Name("instance-syncs-create")
}

func (srv *server) setupMiddleware() {
	srv.n.Use(negroni.NewRecovery())
	srv.n.Use(negronilogrus.NewCustomMiddleware(srv.log.Level, srv.log.Formatter, "web"))
	srv.n.Use(negroni.HandlerFunc(srv.authMiddleware))
	nr, err := negroniraven.NewMiddleware(srv.sentryDSN)
	if err != nil {
		panic(err)
	}
	srv.n.Use(nr)
	srv.n.UseHandler(srv.r)
}

func (srv *server) authMiddleware(w http.ResponseWriter, req *http.Request, f http.HandlerFunc) {
	authHeader := req.Header.Get("Authorization")
	srv.log.WithField("authorization", authHeader).Debug("raw authorization header")

	if authHeader == "" {
		w.Header().Set("WWW-Authenticate", "token")
		srv.log.WithField("request_id", req.Header.Get("X-Request-ID")).Debug("responding 401 due to empty Authorization header")

		jsonapi.Error(w, errors.New("token is required"), http.StatusUnauthorized)
		return
	}

	if strings.HasPrefix(authHeader, "token ") && subtle.ConstantTimeCompare([]byte("token "+srv.authToken), []byte(authHeader)) == 1 {
		f(w, req)
		return
	} else if strings.HasPrefix(authHeader, "token=") && subtle.ConstantTimeCompare([]byte("token="+srv.authToken), []byte(authHeader)) == 1 {
		f(w, req)
		return
	}

	jsonapi.Error(w, errors.New("incorrect token"), http.StatusUnauthorized)
}

func (srv *server) handleInstancesList(w http.ResponseWriter, req *http.Request) {
	defer metrics.TimeSince("travis.jupiter-brain.endpoints.instances-list", time.Now())

	instances, err := srv.i.List(req.Context())
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	dbInstanceIDs := []string{}
	dbInstanceIDCreatedMap := map[string]time.Time{}
	applyDBFilter := false

	if req.FormValue("min_age") != "" {
		dur, err := time.ParseDuration(req.FormValue("min_age"))
		if err != nil {
			jsonapi.Error(w, err, http.StatusBadRequest)
			return
		}

		res, err := srv.db.FetchInstances(&databaseQuery{MinAge: dur})
		if err != nil {
			jsonapi.Error(w, err, http.StatusBadRequest)
			return
		}

		srv.log.WithFields(logrus.Fields{
			"n": len(res),
		}).Debug("retrieved instances from database")

		for _, r := range res {
			dbInstanceIDCreatedMap[r.ID] = r.CreatedAt
			dbInstanceIDs = append(dbInstanceIDs, r.ID)
		}

		applyDBFilter = true
	}

	response := map[string][]interface{}{
		"data": make([]interface{}, 0),
	}

	if applyDBFilter {
		keptInstances := []*jupiterbrain.Instance{}
		for _, instance := range instances {
			for _, instID := range dbInstanceIDs {
				if instID == instance.ID {
					instance.CreatedAt = dbInstanceIDCreatedMap[instID]
					keptInstances = append(keptInstances, instance)
				}
			}
		}

		srv.log.WithFields(logrus.Fields{
			"pre_filter":  len(instances),
			"post_filter": len(keptInstances),
		}).Debug("applying known instance filter")

		instances = keptInstances
	}

	for _, instance := range instances {
		response["data"] = append(response["data"], MarshalInstance(instance))
	}

	b, err := json.MarshalIndent(response, "", "  ")
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, string(b)+"\n")
}

func (srv *server) handleInstancesCreate(w http.ResponseWriter, req *http.Request) {
	defer metrics.TimeSince("travis.jupiter-brain.endpoints.instances-create", time.Now())

	var requestBody map[string]map[string]string

	err := json.NewDecoder(req.Body).Decode(&requestBody)
	if err != nil {
		jsonapi.Error(w, err, http.StatusBadRequest)
		return
	}

	if requestBody["data"] == nil {
		jsonapi.Error(w, &jsonapi.JSONError{Status: "422", Code: "missing-field", Title: "root object must have data field"}, 422)
		return
	}

	if requestBody["data"]["type"] != "instances" {
		jsonapi.Error(w, &jsonapi.JSONError{Status: "409", Code: "incorrect-type", Title: "data must be of type instances"}, http.StatusConflict)
		return
	}

	if requestBody["data"]["base-image"] == "" {
		jsonapi.Error(w, &jsonapi.JSONError{Status: "422", Code: "missing-field", Title: "instance must have base-image field"}, 422)
		return
	}

	instance, err := srv.i.Start(req.Context(), requestBody["data"]["base-image"])
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	recoverDelete := false
	defer func() {
		if recoverDelete && instance != nil {
			go func() {
				srv.s.StartRoutine()
				defer srv.s.FinishRoutine()
				srv.i.Terminate(context.TODO(), instance.ID)
			}()
		}
	}()

	instance.CreatedAt = time.Now().UTC()
	err = srv.db.SaveInstance(instance)
	if err != nil {
		recoverDelete = true
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	response := map[string][]interface{}{
		"data": {MarshalInstance(instance)},
	}

	b, err := json.MarshalIndent(response, "", "  ")
	if err != nil {
		recoverDelete = true
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.Header().Set("Location", fmt.Sprintf("/instances/%s", instance.ID))
	w.WriteHeader(http.StatusCreated)
	fmt.Fprintf(w, string(b)+"\n")
}

func (srv *server) handleInstanceByIDFetch(w http.ResponseWriter, req *http.Request) {
	defer metrics.TimeSince("travis.jupiter-brain.endpoints.instance-by-id-fetch", time.Now())

	vars := mux.Vars(req)
	instance, err := srv.i.Fetch(req.Context(), vars["id"])
	if err != nil {
		switch err.(type) {
		case jupiterbrain.VirtualMachineNotFoundError:
			jsonapi.Error(w, err, http.StatusNotFound)
			return
		default:
			srv.log.WithFields(logrus.Fields{
				"err": err,
				"id":  vars["id"],
			}).Error("failed to fetch instance")
			jsonapi.Error(w, err, http.StatusInternalServerError)
			return
		}
	}

	response := map[string][]interface{}{
		"data": {MarshalInstance(instance)},
	}

	b, err := json.MarshalIndent(response, "", "  ")
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, string(b)+"\n")
}

func (srv *server) handleInstanceByIDTerminate(w http.ResponseWriter, req *http.Request) {
	defer metrics.TimeSince("travis.jupiter-brain.endpoints.instance-by-id-terminate", time.Now())

	vars := mux.Vars(req)
	err := srv.i.Terminate(req.Context(), vars["id"])
	if err != nil {
		switch err.(type) {
		case jupiterbrain.VirtualMachineNotFoundError:
			jsonapi.Error(w, err, http.StatusNotFound)
			return
		default:
			srv.log.WithFields(logrus.Fields{
				"err": err,
				"id":  vars["id"],
			}).Error("failed to terminate instance")
			jsonapi.Error(w, err, http.StatusInternalServerError)
			return
		}
	}

	err = srv.db.DestroyInstance(vars["id"])
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (srv *server) handleInstanceSync(w http.ResponseWriter, req *http.Request) {
	defer metrics.TimeSince("travis.jupiter-brain.endpoints.instance-sync", time.Now())
	instances, err := srv.i.List(req.Context())
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	for _, instance := range instances {
		instance.CreatedAt = time.Now().UTC()
		err = srv.db.SaveInstance(instance)
		if err != nil {
			srv.log.WithFields(logrus.Fields{
				"err": err,
				"id":  instance.ID,
			}).Warn("failed to save instance")
			continue
		}

		srv.log.WithField("id", instance.ID).Debug("synced instance")
	}

	w.WriteHeader(http.StatusNoContent)
}

func (srv *server) signalHandler() {
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGINT, syscall.SIGUSR1)
	for {
		select {
		case sig := <-signalChan:
			switch sig {
			case syscall.SIGTERM:
				srv.log.Info("Received SIGTERM, shutting down now.")
				srv.s.Close()
			case syscall.SIGINT:
				srv.log.Info("Received SIGINT, shutting down now.")
				srv.s.Close()
			case syscall.SIGUSR1:
				srv.log.WithFields(logrus.Fields{
					"version":   os.Getenv("VERSION"),
					"revision":  os.Getenv("REVISION"),
					"boot_time": srv.bootTime,
					"uptime":    time.Since(srv.bootTime),
				}).Info("Received SIGUSR1.")
			default:
				log.Print("ignoring unknown signal")
			}
		default:
			time.Sleep(time.Second)
		}
	}
}
<|endoftext|>


<commit_before>package server

import (
	iniconf "code.google.com/p/goconf/conf"
	"fmt"
	"strings"
	"time"
)

type BotHandler interface {
	DispatchRequest(botRequest *BotRequest)
}

func (bhs *BotHandlerService) CreateBotResponse(botRequest *BotRequest) *BotResponse {
	return &BotResponse{Target: botRequest.Channel, Identifier: botRequest.Identifier}
}

func (bhs *BotHandlerService) PublishBotResponse(botResponse *BotResponse) {
	bhs.PublishToIRCChan <- botResponse
}

type BotHandlerService struct {
	PublishToIRCChan chan *BotResponse
	Config           *iniconf.ConfigFile
}

const (
	PRIVMSG = "PRIVMSG"
	NOTICE  = "NOTICE"
	ACTION  = "ACTION"
)

type BotRequest struct {
	// raw data struct (from the bot3)
	Identifier string
	Nick       string
	Channel    string
	ChatText   string
}

func (req *BotRequest) Text() string {
	return req.ChatText
}

type BotResponse struct {
	Identifier   string
	ResponseType string
	Target       string
	Response     []string
}

type Bot3ServerHeartbeat struct {
	ServerID  string
	Timestamp time.Time
}

func (response *BotResponse) IsMultiLineResponse() bool {

	if len(response.Response) > 1 {
		return true
	} else {
		return false
	}
}

func (response *BotResponse) SingleLineResponse() string {

	return response.Response[0]
}

func (response *BotResponse) SetSingleLineResponse(rstr string) {

	responseArr := []string{rstr}
	response.Response = responseArr
}

func (response *BotResponse) SetMultipleLineResponse(rstr []string) {

	response.Response = rstr
}

func (request *BotRequest) RequestIsCommand() bool {

	return stringIsCommand(request.Text())
}

func (request *BotRequest) Command() string {

	return getCommandFromString(request.Text())
}

func (request *BotRequest) LineTextWithoutCommand() string {

	if request.RequestIsCommand() {
		lineTxt := strings.TrimPrefix(request.Text(), fmt.Sprintf("!%s", request.Command()))
		return strings.TrimSpace(lineTxt)
	} else {
		return request.Text()
	}
}

func stringIsCommand(rawstring string) bool {

	if strings.HasPrefix(rawstring, "!") {
		return true
	} else {
		return false
	}
}

func getCommandFromString(rawstring string) string {

	var commandStr = ""

	if stringIsCommand(rawstring) {
		index := strings.Index(rawstring, " ")
		if index > 0 {
			commandStr = rawstring[1:index]
		} else if index == -1 {
			// no space, so return entirety of string except first char
			commandStr = rawstring[1:len(rawstring)]
		}
	}

	return commandStr
}
<commit_msg>refactored string operations to util lib<commit_after>package server

import (
	iniconf "code.google.com/p/goconf/conf"
	"github.com/gamelost/bot3server/util"
	"time"
)

type BotHandler interface {
	DispatchRequest(botRequest *BotRequest)
}

func (bhs *BotHandlerService) CreateBotResponse(botRequest *BotRequest) *BotResponse {
	return &BotResponse{Target: botRequest.Channel, Identifier: botRequest.Identifier}
}

func (bhs *BotHandlerService) PublishBotResponse(botResponse *BotResponse) {
	bhs.PublishToIRCChan <- botResponse
}

type BotHandlerService struct {
	PublishToIRCChan chan *BotResponse
	Config           *iniconf.ConfigFile
}

const (
	PRIVMSG = "PRIVMSG"
	NOTICE  = "NOTICE"
	ACTION  = "ACTION"
)

type BotRequest struct {
	// raw data struct (from the bot3)
	Identifier string
	Nick       string
	Channel    string
	ChatText   string
}

func (req *BotRequest) Text() string {
	return req.ChatText
}

type BotResponse struct {
	Identifier   string
	ResponseType string
	Target       string
	Response     []string
}

type Bot3ServerHeartbeat struct {
	ServerID  string
	Timestamp time.Time
}

func (response *BotResponse) IsMultiLineResponse() bool {

	if len(response.Response) > 1 {
		return true
	} else {
		return false
	}
}

func (response *BotResponse) SingleLineResponse() string {

	return response.Response[0]
}

func (response *BotResponse) SetSingleLineResponse(rstr string) {

	responseArr := []string{rstr}
	response.Response = responseArr
}

func (response *BotResponse) SetMultipleLineResponse(rstr []string) {

	response.Response = rstr
}

func (request *BotRequest) RequestIsCommand() bool {

	return util.StringIsCommand(request.Text())
}

func (request *BotRequest) Command() string {

	return util.GetCommandFromString(request.Text())
}

func (request *BotRequest) LineTextWithoutCommand() string {

	if request.RequestIsCommand() {
		return util.TrimCommandFromString(request.Text(), request.Command())
	} else {
		return request.Text()
	}
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/registrar"
	"github.com/docker/docker/pkg/truncindex"
	"github.com/kubernetes-incubator/cri-o/oci"
	"github.com/kubernetes-incubator/cri-o/utils"
	"github.com/opencontainers/runc/libcontainer/label"
	rspec "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/rajatchopra/ocicni"
)

const (
	runtimeAPIVersion = "v1alpha1"
	imageStore        = "/var/lib/ocid/images"
)

// Server implements the RuntimeService and ImageService
type Server struct {
	root         string
	runtime      *oci.Runtime
	sandboxDir   string
	pausePath    string
	stateLock    sync.Mutex
	state        *serverState
	netPlugin    ocicni.CNIPlugin
	podNameIndex *registrar.Registrar
	podIDIndex   *truncindex.TruncIndex
	ctrNameIndex *registrar.Registrar
	ctrIDIndex   *truncindex.TruncIndex
}

func (s *Server) loadContainer(id string) error {
	config, err := ioutil.ReadFile(filepath.Join(s.runtime.ContainerDir(), id, "config.json"))
	if err != nil {
		return err
	}
	var m rspec.Spec
	if err = json.Unmarshal(config, &m); err != nil {
		return err
	}
	labels := make(map[string]string)
	if err = json.Unmarshal([]byte(m.Annotations["ocid/labels"]), &labels); err != nil {
		return err
	}
	name := m.Annotations["ocid/name"]
	name, err = s.reserveContainerName(id, name)
	if err != nil {
		return err
	}
	sb := s.getSandbox(m.Annotations["ocid/sandbox_id"])
	if sb == nil {
		logrus.Warnf("could not get sandbox with id %s, skipping", m.Annotations["ocid/sandbox_id"])
	}

	var tty bool
	if v := m.Annotations["ocid/tty"]; v == "true" {
		tty = true
	}
	containerPath := filepath.Join(s.runtime.ContainerDir(), id)

	ctr, err := oci.NewContainer(id, name, containerPath, m.Annotations["ocid/log_path"], labels, sb.id, tty)
	if err != nil {
		return err
	}
	s.addContainer(ctr)
	if err = s.runtime.UpdateStatus(ctr); err != nil {
		logrus.Warnf("error updating status for container %s: %v", ctr.ID(), err)
	}
	if err = s.ctrIDIndex.Add(id); err != nil {
		return err
	}
	return nil
}

func (s *Server) loadSandbox(id string) error {
	config, err := ioutil.ReadFile(filepath.Join(s.sandboxDir, id, "config.json"))
	if err != nil {
		return err
	}
	var m rspec.Spec
	if err = json.Unmarshal(config, &m); err != nil {
		return err
	}
	labels := make(map[string]string)
	if err = json.Unmarshal([]byte(m.Annotations["ocid/labels"]), &labels); err != nil {
		return err
	}
	name := m.Annotations["ocid/name"]
	name, err = s.reservePodName(id, name)
	if err != nil {
		return err
	}

	processLabel, mountLabel, err := label.InitLabels(label.DupSecOpt(m.Process.SelinuxLabel))
	if err != nil {
		return err
	}
	s.addSandbox(&sandbox{
		id:           id,
		name:         name,
		logDir:       m.Annotations["ocid/log_path"],
		labels:       labels,
		containers:   oci.NewMemoryStore(),
		processLabel: processLabel,
		mountLabel:   mountLabel,
	})
	sandboxPath := filepath.Join(s.sandboxDir, id)

	if err := label.ReserveLabel(processLabel); err != nil {
		return err
	}

	cname, err := s.reserveContainerName(m.Annotations["ocid/container_id"], m.Annotations["ocid/container_name"])
	if err != nil {
		return err
	}
	scontainer, err := oci.NewContainer(m.Annotations["ocid/container_id"], cname, sandboxPath, sandboxPath, labels, id, false)
	if err != nil {
		return err
	}
	s.addContainer(scontainer)
	if err = s.runtime.UpdateStatus(scontainer); err != nil {
		logrus.Warnf("error updating status for container %s: %v", scontainer.ID(), err)
	}
	if err = s.ctrIDIndex.Add(scontainer.ID()); err != nil {
		return err
	}
	if err = s.podIDIndex.Add(id); err != nil {
		return err
	}
	return nil
}

func (s *Server) restore() {
	sandboxDir, err := ioutil.ReadDir(s.sandboxDir)
	if err != nil && !os.IsNotExist(err) {
		logrus.Warnf("could not read sandbox directory %s: %v", sandboxDir, err)
	}
	for _, v := range sandboxDir {
		if !v.IsDir() {
			continue
		}
		if err = s.loadSandbox(v.Name()); err != nil {
			logrus.Warnf("could not restore sandbox %s: %v", v.Name(), err)
		}
	}
	containerDir, err := ioutil.ReadDir(s.runtime.ContainerDir())
	if err != nil && !os.IsNotExist(err) {
		logrus.Warnf("could not read container directory %s: %v", s.runtime.ContainerDir(), err)
	}
	for _, v := range containerDir {
		if !v.IsDir() {
			continue
		}
		if err := s.loadContainer(v.Name()); err != nil {
			logrus.Warnf("could not restore container %s: %v", v.Name(), err)

		}
	}
}

func (s *Server) reservePodName(id, name string) (string, error) {
	if err := s.podNameIndex.Reserve(name, id); err != nil {
		if err == registrar.ErrNameReserved {
			id, err := s.podNameIndex.Get(name)
			if err != nil {
				logrus.Warnf("name %s already reserved for %s", name, id)
				return "", err
			}
			return "", fmt.Errorf("conflict, name %s already reserved", name)
		}
		return "", fmt.Errorf("error reserving name %s", name)
	}
	return name, nil
}

func (s *Server) releasePodName(name string) {
	s.podNameIndex.Release(name)
}

func (s *Server) reserveContainerName(id, name string) (string, error) {
	if err := s.ctrNameIndex.Reserve(name, id); err != nil {
		if err == registrar.ErrNameReserved {
			id, err := s.ctrNameIndex.Get(name)
			if err != nil {
				logrus.Warnf("get reserved name %s failed", name)
				return "", err
			}
			return "", fmt.Errorf("conflict, name %s already reserved for %s", name, id)
		}
		return "", fmt.Errorf("error reserving name %s", name)
	}
	return name, nil
}

func (s *Server) releaseContainerName(name string) {
	s.ctrNameIndex.Release(name)
}

// New creates a new Server with options provided
func New(runtimePath, root, sandboxDir, containerDir, conmonPath, pausePath string) (*Server, error) {
	// TODO: This will go away later when we have wrapper process or systemd acting as
	// subreaper.
	if err := utils.SetSubreaper(1); err != nil {
		return nil, fmt.Errorf("failed to set server as subreaper: %v", err)
	}

	utils.StartReaper()

	if err := os.MkdirAll(imageStore, 0755); err != nil {
		return nil, err
	}

	if err := os.MkdirAll(sandboxDir, 0755); err != nil {
		return nil, err
	}

	r, err := oci.New(runtimePath, containerDir, conmonPath)
	if err != nil {
		return nil, err
	}
	sandboxes := make(map[string]*sandbox)
	containers := oci.NewMemoryStore()
	netPlugin, err := ocicni.InitCNI("")
	if err != nil {
		return nil, err
	}
	s := &Server{
		root:       root,
		runtime:    r,
		netPlugin:  netPlugin,
		sandboxDir: sandboxDir,
		pausePath:  pausePath,
		state: &serverState{
			sandboxes:  sandboxes,
			containers: containers,
		},
	}

	s.podIDIndex = truncindex.NewTruncIndex([]string{})
	s.podNameIndex = registrar.NewRegistrar()
	s.ctrIDIndex = truncindex.NewTruncIndex([]string{})
	s.ctrNameIndex = registrar.NewRegistrar()

	s.restore()

	logrus.Debugf("sandboxes: %v", s.state.sandboxes)
	logrus.Debugf("containers: %v", s.state.containers)
	return s, nil
}

type serverState struct {
	sandboxes  map[string]*sandbox
	containers oci.Store
}

func (s *Server) addSandbox(sb *sandbox) {
	s.stateLock.Lock()
	s.state.sandboxes[sb.id] = sb
	s.stateLock.Unlock()
}

func (s *Server) getSandbox(id string) *sandbox {
	s.stateLock.Lock()
	sb := s.state.sandboxes[id]
	s.stateLock.Unlock()
	return sb
}

func (s *Server) hasSandbox(id string) bool {
	s.stateLock.Lock()
	_, ok := s.state.sandboxes[id]
	s.stateLock.Unlock()
	return ok
}

func (s *Server) removeSandbox(id string) {
	s.stateLock.Lock()
	delete(s.state.sandboxes, id)
	s.stateLock.Unlock()
}

func (s *Server) addContainer(c *oci.Container) {
	s.stateLock.Lock()
	sandbox := s.state.sandboxes[c.Sandbox()]
	// TODO(runcom): handle !ok above!!! otherwise it panics!
	sandbox.addContainer(c)
	s.state.containers.Add(c.ID(), c)
	s.stateLock.Unlock()
}

func (s *Server) getContainer(id string) *oci.Container {
	s.stateLock.Lock()
	c := s.state.containers.Get(id)
	s.stateLock.Unlock()
	return c
}

func (s *Server) removeContainer(c *oci.Container) {
	s.stateLock.Lock()
	sandbox := s.state.sandboxes[c.Sandbox()]
	sandbox.removeContainer(c)
	s.state.containers.Delete(c.ID())
	s.stateLock.Unlock()
}
<commit_msg>server/server: skip f no sandbox when restoring containers<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/registrar"
	"github.com/docker/docker/pkg/truncindex"
	"github.com/kubernetes-incubator/cri-o/oci"
	"github.com/kubernetes-incubator/cri-o/utils"
	"github.com/opencontainers/runc/libcontainer/label"
	rspec "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/rajatchopra/ocicni"
)

const (
	runtimeAPIVersion = "v1alpha1"
	imageStore        = "/var/lib/ocid/images"
)

// Server implements the RuntimeService and ImageService
type Server struct {
	root         string
	runtime      *oci.Runtime
	sandboxDir   string
	pausePath    string
	stateLock    sync.Mutex
	state        *serverState
	netPlugin    ocicni.CNIPlugin
	podNameIndex *registrar.Registrar
	podIDIndex   *truncindex.TruncIndex
	ctrNameIndex *registrar.Registrar
	ctrIDIndex   *truncindex.TruncIndex
}

func (s *Server) loadContainer(id string) error {
	config, err := ioutil.ReadFile(filepath.Join(s.runtime.ContainerDir(), id, "config.json"))
	if err != nil {
		return err
	}
	var m rspec.Spec
	if err = json.Unmarshal(config, &m); err != nil {
		return err
	}
	labels := make(map[string]string)
	if err = json.Unmarshal([]byte(m.Annotations["ocid/labels"]), &labels); err != nil {
		return err
	}
	name := m.Annotations["ocid/name"]
	name, err = s.reserveContainerName(id, name)
	if err != nil {
		return err
	}
	sb := s.getSandbox(m.Annotations["ocid/sandbox_id"])
	if sb == nil {
		logrus.Warnf("could not get sandbox with id %s, skipping", m.Annotations["ocid/sandbox_id"])
		return nil
	}

	var tty bool
	if v := m.Annotations["ocid/tty"]; v == "true" {
		tty = true
	}
	containerPath := filepath.Join(s.runtime.ContainerDir(), id)

	ctr, err := oci.NewContainer(id, name, containerPath, m.Annotations["ocid/log_path"], labels, sb.id, tty)
	if err != nil {
		return err
	}
	s.addContainer(ctr)
	if err = s.runtime.UpdateStatus(ctr); err != nil {
		logrus.Warnf("error updating status for container %s: %v", ctr.ID(), err)
	}
	if err = s.ctrIDIndex.Add(id); err != nil {
		return err
	}
	return nil
}

func (s *Server) loadSandbox(id string) error {
	config, err := ioutil.ReadFile(filepath.Join(s.sandboxDir, id, "config.json"))
	if err != nil {
		return err
	}
	var m rspec.Spec
	if err = json.Unmarshal(config, &m); err != nil {
		return err
	}
	labels := make(map[string]string)
	if err = json.Unmarshal([]byte(m.Annotations["ocid/labels"]), &labels); err != nil {
		return err
	}
	name := m.Annotations["ocid/name"]
	name, err = s.reservePodName(id, name)
	if err != nil {
		return err
	}

	processLabel, mountLabel, err := label.InitLabels(label.DupSecOpt(m.Process.SelinuxLabel))
	if err != nil {
		return err
	}
	s.addSandbox(&sandbox{
		id:           id,
		name:         name,
		logDir:       m.Annotations["ocid/log_path"],
		labels:       labels,
		containers:   oci.NewMemoryStore(),
		processLabel: processLabel,
		mountLabel:   mountLabel,
	})
	sandboxPath := filepath.Join(s.sandboxDir, id)

	if err := label.ReserveLabel(processLabel); err != nil {
		return err
	}

	cname, err := s.reserveContainerName(m.Annotations["ocid/container_id"], m.Annotations["ocid/container_name"])
	if err != nil {
		return err
	}
	scontainer, err := oci.NewContainer(m.Annotations["ocid/container_id"], cname, sandboxPath, sandboxPath, labels, id, false)
	if err != nil {
		return err
	}
	s.addContainer(scontainer)
	if err = s.runtime.UpdateStatus(scontainer); err != nil {
		logrus.Warnf("error updating status for container %s: %v", scontainer.ID(), err)
	}
	if err = s.ctrIDIndex.Add(scontainer.ID()); err != nil {
		return err
	}
	if err = s.podIDIndex.Add(id); err != nil {
		return err
	}
	return nil
}

func (s *Server) restore() {
	sandboxDir, err := ioutil.ReadDir(s.sandboxDir)
	if err != nil && !os.IsNotExist(err) {
		logrus.Warnf("could not read sandbox directory %s: %v", sandboxDir, err)
	}
	for _, v := range sandboxDir {
		if !v.IsDir() {
			continue
		}
		if err = s.loadSandbox(v.Name()); err != nil {
			logrus.Warnf("could not restore sandbox %s: %v", v.Name(), err)
		}
	}
	containerDir, err := ioutil.ReadDir(s.runtime.ContainerDir())
	if err != nil && !os.IsNotExist(err) {
		logrus.Warnf("could not read container directory %s: %v", s.runtime.ContainerDir(), err)
	}
	for _, v := range containerDir {
		if !v.IsDir() {
			continue
		}
		if err := s.loadContainer(v.Name()); err != nil {
			logrus.Warnf("could not restore container %s: %v", v.Name(), err)

		}
	}
}

func (s *Server) reservePodName(id, name string) (string, error) {
	if err := s.podNameIndex.Reserve(name, id); err != nil {
		if err == registrar.ErrNameReserved {
			id, err := s.podNameIndex.Get(name)
			if err != nil {
				logrus.Warnf("name %s already reserved for %s", name, id)
				return "", err
			}
			return "", fmt.Errorf("conflict, name %s already reserved", name)
		}
		return "", fmt.Errorf("error reserving name %s", name)
	}
	return name, nil
}

func (s *Server) releasePodName(name string) {
	s.podNameIndex.Release(name)
}

func (s *Server) reserveContainerName(id, name string) (string, error) {
	if err := s.ctrNameIndex.Reserve(name, id); err != nil {
		if err == registrar.ErrNameReserved {
			id, err := s.ctrNameIndex.Get(name)
			if err != nil {
				logrus.Warnf("get reserved name %s failed", name)
				return "", err
			}
			return "", fmt.Errorf("conflict, name %s already reserved for %s", name, id)
		}
		return "", fmt.Errorf("error reserving name %s", name)
	}
	return name, nil
}

func (s *Server) releaseContainerName(name string) {
	s.ctrNameIndex.Release(name)
}

// New creates a new Server with options provided
func New(runtimePath, root, sandboxDir, containerDir, conmonPath, pausePath string) (*Server, error) {
	// TODO: This will go away later when we have wrapper process or systemd acting as
	// subreaper.
	if err := utils.SetSubreaper(1); err != nil {
		return nil, fmt.Errorf("failed to set server as subreaper: %v", err)
	}

	utils.StartReaper()

	if err := os.MkdirAll(imageStore, 0755); err != nil {
		return nil, err
	}

	if err := os.MkdirAll(sandboxDir, 0755); err != nil {
		return nil, err
	}

	r, err := oci.New(runtimePath, containerDir, conmonPath)
	if err != nil {
		return nil, err
	}
	sandboxes := make(map[string]*sandbox)
	containers := oci.NewMemoryStore()
	netPlugin, err := ocicni.InitCNI("")
	if err != nil {
		return nil, err
	}
	s := &Server{
		root:       root,
		runtime:    r,
		netPlugin:  netPlugin,
		sandboxDir: sandboxDir,
		pausePath:  pausePath,
		state: &serverState{
			sandboxes:  sandboxes,
			containers: containers,
		},
	}

	s.podIDIndex = truncindex.NewTruncIndex([]string{})
	s.podNameIndex = registrar.NewRegistrar()
	s.ctrIDIndex = truncindex.NewTruncIndex([]string{})
	s.ctrNameIndex = registrar.NewRegistrar()

	s.restore()

	logrus.Debugf("sandboxes: %v", s.state.sandboxes)
	logrus.Debugf("containers: %v", s.state.containers)
	return s, nil
}

type serverState struct {
	sandboxes  map[string]*sandbox
	containers oci.Store
}

func (s *Server) addSandbox(sb *sandbox) {
	s.stateLock.Lock()
	s.state.sandboxes[sb.id] = sb
	s.stateLock.Unlock()
}

func (s *Server) getSandbox(id string) *sandbox {
	s.stateLock.Lock()
	sb := s.state.sandboxes[id]
	s.stateLock.Unlock()
	return sb
}

func (s *Server) hasSandbox(id string) bool {
	s.stateLock.Lock()
	_, ok := s.state.sandboxes[id]
	s.stateLock.Unlock()
	return ok
}

func (s *Server) removeSandbox(id string) {
	s.stateLock.Lock()
	delete(s.state.sandboxes, id)
	s.stateLock.Unlock()
}

func (s *Server) addContainer(c *oci.Container) {
	s.stateLock.Lock()
	sandbox := s.state.sandboxes[c.Sandbox()]
	// TODO(runcom): handle !ok above!!! otherwise it panics!
	sandbox.addContainer(c)
	s.state.containers.Add(c.ID(), c)
	s.stateLock.Unlock()
}

func (s *Server) getContainer(id string) *oci.Container {
	s.stateLock.Lock()
	c := s.state.containers.Get(id)
	s.stateLock.Unlock()
	return c
}

func (s *Server) removeContainer(c *oci.Container) {
	s.stateLock.Lock()
	sandbox := s.state.sandboxes[c.Sandbox()]
	sandbox.removeContainer(c)
	s.state.containers.Delete(c.ID())
	s.stateLock.Unlock()
}
<|endoftext|>


<commit_before>package main

import (
	"crypto"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
)

var listenAddress = "0.0.0.0:8043"
var privateKeyPath = "server.key"
var certChainPath = "server.crt"
var caBundlePath = "ca-bundle.crt"

func panicOnError(err error) {
	if err != nil {
		panic(err)
	}
}

func parseCertificates(data []byte) (certs [][]byte, err error) {
	for {
		var block *pem.Block
		block, data = pem.Decode(data)
		if block == nil {
			break
		}

		_, err = x509.ParseCertificate(block.Bytes)
		if err != nil {
			return
		}

		certs = append(certs, block.Bytes)
	}

	return
}

func parsePrivateKey(data []byte) (key crypto.PrivateKey, err error) {
	var block *pem.Block
	block, _ = pem.Decode(data)
	if block == nil {
		err = errors.New("invalid private key pem")
		return
	}

	key, err = x509.ParsePKCS1PrivateKey(block.Bytes)
	return
}

func main() {
	caBundleBytes, err := ioutil.ReadFile(caBundlePath)
	panicOnError(err)

	caBundle := x509.NewCertPool()
	caBundle.AppendCertsFromPEM(caBundleBytes)

	privateKeyBytes, err := ioutil.ReadFile(privateKeyPath)
	panicOnError(err)

	privateKey, err := parsePrivateKey(privateKeyBytes)
	panicOnError(err)

	certChainBytes, err := ioutil.ReadFile(certChainPath)
	panicOnError(err)

	certChain, err := parseCertificates(certChainBytes)
	panicOnError(err)

	certAndKey := []tls.Certificate{
		tls.Certificate{
			Certificate: certChain,
			PrivateKey:  privateKey,
		},
	}

	config := tls.Config{
		// Certificates
		Certificates: certAndKey,
		RootCAs:      caBundle,
		ClientCAs:    caBundle,

		// Options
		ClientAuth: tls.RequireAndVerifyClientCert,
		MinVersion: tls.VersionTLS12,
	}

	listener, err := tls.Listen("tcp", listenAddress, &config)
	panicOnError(err)

	log.Printf("Listening on %s", listenAddress)

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Printf("Error accepting connection: %s", err)
			continue
		}
		go handle(conn)
	}
}

func handle(conn net.Conn) {
	log.Printf("New connection from %s", conn.RemoteAddr())

	defer conn.Close()
	n, err := io.Copy(os.Stdout, conn)

	if err == nil {
		log.Printf("Closed connection from %s (success, copied %d bytes total)", conn.RemoteAddr(), n)
	} else {
		log.Printf("Closed connection from %s (%s)", conn.RemoteAddr(), err)
	}
}
<commit_msg>Use SO_REUSEPORT to support multiple running instances<commit_after>package main

import (
	"crypto"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"runtime"

	"github.com/kavu/go_reuseport"
)

var listenAddress = "127.0.0.1:8043"
var privateKeyPath = "server.key"
var certChainPath = "server.crt"
var caBundlePath = "ca-bundle.crt"

func panicOnError(err error) {
	if err != nil {
		panic(err)
	}
}

func parseCertificates(data []byte) (certs [][]byte, err error) {
	for {
		var block *pem.Block
		block, data = pem.Decode(data)
		if block == nil {
			break
		}

		_, err = x509.ParseCertificate(block.Bytes)
		if err != nil {
			return
		}

		certs = append(certs, block.Bytes)
	}

	return
}

func parsePrivateKey(data []byte) (key crypto.PrivateKey, err error) {
	var block *pem.Block
	block, _ = pem.Decode(data)
	if block == nil {
		err = errors.New("invalid private key pem")
		return
	}

	key, err = x509.ParsePKCS1PrivateKey(block.Bytes)
	return
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	caBundleBytes, err := ioutil.ReadFile(caBundlePath)
	panicOnError(err)

	caBundle := x509.NewCertPool()
	caBundle.AppendCertsFromPEM(caBundleBytes)

	privateKeyBytes, err := ioutil.ReadFile(privateKeyPath)
	panicOnError(err)

	privateKey, err := parsePrivateKey(privateKeyBytes)
	panicOnError(err)

	certChainBytes, err := ioutil.ReadFile(certChainPath)
	panicOnError(err)

	certChain, err := parseCertificates(certChainBytes)
	panicOnError(err)

	certAndKey := []tls.Certificate{
		tls.Certificate{
			Certificate: certChain,
			PrivateKey:  privateKey,
		},
	}

	config := tls.Config{
		// Certificates
		Certificates: certAndKey,
		RootCAs:      caBundle,
		ClientCAs:    caBundle,

		// Options
		ClientAuth: tls.RequireAndVerifyClientCert,
		MinVersion: tls.VersionTLS12,
	}

	rawListener, err := reuseport.NewReusablePortListener("tcp4", listenAddress)
	panicOnError(err)

	listener := tls.NewListener(rawListener, &config)

	log.Printf("Listening on %s", listenAddress)

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Printf("Error accepting connection: %s", err)
			continue
		}
		go handle(conn)
	}
}

func handle(conn net.Conn) {
	log.Printf("New connection from %s", conn.RemoteAddr())

	defer conn.Close()
	n, err := io.Copy(os.Stdout, conn)

	if err == nil {
		log.Printf("Closed connection from %s (success, copied %d bytes total)", conn.RemoteAddr(), n)
	} else {
		log.Printf("Closed connection from %s (%s)", conn.RemoteAddr(), err)
	}
}
<|endoftext|>