<commit_before>package mtutils

import (
	"runtime"
	"sync"
)

// template type Monitor(A)
type A int

type MonitoredObj A
type AccessFunc func(MonitoredObj)

type Monitor struct {
	sync.Mutex
	obj MonitoredObj
}

func NewMonitor(obj MonitoredObj) *Monitor {
	ret := new(Monitor)
	ret.obj = obj
	runtime.SetFinalizer(ret, func(m *Monitor) {
		m.Unlock()
	})
	return ret
}

func (m *Monitor) Capture() MonitoredObj {
	m.Lock()
	return m.obj
}

func (m *Monitor) Release() {
	m.Unlock()
}

func (m *Monitor) Access(f AccessFunc) {
	m.Lock()
	defer m.Unlock()
	f(m.obj)
}
<commit_msg>simplify monitor<commit_after>package mtutils

import (
	"runtime"
	"sync"
)

// template type Monitor(A)
type A int

type AccessFunc func(A)

type Monitor struct {
	sync.Mutex
	obj A
}

func NewMonitor(obj A) *Monitor {
	ret := new(Monitor)
	ret.obj = obj
	runtime.SetFinalizer(ret, func(m *Monitor) {
		m.Unlock()
	})
	return ret
}

func (m *Monitor) Capture() A {
	m.Lock()
	return m.obj
}

func (m *Monitor) Release() {
	m.Unlock()
}

func (m *Monitor) Access(f AccessFunc) {
	m.Lock()
	defer m.Unlock()
	f(m.obj)
}
<|endoftext|>


<commit_before>package envoy

// BootstrapTplArgs is the set of arguments that may be interpolated into the
// Envoy bootstrap template.
type BootstrapTplArgs struct {
	GRPC

	// ProxyCluster is the cluster name for the the Envoy `node` specification and
	// is typically the same as the ProxyID.
	ProxyCluster string

	// ProxyID is the ID of the proxy service instance as registered with the
	// local Consul agent. This must be used as the Envoy `node.id` in order for
	// the agent to deliver the correct configuration.
	ProxyID string

	// NodeName is the name of the node on which the proxy service instance is registered.
	NodeName string

	// ProxySourceService is the Consul service name to report for this proxy
	// instance's source service label. For sidecars it should be the
	// Proxy.DestinationServiceName. For gateways and similar it is the service
	// name of the proxy service itself.
	ProxySourceService string

	// AgentCAPEM is the CA to use to verify the local agent gRPC service if
	// TLS is enabled.
	AgentCAPEM string

	// AdminAccessLogPath The path to write the access log for the
	// administration server. If no access log is desired specify
	// "/dev/null". By default it will use "/dev/null".
	AdminAccessLogPath string

	// AdminBindAddress is the address the Envoy admin server should bind to.
	AdminBindAddress string

	// AdminBindPort is the port the Envoy admin server should bind to.
	AdminBindPort string

	// LocalAgentClusterName is the name reserved for the local Consul agent gRPC
	// service and is expected to be used for that purpose.
	LocalAgentClusterName string

	// Token is the Consul ACL token provided which is required to make gRPC
	// discovery requests. If non-empty, this must be configured as the gRPC
	// service "initial_metadata" with the key "x-consul-token" in order to
	// authorize the discovery streaming RPCs.
	Token string

	// StaticClustersJSON is JSON string, each is expected to be a valid Cluster
	// definition. They are appended to the "static_resources.clusters" list. Note
	// that cluster names should be chosen in such a way that they won't collide
	// with service names since we use plain service names as cluster names in xDS
	// to make metrics population simpler and cluster names mush be unique. See
	// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/api/v2/cds.proto.
	StaticClustersJSON string

	// StaticListenersJSON is a JSON string containing zero or more Listener
	// definitions. They are appended to the "static_resources.listeners" list. A
	// single listener should be given as a plain object, if more than one is to
	// be added, they should be separated by a comma suitable for direct injection
	// into a JSON array.
	// See https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/api/v2/lds.proto.
	StaticListenersJSON string

	// StatsSinksJSON is a JSON string containing an array in the right format
	// to be rendered as the body of the `stats_sinks` field at the top level of
	// the bootstrap config. It's format may vary based on Envoy version used. See
	// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/metrics/v2/stats.proto#config-metrics-v2-statssink.
	StatsSinksJSON string

	// StatsConfigJSON is a JSON string containing an object in the right format
	// to be rendered as the body of the `stats_config` field at the top level of
	// the bootstrap config. It's format may vary based on Envoy version used. See
	// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/metrics/v2/stats.proto#envoy-api-msg-config-metrics-v2-statsconfig.
	StatsConfigJSON string

	// StaticSecretsJSON is a JSON string containing zero or more Secret definitions.
	// See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/transport_sockets/tls/v3/secret.proto#envoy-v3-api-msg-extensions-transport-sockets-tls-v3-secret
	StaticSecretsJSON string

	// StatsFlushInterval is the time duration between Envoy stats flushes. It is
	// in proto3 "duration" string format for example "1.12s" See
	// https://developers.google.com/protocol-buffers/docs/proto3#json and
	// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/bootstrap/v2/bootstrap.proto#bootstrap
	StatsFlushInterval string

	// TracingConfigJSON is a JSON string containing an object in the right format
	// to be rendered as the body of the `tracing` field at the top level of
	// the bootstrap config. It's format may vary based on Envoy version used.
	// See https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/trace/v2/trace.proto.
	TracingConfigJSON string

	// Namespace is the Consul Enterprise Namespace of the proxy service instance
	// as registered with the Consul agent.
	Namespace string

	// Partition is the Consul Enterprise Partition of the proxy service instance
	// as registered with the Consul agent.
	Partition string

	// Datacenter is the datacenter where the proxy service instance is registered.
	Datacenter string

	// PrometheusBackendPort will configure a "prometheus_backend" cluster which
	// envoy_prometheus_bind_addr will point to.
	PrometheusBackendPort string

	// PrometheusScrapePath will configure the path where metrics are exposed on
	// the envoy_prometheus_bind_addr listener.
	PrometheusScrapePath string

	PrometheusCAFile   string
	PrometheusCAPath   string
	PrometheusCertFile string
	PrometheusKeyFile  string
}

// GRPC settings used in the bootstrap template.
type GRPC struct {
	// AgentAddress is the IP address of the local agent where the proxy instance
	// is registered.
	AgentAddress string

	// AgentPort is the gRPC port exposed on the local agent.
	AgentPort string

	// AgentTLS is true if the local agent gRPC service should be accessed over
	// TLS.
	AgentTLS bool

	// AgentSocket is the path to a Unix Socket for communicating with the
	// local agent's gRPC endpoint. Disabled if the empty (the default),
	// but overrides AgentAddress and AgentPort if set.
	AgentSocket string
}

// bootstrapTemplate sets '"ignore_health_on_host_removal": false' JUST to force this to be detected as a v3 bootstrap
// config.
const bootstrapTemplate = `{
  "admin": {
    "access_log_path": "{{ .AdminAccessLogPath }}",
    "address": {
      "socket_address": {
        "address": "{{ .AdminBindAddress }}",
        "port_value": {{ .AdminBindPort }}
      }
    }
  },
  "node": {
    "cluster": "{{ .ProxyCluster }}",
    "id": "{{ .ProxyID }}",
    "metadata": {
      {{- if .NodeName }}
      "node_name": "{{ .NodeName }}",
      {{- end }}
      "namespace": "{{if ne .Namespace ""}}{{ .Namespace }}{{else}}default{{end}}",
      "partition": "{{if ne .Partition ""}}{{ .Partition }}{{else}}default{{end}}"
    }
  },
  "layered_runtime": {
    "layers": [
      {
        "name": "base",
        "static_layer": {
          "re2.max_program_size.error_level": 1048576
        }
      }
    ]
  },
  "static_resources": {
    "clusters": [
      {
        "name": "{{ .LocalAgentClusterName }}",
        "ignore_health_on_host_removal": false,
        "connect_timeout": "1s",
        "type": "STATIC",
        {{- if .AgentTLS -}}
        "transport_socket": {
          "name": "tls",
          "typed_config": {
            "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
            "common_tls_context": {
              "validation_context": {
                "trusted_ca": {
                  "inline_string": "{{ .AgentCAPEM }}"
                }
              }
            }
          }
        },
        {{- end }}
        "http2_protocol_options": {},
        "loadAssignment": {
          "clusterName": "{{ .LocalAgentClusterName }}",
          "endpoints": [
            {
              "lbEndpoints": [
                {
                  "endpoint": {
                    "address": {
                      {{- if .AgentSocket -}}
                      "pipe": {
                        "path": "{{ .AgentSocket }}"
                      }
                      {{- else -}}
                      "socket_address": {
                        "address": "{{ .AgentAddress }}",
                        "port_value": {{ .AgentPort }}
                      }
                      {{- end -}}
                    }
                  }
                }
              ]
            }
          ]
        }
      }
      {{- if .StaticClustersJSON -}}
      ,
      {{ .StaticClustersJSON }}
      {{- end }}
    ]{{- if .StaticListenersJSON -}}
    ,
    "listeners": [
      {{ .StaticListenersJSON }}
    ]
    {{- end }}
    {{- if .StaticSecretsJSON -}}
    ,
    "secrets": [
      {{ .StaticSecretsJSON }}
    ]
    {{- end }}
  },
  {{- if .StatsSinksJSON }}
  "stats_sinks": {{ .StatsSinksJSON }},
  {{- end }}
  {{- if .StatsConfigJSON }}
  "stats_config": {{ .StatsConfigJSON }},
  {{- end }}
  {{- if .StatsFlushInterval }}
  "stats_flush_interval": "{{ .StatsFlushInterval }}",
  {{- end }}
  {{- if .TracingConfigJSON }}
  "tracing": {{ .TracingConfigJSON }},
  {{- end }}
  "dynamic_resources": {
    "lds_config": {
      "ads": {},
      "resource_api_version": "V3"
    },
    "cds_config": {
      "ads": {},
      "resource_api_version": "V3"
    },
    "ads_config": {
      "api_type": "DELTA_GRPC",
      "transport_api_version": "V3",
      "grpc_services": {
        "initial_metadata": [
          {
            "key": "x-consul-token",
            "value": "{{ .Token }}"
          }
        ],
        "envoy_grpc": {
          "cluster_name": "{{ .LocalAgentClusterName }}"
        }
      }
    }
  }
}
`
<commit_msg>update prometheus template args<commit_after>package envoy

// BootstrapTplArgs is the set of arguments that may be interpolated into the
// Envoy bootstrap template.
type BootstrapTplArgs struct {
	GRPC

	// ProxyCluster is the cluster name for the the Envoy `node` specification and
	// is typically the same as the ProxyID.
	ProxyCluster string

	// ProxyID is the ID of the proxy service instance as registered with the
	// local Consul agent. This must be used as the Envoy `node.id` in order for
	// the agent to deliver the correct configuration.
	ProxyID string

	// NodeName is the name of the node on which the proxy service instance is registered.
	NodeName string

	// ProxySourceService is the Consul service name to report for this proxy
	// instance's source service label. For sidecars it should be the
	// Proxy.DestinationServiceName. For gateways and similar it is the service
	// name of the proxy service itself.
	ProxySourceService string

	// AgentCAPEM is the CA to use to verify the local agent gRPC service if
	// TLS is enabled.
	AgentCAPEM string

	// AdminAccessLogPath The path to write the access log for the
	// administration server. If no access log is desired specify
	// "/dev/null". By default it will use "/dev/null".
	AdminAccessLogPath string

	// AdminBindAddress is the address the Envoy admin server should bind to.
	AdminBindAddress string

	// AdminBindPort is the port the Envoy admin server should bind to.
	AdminBindPort string

	// LocalAgentClusterName is the name reserved for the local Consul agent gRPC
	// service and is expected to be used for that purpose.
	LocalAgentClusterName string

	// Token is the Consul ACL token provided which is required to make gRPC
	// discovery requests. If non-empty, this must be configured as the gRPC
	// service "initial_metadata" with the key "x-consul-token" in order to
	// authorize the discovery streaming RPCs.
	Token string

	// StaticClustersJSON is JSON string, each is expected to be a valid Cluster
	// definition. They are appended to the "static_resources.clusters" list. Note
	// that cluster names should be chosen in such a way that they won't collide
	// with service names since we use plain service names as cluster names in xDS
	// to make metrics population simpler and cluster names mush be unique. See
	// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/api/v2/cds.proto.
	StaticClustersJSON string

	// StaticListenersJSON is a JSON string containing zero or more Listener
	// definitions. They are appended to the "static_resources.listeners" list. A
	// single listener should be given as a plain object, if more than one is to
	// be added, they should be separated by a comma suitable for direct injection
	// into a JSON array.
	// See https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/api/v2/lds.proto.
	StaticListenersJSON string

	// StatsSinksJSON is a JSON string containing an array in the right format
	// to be rendered as the body of the `stats_sinks` field at the top level of
	// the bootstrap config. It's format may vary based on Envoy version used. See
	// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/metrics/v2/stats.proto#config-metrics-v2-statssink.
	StatsSinksJSON string

	// StatsConfigJSON is a JSON string containing an object in the right format
	// to be rendered as the body of the `stats_config` field at the top level of
	// the bootstrap config. It's format may vary based on Envoy version used. See
	// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/metrics/v2/stats.proto#envoy-api-msg-config-metrics-v2-statsconfig.
	StatsConfigJSON string

	// StaticSecretsJSON is a JSON string containing zero or more Secret definitions.
	// See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/transport_sockets/tls/v3/secret.proto#envoy-v3-api-msg-extensions-transport-sockets-tls-v3-secret
	StaticSecretsJSON string

	// StatsFlushInterval is the time duration between Envoy stats flushes. It is
	// in proto3 "duration" string format for example "1.12s" See
	// https://developers.google.com/protocol-buffers/docs/proto3#json and
	// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/bootstrap/v2/bootstrap.proto#bootstrap
	StatsFlushInterval string

	// TracingConfigJSON is a JSON string containing an object in the right format
	// to be rendered as the body of the `tracing` field at the top level of
	// the bootstrap config. It's format may vary based on Envoy version used.
	// See https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/trace/v2/trace.proto.
	TracingConfigJSON string

	// Namespace is the Consul Enterprise Namespace of the proxy service instance
	// as registered with the Consul agent.
	Namespace string

	// Partition is the Consul Enterprise Partition of the proxy service instance
	// as registered with the Consul agent.
	Partition string

	// Datacenter is the datacenter where the proxy service instance is registered.
	Datacenter string

	// PrometheusBackendPort will configure a "prometheus_backend" cluster which
	// envoy_prometheus_bind_addr will point to.
	PrometheusBackendPort string

	// PrometheusScrapePath will configure the path where metrics are exposed on
	// the envoy_prometheus_bind_addr listener.
	PrometheusScrapePath string

	// PrometheusCAFile is the path to a CA file for Envoy to use when serving TLS on the Prometheius metrics
	// endpoint. Only applicable when envoy_prometheus_bind_addr is set in the proxy config.
	PrometheusCAFile string

	// PrometheusCAPath is the path to a directory of CA certificates for Envoy to use when serving the Prometheus
	// metrics endpoint. Only applicable when envoy_prometheus_bind_addr is set in the proxy config.
	PrometheusCAPath string

	// PrometheusCertFile is the path to a certificate file for Envoy to use when serving TLS on the Prometheus
	// metrics endpoint. Only applicable when envoy_prometheus_bind_addr is set in the proxy config.
	PrometheusCertFile string

	// PrometheusKeyFile is the path to a private key file Envoy to use when service TLS on the Prometheus metrics
	// endpoint. Only applicable when envoy_prometheus_bind_addr is set in the proxy config.
	PrometheusKeyFile string
}

// GRPC settings used in the bootstrap template.
type GRPC struct {
	// AgentAddress is the IP address of the local agent where the proxy instance
	// is registered.
	AgentAddress string

	// AgentPort is the gRPC port exposed on the local agent.
	AgentPort string

	// AgentTLS is true if the local agent gRPC service should be accessed over
	// TLS.
	AgentTLS bool

	// AgentSocket is the path to a Unix Socket for communicating with the
	// local agent's gRPC endpoint. Disabled if the empty (the default),
	// but overrides AgentAddress and AgentPort if set.
	AgentSocket string
}

// bootstrapTemplate sets '"ignore_health_on_host_removal": false' JUST to force this to be detected as a v3 bootstrap
// config.
const bootstrapTemplate = `{
  "admin": {
    "access_log_path": "{{ .AdminAccessLogPath }}",
    "address": {
      "socket_address": {
        "address": "{{ .AdminBindAddress }}",
        "port_value": {{ .AdminBindPort }}
      }
    }
  },
  "node": {
    "cluster": "{{ .ProxyCluster }}",
    "id": "{{ .ProxyID }}",
    "metadata": {
      {{- if .NodeName }}
      "node_name": "{{ .NodeName }}",
      {{- end }}
      "namespace": "{{if ne .Namespace ""}}{{ .Namespace }}{{else}}default{{end}}",
      "partition": "{{if ne .Partition ""}}{{ .Partition }}{{else}}default{{end}}"
    }
  },
  "layered_runtime": {
    "layers": [
      {
        "name": "base",
        "static_layer": {
          "re2.max_program_size.error_level": 1048576
        }
      }
    ]
  },
  "static_resources": {
    "clusters": [
      {
        "name": "{{ .LocalAgentClusterName }}",
        "ignore_health_on_host_removal": false,
        "connect_timeout": "1s",
        "type": "STATIC",
        {{- if .AgentTLS -}}
        "transport_socket": {
          "name": "tls",
          "typed_config": {
            "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
            "common_tls_context": {
              "validation_context": {
                "trusted_ca": {
                  "inline_string": "{{ .AgentCAPEM }}"
                }
              }
            }
          }
        },
        {{- end }}
        "http2_protocol_options": {},
        "loadAssignment": {
          "clusterName": "{{ .LocalAgentClusterName }}",
          "endpoints": [
            {
              "lbEndpoints": [
                {
                  "endpoint": {
                    "address": {
                      {{- if .AgentSocket -}}
                      "pipe": {
                        "path": "{{ .AgentSocket }}"
                      }
                      {{- else -}}
                      "socket_address": {
                        "address": "{{ .AgentAddress }}",
                        "port_value": {{ .AgentPort }}
                      }
                      {{- end -}}
                    }
                  }
                }
              ]
            }
          ]
        }
      }
      {{- if .StaticClustersJSON -}}
      ,
      {{ .StaticClustersJSON }}
      {{- end }}
    ]{{- if .StaticListenersJSON -}}
    ,
    "listeners": [
      {{ .StaticListenersJSON }}
    ]
    {{- end }}
    {{- if .StaticSecretsJSON -}}
    ,
    "secrets": [
      {{ .StaticSecretsJSON }}
    ]
    {{- end }}
  },
  {{- if .StatsSinksJSON }}
  "stats_sinks": {{ .StatsSinksJSON }},
  {{- end }}
  {{- if .StatsConfigJSON }}
  "stats_config": {{ .StatsConfigJSON }},
  {{- end }}
  {{- if .StatsFlushInterval }}
  "stats_flush_interval": "{{ .StatsFlushInterval }}",
  {{- end }}
  {{- if .TracingConfigJSON }}
  "tracing": {{ .TracingConfigJSON }},
  {{- end }}
  "dynamic_resources": {
    "lds_config": {
      "ads": {},
      "resource_api_version": "V3"
    },
    "cds_config": {
      "ads": {},
      "resource_api_version": "V3"
    },
    "ads_config": {
      "api_type": "DELTA_GRPC",
      "transport_api_version": "V3",
      "grpc_services": {
        "initial_metadata": [
          {
            "key": "x-consul-token",
            "value": "{{ .Token }}"
          }
        ],
        "envoy_grpc": {
          "cluster_name": "{{ .LocalAgentClusterName }}"
        }
      }
    }
  }
}
`
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	sq "github.com/Masterminds/squirrel"
	log "github.com/Sirupsen/logrus"
	"github.com/getsentry/raven-go"
	"github.com/guregu/null"
	"github.com/julienschmidt/httprouter"
	"net/http"
	"strconv"
)

// This is the "JSON" struct that appears in the array returned by getRecentSeeks
type SeeksItem struct {
	KeyID                int         `json:"keyId"`
	CreationDate         null.Time   `json:"creationDate"`
	LastModificationDate null.Time   `json:"lastModificationDate"`
	Title                string      `json:"title"`
	Description          null.String `json:"description"` // expect to be truncated
	UserID               int         `json:"userId"`
	SavedSearchID        null.Int    `json:"savedSearchId"`
	NotifyEnabled        null.Bool   `json:"notifyEnabled"`
	Status               null.String `json:"status"`
}
// TODO Maybe use the same struct for both of these? The only difference is truncation of the descrip
// Returned by a getById function
type Seek struct {
	KeyID                int         `json:"keyId"`
	CreationDate         null.Time   `json:"creationDate"`
	LastModificationDate null.Time   `json:"lastModificationDate"`
	Title                string      `json:"title"`
	Description          null.String `json:"description"`
	UserID               int         `json:"userId"`
	SavedSearchID        null.Int    `json:"savedSearchId"`
	NotifyEnabled        null.Bool   `json:"notifyEnabled"`
	Status               null.String `json:"status"`
}

// Writes the most recent count seeks, based on original date created to w
func ServeRecentSeeks(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if r.Method != "GET" {
		http.Error(w, http.StatusText(405), 405)
		return
	}

	// Get limit from params
	limitStr := r.URL.Query().Get("limit")
	limit := defaultNumResults
	var e error
	if limitStr != "" {
		limit, e = strconv.Atoi(limitStr)
		if e != nil || limit == 0 {
			limit = defaultNumResults
		}
	}
	if limit > maxNumResults {
		limit = maxNumResults
	}

	seeks, err, code := GetRecentSeeks(truncationLength, uint64(limit))
	if err != nil {
		raven.CaptureError(err, nil)
		log.Print(err)
		http.Error(w, http.StatusText(code), code)
	}

	Serve(w, seeks)
}

// Returns the most recent count seeks, based on original date created. On error
// returns an error and the HTTP code associated with that error.
func GetRecentSeeks(maxDescriptionSize int, limit uint64) ([]*SeeksItem, error, int) {
	// Create seeks query
	query := psql.
		Select("seeks.key_id", "seeks.creation_date", "seeks.last_modification_date",
			"title", fmt.Sprintf("left(description, %d)", maxDescriptionSize),
			"user_id", "saved_search_id", "notify_enabled", "status").
		From("seeks").
		Where("seeks.is_active=true").
		OrderBy("seeks.creation_date DESC").
		Limit(limit)

	// Query db
	rows, err := query.RunWith(db).Query()
	if err != nil {
		return nil, err, 500
	}
	defer rows.Close()

	// Populate seek structs
	seeks := make([]*SeeksItem, 0)
	for rows.Next() {
		l := new(SeeksItem)
		err := rows.Scan(&l.KeyID, &l.CreationDate, &l.LastModificationDate,
			&l.Title, &l.Description, &l.UserID, &l.SavedSearchID,
			&l.NotifyEnabled, &l.Status)
		if err != nil {
			return nil, err, 500
		}
		seeks = append(seeks, l)
	}
	if err = rows.Err(); err != nil {
		return nil, err, 500
	}

	return seeks, nil, 0
}

// Writes the most recent count seeks, based on original date created to w
func ServeSeekById(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if r.Method != "GET" {
		http.Error(w, http.StatusText(405), 405)
		return
	}

	// Get ID from params
	id := ps.ByName("id")
	if id == "" {
		// Return 404 error with empty body if not found
		http.Error(w, "", 404)
		return
	}

	seeks, err, code := GetSeekById(id)
	if err != nil {
		raven.CaptureError(err, nil)
		log.Print(err)
		http.Error(w, http.StatusText(code), code)
	}

	Serve(w, seeks)
}

// Returns the most recent count seeks, based on original date created. On error
// returns an error and the HTTP code associated with that error.
func GetSeekById(id string) (Seek, error, int) {
	var seek Seek

	// Create seek query
	query := psql.
		Select("seeks.key_id", "seeks.creation_date",
		       "seeks.last_modification_date", "title", "description", "user_id",
		"saved_search_id", "notify_enabled", "status").
		From("seeks").
		Where("seeks.is_active=true").
		Where(sq.Eq{"seeks.key_id": id})

	// Query db for seek
	rows, err := query.RunWith(db).Query()
	if err != nil {
		return seek, err, 500
	}
	defer rows.Close()

	// Populate seek struct
	rows.Next()
	l := new(SeeksItem)
	err = rows.Scan(&l.KeyID, &l.CreationDate, &l.LastModificationDate,
                        &l.Title, &l.Description, &l.UserID, &l.SavedSearchID,
                        &l.NotifyEnabled, &l.Status)
	if err != nil {
		return seek, err, 500
	}

	return seek, nil, 0
}

func ServeAddSeek(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

	if r.Method != "POST" {
		http.Error(w, http.StatusText(405), 405)
		return
	}

	// Get seek to add from request body
	seek := Seek{}
	// TODO this fails silently for some reason if r.Body contains invalid JSON
	json.NewDecoder(r.Body).Decode(&seek)

	// Retrieve UserID
	user, err := getUser(getUsername(r))
	if err != nil { // Not authorized
		raven.CaptureError(err, nil)
		log.Print(err)
		http.Error(w, http.StatusText(401), 401)
	}

	seek, err, code := AddSeek(seek, user.KeyID)
	if err != nil {
		raven.CaptureError(err, nil)
		log.Print(err)
		http.Error(w, http.StatusText(code), code)
	}

	Serve(w, seek)
}

// Inserts the given seek (belonging to userId) into the database. Returns
// seek with its new KeyID added.
func AddSeek(seek Seek, userId int) (Seek, error, int) {
	seek.UserID = userId

	// Insert seek
	stmt := psql.Insert("seeks").
		Columns("title", "description", "user_id", "saved_search_id", "notify_enabled", "status").
		Values(seek.Title, seek.Description, userId, seek.SavedSearchID, seek.NotifyEnabled, seek.Status).
		Suffix("RETURNING key_id, creation_date")

	// Query db for seek
	rows, err := stmt.RunWith(db).Query()
	if err != nil {
		return seek, err, 500
	}
	defer rows.Close()

	// Populate seek struct
	rows.Next()
	err = rows.Scan(&seek.KeyID, &seek.CreationDate)
	if err != nil {
		return seek, err, 500
	}

	return seek, nil, 0
}

<commit_msg>Fix getSeekById returning empty seeks<commit_after>package server

import (
	"encoding/json"
	"fmt"
	sq "github.com/Masterminds/squirrel"
	log "github.com/Sirupsen/logrus"
	"github.com/getsentry/raven-go"
	"github.com/guregu/null"
	"github.com/julienschmidt/httprouter"
	"net/http"
	"strconv"
)

// This is the "JSON" struct that appears in the array returned by getRecentSeeks
type SeeksItem struct {
	KeyID                int         `json:"keyId"`
	CreationDate         null.Time   `json:"creationDate"`
	LastModificationDate null.Time   `json:"lastModificationDate"`
	Title                string      `json:"title"`
	Description          null.String `json:"description"` // expect to be truncated
	UserID               int         `json:"userId"`
	SavedSearchID        null.Int    `json:"savedSearchId"`
	NotifyEnabled        null.Bool   `json:"notifyEnabled"`
	Status               null.String `json:"status"`
}
// TODO Maybe use the same struct for both of these? The only difference is truncation of the descrip
// Returned by a getById function
type Seek struct {
	KeyID                int         `json:"keyId"`
	CreationDate         null.Time   `json:"creationDate"`
	LastModificationDate null.Time   `json:"lastModificationDate"`
	Title                string      `json:"title"`
	Description          null.String `json:"description"`
	UserID               int         `json:"userId"`
	SavedSearchID        null.Int    `json:"savedSearchId"`
	NotifyEnabled        null.Bool   `json:"notifyEnabled"`
	Status               null.String `json:"status"`
}

// Writes the most recent count seeks, based on original date created to w
func ServeRecentSeeks(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if r.Method != "GET" {
		http.Error(w, http.StatusText(405), 405)
		return
	}

	// Get limit from params
	limitStr := r.URL.Query().Get("limit")
	limit := defaultNumResults
	var e error
	if limitStr != "" {
		limit, e = strconv.Atoi(limitStr)
		if e != nil || limit == 0 {
			limit = defaultNumResults
		}
	}
	if limit > maxNumResults {
		limit = maxNumResults
	}

	seeks, err, code := GetRecentSeeks(truncationLength, uint64(limit))
	if err != nil {
		raven.CaptureError(err, nil)
		log.Print(err)
		http.Error(w, http.StatusText(code), code)
	}

	Serve(w, seeks)
}

// Returns the most recent count seeks, based on original date created. On error
// returns an error and the HTTP code associated with that error.
func GetRecentSeeks(maxDescriptionSize int, limit uint64) ([]*SeeksItem, error, int) {
	// Create seeks query
	query := psql.
		Select("seeks.key_id", "seeks.creation_date", "seeks.last_modification_date",
			"title", fmt.Sprintf("left(description, %d)", maxDescriptionSize),
			"user_id", "saved_search_id", "notify_enabled", "status").
		From("seeks").
		Where("seeks.is_active=true").
		OrderBy("seeks.creation_date DESC").
		Limit(limit)

	// Query db
	rows, err := query.RunWith(db).Query()
	if err != nil {
		return nil, err, 500
	}
	defer rows.Close()

	// Populate seek structs
	seeks := make([]*SeeksItem, 0)
	for rows.Next() {
		l := new(SeeksItem)
		err := rows.Scan(&l.KeyID, &l.CreationDate, &l.LastModificationDate,
			&l.Title, &l.Description, &l.UserID, &l.SavedSearchID,
			&l.NotifyEnabled, &l.Status)
		if err != nil {
			return nil, err, 500
		}
		seeks = append(seeks, l)
	}
	if err = rows.Err(); err != nil {
		return nil, err, 500
	}

	return seeks, nil, 0
}

// Writes the most recent count seeks, based on original date created to w
func ServeSeekById(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if r.Method != "GET" {
		http.Error(w, http.StatusText(405), 405)
		return
	}

	// Get ID from params
	id := ps.ByName("id")
	if id == "" {
		// Return 404 error with empty body if not found
		http.Error(w, "", 404)
		return
	}

	seeks, err, code := GetSeekById(id)
	if err != nil {
		raven.CaptureError(err, nil)
		log.Print(err)
		http.Error(w, http.StatusText(code), code)
	}

	Serve(w, seeks)
}

// Returns the most recent count seeks, based on original date created. On error
// returns an error and the HTTP code associated with that error.
func GetSeekById(id string) (Seek, error, int) {
	var seek Seek

	// Create seek query
	query := psql.
		Select("seeks.key_id", "seeks.creation_date",
		       "seeks.last_modification_date", "title", "description", "user_id",
		"saved_search_id", "notify_enabled", "status").
		From("seeks").
		Where("seeks.is_active=true").
		Where(sq.Eq{"seeks.key_id": id})

	// Query db for seek
	rows, err := query.RunWith(db).Query()
	if err != nil {
		return seek, err, 500
	}
	defer rows.Close()

	// Populate seek struct
	rows.Next()
	err = rows.Scan(&seek.KeyID, &seek.CreationDate, &seek.LastModificationDate,
		&seek.Title, &seek.Description, &seek.UserID, &seek.SavedSearchID,
		&seek.NotifyEnabled, &seek.Status)
	if err != nil {
		return seek, err, 500
	}

	return seek, nil, 0
}

func ServeAddSeek(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

	if r.Method != "POST" {
		http.Error(w, http.StatusText(405), 405)
		return
	}

	// Get seek to add from request body
	seek := Seek{}
	// TODO this fails silently for some reason if r.Body contains invalid JSON
	json.NewDecoder(r.Body).Decode(&seek)

	// Retrieve UserID
	user, err := getUser(getUsername(r))
	if err != nil { // Not authorized
		raven.CaptureError(err, nil)
		log.Print(err)
		http.Error(w, http.StatusText(401), 401)
	}

	seek, err, code := AddSeek(seek, user.KeyID)
	if err != nil {
		raven.CaptureError(err, nil)
		log.Print(err)
		http.Error(w, http.StatusText(code), code)
	}

	Serve(w, seek)
}

// Inserts the given seek (belonging to userId) into the database. Returns
// seek with its new KeyID added.
func AddSeek(seek Seek, userId int) (Seek, error, int) {
	seek.UserID = userId

	// Insert seek
	stmt := psql.Insert("seeks").
		Columns("title", "description", "user_id", "saved_search_id", "notify_enabled", "status").
		Values(seek.Title, seek.Description, userId, seek.SavedSearchID, seek.NotifyEnabled, seek.Status).
		Suffix("RETURNING key_id, creation_date")

	// Query db for seek
	rows, err := stmt.RunWith(db).Query()
	if err != nil {
		return seek, err, 500
	}
	defer rows.Close()

	// Populate seek struct
	rows.Next()
	err = rows.Scan(&seek.KeyID, &seek.CreationDate)
	if err != nil {
		return seek, err, 500
	}

	return seek, nil, 0
}

<|endoftext|>


<commit_before>// Backend server - main part of LogVoyage service.
// It accepts connections from "Client", parses string and pushes it to ElasticSearch index
package main

import (
	"encoding/json"
	"errors"
	"flag"
	"log"
	"runtime"
	"strings"
	"time"

	"github.com/firstrow/logvoyage/common"
	"github.com/firstrow/tcp_server"
)

var (
	defaultHost = ""
	defaultPort = "27077"
)

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

	log.Print("Initializing server")

	host := flag.String("host", defaultHost, "Host to open server. Set to `localhost` to accept only local connections.")
	port := flag.String("port", defaultPort, "Port to accept new connections. Default value: "+defaultPort)
	flag.Parse()

	server := tcp_server.New(*host + ":" + *port)
	server.OnNewClient(func(c *tcp_server.Client) {
		log.Print("New client")
	})

	// Receives new message and send it to Elastic server
	// Message examples:
	// apiKey Some text
	// apiKey {message: "Some text", field:"value", ...}
	server.OnNewMessage(func(c *tcp_server.Client, message string) {
		indexName, err := getIndexName(message)
		if err != nil {
			// TODO: Log error
		} else {
			message = common.RemoveApiKey(message)
			message = strings.TrimSpace(message)

			var data map[string]interface{}
			err := json.Unmarshal([]byte(message), &data)

			if err == nil {
				// Save parsed json
				data["datetime"] = time.Now().UTC()
				toElastic(indexName, data)
			} else {
				// Could not parse json, save entire message.
				record := &common.LogRecord{
					Message:  message,
					Datetime: time.Now().UTC(),
				}
				toElastic(indexName, record)
			}
		}
	})

	server.OnClientConnectionClosed(func(c *tcp_server.Client, err error) {
		log.Print("Client disconnected")
	})
	server.Listen()
}

// Get users index name by apiKey
func getIndexName(message string) (string, error) {
	key, err := common.ExtractApiKey(message)
	if err != nil {
		log.Println(err.Error())
		return "", err
	}

	user := common.FindUserByApiKey(key)
	if user == nil {
		log.Println("User not found")
		return "", errors.New("Error. User not found")
	}

	return user.GetIndexName(), nil
}

func toElastic(indexName string, record interface{}) {
	j, err := json.Marshal(record)
	if err != nil {
		log.Print("Error encoding message to JSON")
	} else {
		common.SendToElastic(indexName+"/logs", "POST", j)
	}
}
<commit_msg>Added comment<commit_after>// Backend server - main part of LogVoyage service.
// It accepts connections from "Client", parses string and pushes it to ElasticSearch index
package main

import (
	"encoding/json"
	"errors"
	"flag"
	"log"
	"runtime"
	"strings"
	"time"

	"github.com/firstrow/logvoyage/common"
	"github.com/firstrow/tcp_server"
)

var (
	defaultHost = ""
	defaultPort = "27077"
)

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

	log.Print("Initializing server")

	host := flag.String("host", defaultHost, "Host to open server. Set to `localhost` to accept only local connections.")
	port := flag.String("port", defaultPort, "Port to accept new connections. Default value: "+defaultPort)
	flag.Parse()

	server := tcp_server.New(*host + ":" + *port)
	server.OnNewClient(func(c *tcp_server.Client) {
		log.Print("New client")
	})

	// Receives new message and send it to Elastic server
	// Message examples:
	// apiKey Some text
	// apiKey {message: "Some text", field:"value", ...}
	server.OnNewMessage(func(c *tcp_server.Client, message string) {
		indexName, err := getIndexName(message)
		if err != nil {
			// TODO: Log error
		} else {
			message = common.RemoveApiKey(message)
			message = strings.TrimSpace(message)

			var data map[string]interface{}
			err := json.Unmarshal([]byte(message), &data)

			if err == nil {
				// Save parsed json
				data["datetime"] = time.Now().UTC()
				toElastic(indexName, data)
			} else {
				// Could not parse json, save entire message.
				record := &common.LogRecord{
					Message:  message,
					Datetime: time.Now().UTC(),
				}
				toElastic(indexName, record)
			}
		}
	})

	server.OnClientConnectionClosed(func(c *tcp_server.Client, err error) {
		log.Print("Client disconnected")
	})
	server.Listen()
}

// Get users index name by apiKey
func getIndexName(message string) (string, error) {
	key, err := common.ExtractApiKey(message)
	if err != nil {
		log.Println(err.Error())
		return "", err
	}

	user := common.FindUserByApiKey(key)
	if user == nil {
		log.Println("User not found")
		return "", errors.New("Error. User not found")
	}

	return user.GetIndexName(), nil
}

// Sends data to elastic index
func toElastic(indexName string, record interface{}) {
	j, err := json.Marshal(record)
	if err != nil {
		log.Print("Error encoding message to JSON")
	} else {
		common.SendToElastic(indexName+"/logs", "POST", j)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"

	"github.com/gorilla/mux"
	"github.com/op/go-logging"
	"syscall"
	"path"
	"flag"
	"os"
)

var log = logging.MustGetLogger("nomad-ui")

func init() {
	var format = logging.MustStringFormatter(
		`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{color:reset} %{message}`,
	)

	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	backend2Formatter := logging.NewBackendFormatter(logBackend, format)
	logging.SetBackend(backend2Formatter)
}

type Config struct {
	Address       string
	ListenAddress string
	Endpoint      string
}

func DefaultConfig() *Config {
	return &Config{
		Address: "http://127.0.0.1:4646",
		ListenAddress: ":3000",
		Endpoint: "/",
	}
}

var (
	flagAddress = flag.String("address", "", "The address of the Nomad server. " +
		"Overrides the NOMAD_ADDR environment variable if set. " +
		"(default: \"http://127.0.0.1:4646\")")
	flagListenAddress = flag.String("web.listen-address", "",
		"The address on which to expose the web interface. (default: \":3000\")")
	flagEndpoint = flag.String("web.path", "",
		"Path under which to expose the web interface. (default: \"/\")")
)

func (c *Config) Parse() {
	flag.Parse()

	address, ok := syscall.Getenv("NOMAD_ADDR")
	if ok {
		c.Address = address
	}
	if *flagAddress != "" {
		c.Address = *flagAddress
	}
	if *flagListenAddress != "" {
		c.ListenAddress = *flagListenAddress
	}
	if *flagEndpoint != "" {
		c.Endpoint = *flagEndpoint
	}
}

func main() {
	cfg := DefaultConfig()
	cfg.Parse()

	router := mux.NewRouter()

	broadcast := make(chan *Action)

	nomad := NewNomad(cfg.Address, broadcast)
	go nomad.watchAllocs()
	go nomad.watchEvals()
	go nomad.watchNodes()
	go nomad.watchJobs()

	hub := NewHub(nomad, broadcast)
	go hub.Run()

	router.HandleFunc(path.Join(cfg.Endpoint, "ws"), hub.Handler)
	router.PathPrefix(cfg.Endpoint).Handler(http.FileServer(assetFS()))

	log.Infof("Listening on %s", cfg.ListenAddress)
	err := http.ListenAndServe(cfg.ListenAddress, router)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Use config defaults<commit_after>package main

import (
	"net/http"

	"github.com/gorilla/mux"
	"github.com/op/go-logging"
	"syscall"
	"path"
	"flag"
	"os"
	"fmt"
)

var log = logging.MustGetLogger("nomad-ui")

func init() {
	var format = logging.MustStringFormatter(
		`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{color:reset} %{message}`,
	)

	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	backend2Formatter := logging.NewBackendFormatter(logBackend, format)
	logging.SetBackend(backend2Formatter)
}

type Config struct {
	Address       string
	ListenAddress string
	Endpoint      string
}

func DefaultConfig() *Config {
	return &Config{
		Address: "http://127.0.0.1:4646",
		ListenAddress: ":3000",
		Endpoint: "/",
	}
}

func flagDefault(value string) string {
	return fmt.Sprintf("(default: \"%s\")", value)
}

var (
	defaultConfig = DefaultConfig()

	flagAddress = flag.String("address", "", "The address of the Nomad server. " +
		"Overrides the NOMAD_ADDR environment variable if set. " + flagDefault(defaultConfig.Address))
	flagListenAddress = flag.String("web.listen-address", "",
		"The address on which to expose the web interface. " + flagDefault(defaultConfig.ListenAddress))
	flagEndpoint = flag.String("web.path", "",
		"Path under which to expose the web interface. " + flagDefault(defaultConfig.Endpoint))
)

func (c *Config) Parse() {
	flag.Parse()

	address, ok := syscall.Getenv("NOMAD_ADDR")
	if ok {
		c.Address = address
	}
	if *flagAddress != "" {
		c.Address = *flagAddress
	}
	if *flagListenAddress != "" {
		c.ListenAddress = *flagListenAddress
	}
	if *flagEndpoint != "" {
		c.Endpoint = *flagEndpoint
	}
}

func main() {
	cfg := DefaultConfig()
	cfg.Parse()

	router := mux.NewRouter()

	broadcast := make(chan *Action)

	nomad := NewNomad(cfg.Address, broadcast)
	go nomad.watchAllocs()
	go nomad.watchEvals()
	go nomad.watchNodes()
	go nomad.watchJobs()

	hub := NewHub(nomad, broadcast)
	go hub.Run()

	router.HandleFunc(path.Join(cfg.Endpoint, "ws"), hub.Handler)
	router.PathPrefix(cfg.Endpoint).Handler(http.FileServer(assetFS()))

	log.Infof("Listening on %s", cfg.ListenAddress)
	err := http.ListenAndServe(cfg.ListenAddress, router)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package quic

import (
	"bytes"
	"net"
	"time"

	"github.com/lucas-clemente/quic-go/crypto"
	"github.com/lucas-clemente/quic-go/handshake"
	"github.com/lucas-clemente/quic-go/protocol"
	"github.com/lucas-clemente/quic-go/testdata"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type mockSession struct {
	connectionID protocol.ConnectionID
	packetCount  int
}

func (s *mockSession) HandlePacket(addr interface{}, publicHeader *PublicHeader, r *bytes.Reader) {
	s.packetCount++
}

func (s *mockSession) Run() {
}

func newMockSession(conn connection, v protocol.VersionNumber, connectionID protocol.ConnectionID, sCfg *handshake.ServerConfig, streamCallback StreamCallback, closeCallback CloseCallback) PacketHandler {
	return &mockSession{
		connectionID: connectionID,
	}
}

var _ = Describe("Server", func() {
	Describe("with mock session", func() {
		var (
			server *Server
		)

		BeforeEach(func() {
			server = &Server{
				sessions:   map[protocol.ConnectionID]PacketHandler{},
				newSession: newMockSession,
			}
		})

		It("composes version negotiation packets", func() {
			expected := append(
				[]byte{0x3d, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0},
				protocol.SupportedVersionsAsTags...,
			)
			Expect(composeVersionNegotiation(1)).To(Equal(expected))
		})

		It("creates new sessions", func() {
			err := server.handlePacket(nil, nil, []byte{0x08, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x01})
			Expect(err).ToNot(HaveOccurred())
			Expect(server.sessions).To(HaveLen(1))
			Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).connectionID).To(Equal(protocol.ConnectionID(0x4cfa9f9b668619f6)))
			Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).packetCount).To(Equal(1))
		})

		It("assigns packets to existing sessions", func() {
			err := server.handlePacket(nil, nil, []byte{0x08, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x01})
			Expect(err).ToNot(HaveOccurred())
			err = server.handlePacket(nil, nil, []byte{0x08, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x01})
			Expect(err).ToNot(HaveOccurred())
			Expect(server.sessions).To(HaveLen(1))
			Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).connectionID).To(Equal(protocol.ConnectionID(0x4cfa9f9b668619f6)))
			Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).packetCount).To(Equal(2))
		})
	})

	It("setups and responds with version negotiation", func() {
		server, err := NewServer(testdata.GetTLSConfig(), nil)
		Expect(err).ToNot(HaveOccurred())
		go func() {
			defer GinkgoRecover()
			err := server.ListenAndServe("127.0.0.1:13370")
			Expect(err).To(HaveOccurred())
		}()

		time.Sleep(10 * time.Millisecond)
		addr, err2 := net.ResolveUDPAddr("udp", "127.0.0.1:13370")
		Expect(err2).ToNot(HaveOccurred())
		conn, err2 := net.DialUDP("udp", nil, addr)
		Expect(err2).ToNot(HaveOccurred())
		_, err2 = conn.Write([]byte{0x09, 0x01, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 'Q', '0', '0', '0', 0x01})
		Expect(err2).ToNot(HaveOccurred())
		data := make([]byte, 1000)
		n, _, err2 := conn.ReadFromUDP(data)
		Expect(err2).ToNot(HaveOccurred())
		data = data[:n]
		expected := append(
			[]byte{0x3d, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0},
			protocol.SupportedVersionsAsTags...,
		)
		Expect(data).To(Equal(expected))
		err2 = server.Close()
		Expect(err2).ToNot(HaveOccurred())
	})

	It("setups and responds with error on invalid frame", func() {
		server, err := NewServer(testdata.GetTLSConfig(), nil)
		Expect(err).ToNot(HaveOccurred())
		go func() {
			defer GinkgoRecover()
			err := server.ListenAndServe("127.0.0.1:13370")
			Expect(err).To(HaveOccurred())
		}()

		time.Sleep(10 * time.Millisecond)
		addr, err2 := net.ResolveUDPAddr("udp", "127.0.0.1:13370")
		Expect(err2).ToNot(HaveOccurred())
		conn, err2 := net.DialUDP("udp", nil, addr)
		Expect(err2).ToNot(HaveOccurred())
		_, err2 = conn.Write([]byte{0x09, 0x01, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 'Q', '0', '0', '0', 0x01, 0x00})
		Expect(err2).ToNot(HaveOccurred())
		data := make([]byte, 1000)
		n, _, err2 := conn.ReadFromUDP(data)
		Expect(err2).ToNot(HaveOccurred())
		Expect(n).ToNot(BeZero())
		time.Sleep(10 * time.Millisecond)
		err2 = server.Close()
		Expect(err2).ToNot(HaveOccurred())
	})

	It("closes and deletes sessions", func() {
		server, err := NewServer(testdata.GetTLSConfig(), nil)
		Expect(err).ToNot(HaveOccurred())
		go func() {
			defer GinkgoRecover()
			err := server.ListenAndServe("127.0.0.1:13370")
			Expect(err).To(HaveOccurred())
		}()

		addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:13370")
		Expect(err).ToNot(HaveOccurred())

		// Send an invalid packet
		time.Sleep(10 * time.Millisecond)
		conn, err := net.DialUDP("udp", nil, addr)
		Expect(err).ToNot(HaveOccurred())
		pheader := []byte{0x09, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x51, 0x30, 0x33, 0x32, 0x01}
		_, err = conn.Write(append(pheader, (&crypto.NullAEAD{}).Seal(0, pheader, nil)...))
		Expect(err).ToNot(HaveOccurred())

		time.Sleep(10 * time.Millisecond)

		// The server should now have closed the session, leaving a nil value in the sessions map
		Expect(server.sessions).To(HaveLen(1))
		// Expect(server.sessions[0x4cfa9f9b668619f6]).To(BeNil())
		Expect(server.sessions[0x4cfa9f9b668619f6]).To(BeNil())

		err = server.Close()
		Expect(err).ToNot(HaveOccurred())
	})
})
<commit_msg>increase sleeps in server test<commit_after>package quic

import (
	"bytes"
	"net"
	"time"

	"github.com/lucas-clemente/quic-go/crypto"
	"github.com/lucas-clemente/quic-go/handshake"
	"github.com/lucas-clemente/quic-go/protocol"
	"github.com/lucas-clemente/quic-go/testdata"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type mockSession struct {
	connectionID protocol.ConnectionID
	packetCount  int
}

func (s *mockSession) HandlePacket(addr interface{}, publicHeader *PublicHeader, r *bytes.Reader) {
	s.packetCount++
}

func (s *mockSession) Run() {
}

func newMockSession(conn connection, v protocol.VersionNumber, connectionID protocol.ConnectionID, sCfg *handshake.ServerConfig, streamCallback StreamCallback, closeCallback CloseCallback) PacketHandler {
	return &mockSession{
		connectionID: connectionID,
	}
}

var _ = Describe("Server", func() {
	Describe("with mock session", func() {
		var (
			server *Server
		)

		BeforeEach(func() {
			server = &Server{
				sessions:   map[protocol.ConnectionID]PacketHandler{},
				newSession: newMockSession,
			}
		})

		It("composes version negotiation packets", func() {
			expected := append(
				[]byte{0x3d, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0},
				protocol.SupportedVersionsAsTags...,
			)
			Expect(composeVersionNegotiation(1)).To(Equal(expected))
		})

		It("creates new sessions", func() {
			err := server.handlePacket(nil, nil, []byte{0x08, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x01})
			Expect(err).ToNot(HaveOccurred())
			Expect(server.sessions).To(HaveLen(1))
			Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).connectionID).To(Equal(protocol.ConnectionID(0x4cfa9f9b668619f6)))
			Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).packetCount).To(Equal(1))
		})

		It("assigns packets to existing sessions", func() {
			err := server.handlePacket(nil, nil, []byte{0x08, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x01})
			Expect(err).ToNot(HaveOccurred())
			err = server.handlePacket(nil, nil, []byte{0x08, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x01})
			Expect(err).ToNot(HaveOccurred())
			Expect(server.sessions).To(HaveLen(1))
			Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).connectionID).To(Equal(protocol.ConnectionID(0x4cfa9f9b668619f6)))
			Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).packetCount).To(Equal(2))
		})
	})

	It("setups and responds with version negotiation", func() {
		server, err := NewServer(testdata.GetTLSConfig(), nil)
		Expect(err).ToNot(HaveOccurred())
		go func() {
			defer GinkgoRecover()
			err := server.ListenAndServe("127.0.0.1:13370")
			Expect(err).To(HaveOccurred())
		}()

		time.Sleep(50 * time.Millisecond)
		addr, err2 := net.ResolveUDPAddr("udp", "127.0.0.1:13370")
		Expect(err2).ToNot(HaveOccurred())
		conn, err2 := net.DialUDP("udp", nil, addr)
		Expect(err2).ToNot(HaveOccurred())
		_, err2 = conn.Write([]byte{0x09, 0x01, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 'Q', '0', '0', '0', 0x01})
		Expect(err2).ToNot(HaveOccurred())
		data := make([]byte, 1000)
		n, _, err2 := conn.ReadFromUDP(data)
		Expect(err2).ToNot(HaveOccurred())
		data = data[:n]
		expected := append(
			[]byte{0x3d, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0},
			protocol.SupportedVersionsAsTags...,
		)
		Expect(data).To(Equal(expected))
		err2 = server.Close()
		Expect(err2).ToNot(HaveOccurred())
	})

	It("setups and responds with error on invalid frame", func() {
		server, err := NewServer(testdata.GetTLSConfig(), nil)
		Expect(err).ToNot(HaveOccurred())
		go func() {
			defer GinkgoRecover()
			err := server.ListenAndServe("127.0.0.1:13370")
			Expect(err).To(HaveOccurred())
		}()

		time.Sleep(50 * time.Millisecond)
		addr, err2 := net.ResolveUDPAddr("udp", "127.0.0.1:13370")
		Expect(err2).ToNot(HaveOccurred())
		conn, err2 := net.DialUDP("udp", nil, addr)
		Expect(err2).ToNot(HaveOccurred())
		_, err2 = conn.Write([]byte{0x09, 0x01, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 'Q', '0', '0', '0', 0x01, 0x00})
		Expect(err2).ToNot(HaveOccurred())
		data := make([]byte, 1000)
		n, _, err2 := conn.ReadFromUDP(data)
		Expect(err2).ToNot(HaveOccurred())
		Expect(n).ToNot(BeZero())
		time.Sleep(20 * time.Millisecond)
		err2 = server.Close()
		Expect(err2).ToNot(HaveOccurred())
	})

	It("closes and deletes sessions", func() {
		server, err := NewServer(testdata.GetTLSConfig(), nil)
		Expect(err).ToNot(HaveOccurred())
		go func() {
			defer GinkgoRecover()
			err := server.ListenAndServe("127.0.0.1:13370")
			Expect(err).To(HaveOccurred())
		}()

		addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:13370")
		Expect(err).ToNot(HaveOccurred())

		// Send an invalid packet
		time.Sleep(50 * time.Millisecond)
		conn, err := net.DialUDP("udp", nil, addr)
		Expect(err).ToNot(HaveOccurred())
		pheader := []byte{0x09, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x51, 0x30, 0x33, 0x32, 0x01}
		_, err = conn.Write(append(pheader, (&crypto.NullAEAD{}).Seal(0, pheader, nil)...))
		Expect(err).ToNot(HaveOccurred())

		time.Sleep(10 * time.Millisecond)

		// The server should now have closed the session, leaving a nil value in the sessions map
		Expect(server.sessions).To(HaveLen(1))
		// Expect(server.sessions[0x4cfa9f9b668619f6]).To(BeNil())
		Expect(server.sessions[0x4cfa9f9b668619f6]).To(BeNil())

		err = server.Close()
		Expect(err).ToNot(HaveOccurred())
	})
})
<|endoftext|>


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

// +build !appengine

package dns

import (
	"runtime"
	"testing"
	"time"
)

func HelloServer(w ResponseWriter, req *Msg) {
	m := new(Msg)
	m.SetReply(req)

	m.Extra = make([]RR, 1)
	m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
	w.WriteMsg(m)
}

func AnotherHelloServer(w ResponseWriter, req *Msg) {
	m := new(Msg)
	m.SetReply(req)

	m.Extra = make([]RR, 1)
	m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello example"}}
	w.WriteMsg(m)
}

func TestServing(t *testing.T) {
	HandleFunc("miek.nl.", HelloServer)
	HandleFunc("example.com.", AnotherHelloServer)
	go func() {
		err := ListenAndServe(":8053", "udp", nil)
		if err != nil {
			t.Log("ListenAndServe: ", err.Error())
			t.Fatal()
		}
	}()
	time.Sleep(4e8)
	c := new(Client)
	m := new(Msg)
	m.SetQuestion("miek.nl.", TypeTXT)
	r, _, _ := c.Exchange(m, "127.0.0.1:8053")
	txt := r.Extra[0].(*TXT).Txt[0]
	if txt != "Hello world" {
		t.Log("Unexpected result for miek.nl", txt, "!= Hello world")
		t.Fail()
	}
	m.SetQuestion("example.com.", TypeTXT)
	r, _, _ = c.Exchange(m, "127.0.0.1:8053")
	txt = r.Extra[0].(*TXT).Txt[0]
	if txt != "Hello example" {
		t.Log("Unexpected result for example.com", txt, "!= Hello example")
		t.Fail()
	}
	// Test Mixes cased as notices by Ask.
	m.SetQuestion("eXaMplE.cOm.", TypeTXT)
	r, _, _ = c.Exchange(m, "127.0.0.1:8053")
	txt = r.Extra[0].(*TXT).Txt[0]
	if txt != "Hello example" {
		t.Log("Unexpected result for example.com", txt, "!= Hello example")
		t.Fail()
	}
}

func BenchmarkServing(b *testing.B) {
	b.StopTimer()
	HandleFunc("miek.nl.", HelloServer)
	a := runtime.GOMAXPROCS(4)
	go func() {
		ListenAndServe("127.0.0.1:8053", "udp", nil)
	}()
	c := new(Client)
	m := new(Msg)
	m.SetQuestion("miek.nl", TypeSOA)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		c.Exchange(m, "127.0.0.1:8053")
	}
	runtime.GOMAXPROCS(a)
}

func HelloServerCompress(w ResponseWriter, req *Msg) {
	m := new(Msg)
	m.SetReply(req)
	m.Extra = make([]RR, 1)
	m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
	m.Compress = true
	w.WriteMsg(m)
}

func BenchmarkServingCompress(b *testing.B) {
	b.StopTimer()
	HandleFunc("miek.nl.", HelloServerCompress)
	a := runtime.GOMAXPROCS(4)
	go func() {
		ListenAndServe("127.0.0.1:8053", "udp", nil)
	}()

	c := new(Client)
	m := new(Msg)
	m.SetQuestion("miek.nl", TypeSOA)
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		c.Exchange(m, "127.0.0.1:8053")
	}
	runtime.GOMAXPROCS(a)
}

func TestDotAsCatchAllWildcard(t *testing.T) {
	mux := NewServeMux()
	mux.Handle(".", HandlerFunc(HelloServer))
	mux.Handle("example.com.", HandlerFunc(AnotherHelloServer))

	handler := mux.match("www.miek.nl.", TypeTXT)
	if handler == nil {
		t.Error("wildcard match failed")
	}

	handler = mux.match("www.example.com.", TypeTXT)
	if handler == nil {
		t.Error("example.com match failed")
	}

	handler = mux.match("a.www.example.com.", TypeTXT)
	if handler == nil {
		t.Error("a.www.example.com match failed")
	}

	handler = mux.match("boe.", TypeTXT)
	if handler == nil {
		t.Error("boe. match failed")
	}
}

func TestRootServer(t *testing.T) {
	mux := NewServeMux()
	mux.Handle(".", HandlerFunc(HelloServer))

	handler := mux.match(".", TypeNS)
	if handler == nil {
		t.Error("root match failed")
	}
}
<commit_msg>tweaks comment<commit_after>// Copyright 2011 Miek Gieben. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !appengine

package dns

import (
	"runtime"
	"testing"
	"time"
)

func HelloServer(w ResponseWriter, req *Msg) {
	m := new(Msg)
	m.SetReply(req)

	m.Extra = make([]RR, 1)
	m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
	w.WriteMsg(m)
}

func AnotherHelloServer(w ResponseWriter, req *Msg) {
	m := new(Msg)
	m.SetReply(req)

	m.Extra = make([]RR, 1)
	m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello example"}}
	w.WriteMsg(m)
}

func TestServing(t *testing.T) {
	HandleFunc("miek.nl.", HelloServer)
	HandleFunc("example.com.", AnotherHelloServer)
	go func() {
		err := ListenAndServe(":8053", "udp", nil)
		if err != nil {
			t.Log("ListenAndServe: ", err.Error())
			t.Fatal()
		}
	}()
	time.Sleep(4e8)
	c := new(Client)
	m := new(Msg)
	m.SetQuestion("miek.nl.", TypeTXT)
	r, _, _ := c.Exchange(m, "127.0.0.1:8053")
	txt := r.Extra[0].(*TXT).Txt[0]
	if txt != "Hello world" {
		t.Log("Unexpected result for miek.nl", txt, "!= Hello world")
		t.Fail()
	}
	m.SetQuestion("example.com.", TypeTXT)
	r, _, _ = c.Exchange(m, "127.0.0.1:8053")
	txt = r.Extra[0].(*TXT).Txt[0]
	if txt != "Hello example" {
		t.Log("Unexpected result for example.com", txt, "!= Hello example")
		t.Fail()
	}
	// Test Mixes cased as noticed by Ask.
	m.SetQuestion("eXaMplE.cOm.", TypeTXT)
	r, _, _ = c.Exchange(m, "127.0.0.1:8053")
	txt = r.Extra[0].(*TXT).Txt[0]
	if txt != "Hello example" {
		t.Log("Unexpected result for example.com", txt, "!= Hello example")
		t.Fail()
	}
}

func BenchmarkServing(b *testing.B) {
	b.StopTimer()
	HandleFunc("miek.nl.", HelloServer)
	a := runtime.GOMAXPROCS(4)
	go func() {
		ListenAndServe("127.0.0.1:8053", "udp", nil)
	}()
	c := new(Client)
	m := new(Msg)
	m.SetQuestion("miek.nl", TypeSOA)

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		c.Exchange(m, "127.0.0.1:8053")
	}
	runtime.GOMAXPROCS(a)
}

func HelloServerCompress(w ResponseWriter, req *Msg) {
	m := new(Msg)
	m.SetReply(req)
	m.Extra = make([]RR, 1)
	m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
	m.Compress = true
	w.WriteMsg(m)
}

func BenchmarkServingCompress(b *testing.B) {
	b.StopTimer()
	HandleFunc("miek.nl.", HelloServerCompress)
	a := runtime.GOMAXPROCS(4)
	go func() {
		ListenAndServe("127.0.0.1:8053", "udp", nil)
	}()

	c := new(Client)
	m := new(Msg)
	m.SetQuestion("miek.nl", TypeSOA)
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		c.Exchange(m, "127.0.0.1:8053")
	}
	runtime.GOMAXPROCS(a)
}

func TestDotAsCatchAllWildcard(t *testing.T) {
	mux := NewServeMux()
	mux.Handle(".", HandlerFunc(HelloServer))
	mux.Handle("example.com.", HandlerFunc(AnotherHelloServer))

	handler := mux.match("www.miek.nl.", TypeTXT)
	if handler == nil {
		t.Error("wildcard match failed")
	}

	handler = mux.match("www.example.com.", TypeTXT)
	if handler == nil {
		t.Error("example.com match failed")
	}

	handler = mux.match("a.www.example.com.", TypeTXT)
	if handler == nil {
		t.Error("a.www.example.com match failed")
	}

	handler = mux.match("boe.", TypeTXT)
	if handler == nil {
		t.Error("boe. match failed")
	}
}

func TestRootServer(t *testing.T) {
	mux := NewServeMux()
	mux.Handle(".", HandlerFunc(HelloServer))

	handler := mux.match(".", TypeNS)
	if handler == nil {
		t.Error("root match failed")
	}
}
<|endoftext|>


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

/*
	Package builtin provides documentation for Go's predeclared identifiers.
	The items documented here are not actually in package builtin
	but their descriptions here allow godoc to present documentation
	for the language's special identifiers.
*/
package builtin

// bool is the set of boolean values, true and false.
type bool bool

// true and false are the two untyped boolean values.
const (
	true  = 0 == 0 // Untyped bool.
	false = 0 != 0 // Untyped bool.
)

// uint8 is the set of all unsigned 8-bit integers.
// Range: 0 through 255.
type uint8 uint8

// uint16 is the set of all unsigned 16-bit integers.
// Range: 0 through 65535.
type uint16 uint16

// uint32 is the set of all unsigned 32-bit integers.
// Range: 0 through 4294967295.
type uint32 uint32

// uint64 is the set of all unsigned 64-bit integers.
// Range: 0 through 18446744073709551615.
type uint64 uint64

// int8 is the set of all signed 8-bit integers.
// Range: -128 through 127.
type int8 int8

// int16 is the set of all signed 16-bit integers.
// Range: -32768 through 32767.
type int16 int16

// int32 is the set of all signed 32-bit integers.
// Range: -2147483648 through 2147483647.
type int32 int32

// int64 is the set of all signed 64-bit integers.
// Range: -9223372036854775808 through 9223372036854775807.
type int64 int64

// float32 is the set of all IEEE-754 32-bit floating-point numbers.
type float32 float32

// float64 is the set of all IEEE-754 64-bit floating-point numbers.
type float64 float64

// complex64 is the set of all complex numbers with float32 real and
// imaginary parts.
type complex64 complex64

// complex128 is the set of all complex numbers with float64 real and
// imaginary parts.
type complex128 complex128

// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string

// int is a signed integer type that is at least 32 bits in size. It is a
// distinct type, however, and not an alias for, say, int32.
type int int

// uint is an unsigned integer type that is at least 32 bits in size. It is a
// distinct type, however, and not an alias for, say, uint32.
type uint uint

// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
type byte = uint8

// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32

// iota is a predeclared identifier representing the untyped integer ordinal
// number of the current const specification in a (usually parenthesized)
// const declaration. It is zero-indexed.
const iota = 0 // Untyped int.

// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

// Type is here for the purposes of documentation only. It is a stand-in
// for any Go type, but represents the same type for any given function
// invocation.
type Type int

// Type1 is here for the purposes of documentation only. It is a stand-in
// for any Go type, but represents the same type for any given function
// invocation.
type Type1 int

// IntegerType is here for the purposes of documentation only. It is a stand-in
// for any integer type: int, uint, int8 etc.
type IntegerType int

// FloatType is here for the purposes of documentation only. It is a stand-in
// for either float type: float32 or float64.
type FloatType float32

// ComplexType is here for the purposes of documentation only. It is a
// stand-in for either complex type: complex64 or complex128.
type ComplexType complex64

// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
//	slice = append(slice, elem1, elem2)
//	slice = append(slice, anotherSlice...)
// As a special case, it is legal to append a string to a byte slice, like this:
//	slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type

// The copy built-in function copies elements from a source slice into a
// destination slice. (As a special case, it also will copy bytes from a
// string to a slice of bytes.) The source and destination may overlap. Copy
// returns the number of elements copied, which will be the minimum of
// len(src) and len(dst).
func copy(dst, src []Type) int

// The delete built-in function deletes the element with the specified key
// (m[key]) from the map. If m is nil or there is no such element, delete
// is a no-op.
func delete(m map[Type]Type1, key Type)

// The len built-in function returns the length of v, according to its type:
//	Array: the number of elements in v.
//	Pointer to array: the number of elements in *v (even if v is nil).
//	Slice, or map: the number of elements in v; if v is nil, len(v) is zero.
//	String: the number of bytes in v.
//	Channel: the number of elements queued (unread) in the channel buffer;
//	if v is nil, len(v) is zero.
func len(v Type) int

// The cap built-in function returns the capacity of v, according to its type:
//	Array: the number of elements in v (same as len(v)).
//	Pointer to array: the number of elements in *v (same as len(v)).
//	Slice: the maximum length the slice can reach when resliced;
//	if v is nil, cap(v) is zero.
//	Channel: the channel buffer capacity, in units of elements;
//	if v is nil, cap(v) is zero.
func cap(v Type) int

// The make built-in function allocates and initializes an object of type
// slice, map, or chan (only). Like new, the first argument is a type, not a
// value. Unlike new, make's return type is the same as the type of its
// argument, not a pointer to it. The specification of the result depends on
// the type:
//	Slice: The size specifies the length. The capacity of the slice is
//	equal to its length. A second integer argument may be provided to
//	specify a different capacity; it must be no smaller than the
//	length, so make([]int, 0, 10) allocates a slice of length 0 and
//	capacity 10.
//	Map: An empty map is allocated with enough space to hold the
//	specified number of elements. The size may be omitted, in which case
//	a small starting size is allocated.
//	Channel: The channel's buffer is initialized with the specified
//	buffer capacity. If zero, or the size is omitted, the channel is
//	unbuffered.
func make(t Type, size ...IntegerType) Type

// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type

// The complex built-in function constructs a complex value from two
// floating-point values. The real and imaginary parts must be of the same
// size, either float32 or float64 (or assignable to them), and the return
// value will be the corresponding complex type (complex64 for float32,
// complex128 for float64).
func complex(r, i FloatType) ComplexType

// The real built-in function returns the real part of the complex number c.
// The return value will be floating point type corresponding to the type of c.
func real(c ComplexType) FloatType

// The imag built-in function returns the imaginary part of the complex
// number c. The return value will be floating point type corresponding to
// the type of c.
func imag(c ComplexType) FloatType

// The close built-in function closes a channel, which must be either
// bidirectional or send-only. It should be executed only by the sender,
// never the receiver, and has the effect of shutting down the channel after
// the last sent value is received. After the last value has been received
// from a closed channel c, any receive from c will succeed without
// blocking, returning the zero value for the channel element. The form
//	x, ok := <-c
// will also set ok to false for a closed channel.
func close(c chan<- Type)

// The panic built-in function stops normal execution of the current
// goroutine. When a function F calls panic, normal execution of F stops
// immediately. Any functions whose execution was deferred by F are run in
// the usual way, and then F returns to its caller. To the caller G, the
// invocation of F then behaves like a call to panic, terminating G's
// execution and running any deferred functions. This continues until all
// functions in the executing goroutine have stopped, in reverse order. At
// that point, the program is terminated and the error condition is reported,
// including the value of the argument to panic. This termination sequence
// is called panicking and can be controlled by the built-in function
// recover.
func panic(v interface{})

// The recover built-in function allows a program to manage behavior of a
// panicking goroutine. Executing a call to recover inside a deferred
// function (but not any function called by it) stops the panicking sequence
// by restoring normal execution and retrieves the error value passed to the
// call of panic. If recover is called outside the deferred function it will
// not stop a panicking sequence. In this case, or when the goroutine is not
// panicking, or if the argument supplied to panic was nil, recover returns
// nil. Thus the return value from recover reports whether the goroutine is
// panicking.
func recover() interface{}

// The print built-in function formats its arguments in an
// implementation-specific way and writes the result to standard error.
// Print is useful for bootstrapping and debugging; it is not guaranteed
// to stay in the language.
func print(args ...Type)

// The println built-in function formats its arguments in an
// implementation-specific way and writes the result to standard error.
// Spaces are always added between arguments and a newline is appended.
// Println is useful for bootstrapping and debugging; it is not guaranteed
// to stay in the language.
func println(args ...Type)

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
	Error() string
}
<commit_msg>builtin: improve docs for make slice<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
	Package builtin provides documentation for Go's predeclared identifiers.
	The items documented here are not actually in package builtin
	but their descriptions here allow godoc to present documentation
	for the language's special identifiers.
*/
package builtin

// bool is the set of boolean values, true and false.
type bool bool

// true and false are the two untyped boolean values.
const (
	true  = 0 == 0 // Untyped bool.
	false = 0 != 0 // Untyped bool.
)

// uint8 is the set of all unsigned 8-bit integers.
// Range: 0 through 255.
type uint8 uint8

// uint16 is the set of all unsigned 16-bit integers.
// Range: 0 through 65535.
type uint16 uint16

// uint32 is the set of all unsigned 32-bit integers.
// Range: 0 through 4294967295.
type uint32 uint32

// uint64 is the set of all unsigned 64-bit integers.
// Range: 0 through 18446744073709551615.
type uint64 uint64

// int8 is the set of all signed 8-bit integers.
// Range: -128 through 127.
type int8 int8

// int16 is the set of all signed 16-bit integers.
// Range: -32768 through 32767.
type int16 int16

// int32 is the set of all signed 32-bit integers.
// Range: -2147483648 through 2147483647.
type int32 int32

// int64 is the set of all signed 64-bit integers.
// Range: -9223372036854775808 through 9223372036854775807.
type int64 int64

// float32 is the set of all IEEE-754 32-bit floating-point numbers.
type float32 float32

// float64 is the set of all IEEE-754 64-bit floating-point numbers.
type float64 float64

// complex64 is the set of all complex numbers with float32 real and
// imaginary parts.
type complex64 complex64

// complex128 is the set of all complex numbers with float64 real and
// imaginary parts.
type complex128 complex128

// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string

// int is a signed integer type that is at least 32 bits in size. It is a
// distinct type, however, and not an alias for, say, int32.
type int int

// uint is an unsigned integer type that is at least 32 bits in size. It is a
// distinct type, however, and not an alias for, say, uint32.
type uint uint

// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
type byte = uint8

// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32

// iota is a predeclared identifier representing the untyped integer ordinal
// number of the current const specification in a (usually parenthesized)
// const declaration. It is zero-indexed.
const iota = 0 // Untyped int.

// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

// Type is here for the purposes of documentation only. It is a stand-in
// for any Go type, but represents the same type for any given function
// invocation.
type Type int

// Type1 is here for the purposes of documentation only. It is a stand-in
// for any Go type, but represents the same type for any given function
// invocation.
type Type1 int

// IntegerType is here for the purposes of documentation only. It is a stand-in
// for any integer type: int, uint, int8 etc.
type IntegerType int

// FloatType is here for the purposes of documentation only. It is a stand-in
// for either float type: float32 or float64.
type FloatType float32

// ComplexType is here for the purposes of documentation only. It is a
// stand-in for either complex type: complex64 or complex128.
type ComplexType complex64

// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
//	slice = append(slice, elem1, elem2)
//	slice = append(slice, anotherSlice...)
// As a special case, it is legal to append a string to a byte slice, like this:
//	slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type

// The copy built-in function copies elements from a source slice into a
// destination slice. (As a special case, it also will copy bytes from a
// string to a slice of bytes.) The source and destination may overlap. Copy
// returns the number of elements copied, which will be the minimum of
// len(src) and len(dst).
func copy(dst, src []Type) int

// The delete built-in function deletes the element with the specified key
// (m[key]) from the map. If m is nil or there is no such element, delete
// is a no-op.
func delete(m map[Type]Type1, key Type)

// The len built-in function returns the length of v, according to its type:
//	Array: the number of elements in v.
//	Pointer to array: the number of elements in *v (even if v is nil).
//	Slice, or map: the number of elements in v; if v is nil, len(v) is zero.
//	String: the number of bytes in v.
//	Channel: the number of elements queued (unread) in the channel buffer;
//	if v is nil, len(v) is zero.
func len(v Type) int

// The cap built-in function returns the capacity of v, according to its type:
//	Array: the number of elements in v (same as len(v)).
//	Pointer to array: the number of elements in *v (same as len(v)).
//	Slice: the maximum length the slice can reach when resliced;
//	if v is nil, cap(v) is zero.
//	Channel: the channel buffer capacity, in units of elements;
//	if v is nil, cap(v) is zero.
func cap(v Type) int

// The make built-in function allocates and initializes an object of type
// slice, map, or chan (only). Like new, the first argument is a type, not a
// value. Unlike new, make's return type is the same as the type of its
// argument, not a pointer to it. The specification of the result depends on
// the type:
//	Slice: The size specifies the length. The capacity of the slice is
//	equal to its length. A second integer argument may be provided to
//	specify a different capacity; it must be no smaller than the
//	length. For example, make([]int, 0, 10) allocates an underlying array
//	of size 10 and returns a slice of length 0 and capacity 10 that is
//	backed by this underlying array.
//	Map: An empty map is allocated with enough space to hold the
//	specified number of elements. The size may be omitted, in which case
//	a small starting size is allocated.
//	Channel: The channel's buffer is initialized with the specified
//	buffer capacity. If zero, or the size is omitted, the channel is
//	unbuffered.
func make(t Type, size ...IntegerType) Type

// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type

// The complex built-in function constructs a complex value from two
// floating-point values. The real and imaginary parts must be of the same
// size, either float32 or float64 (or assignable to them), and the return
// value will be the corresponding complex type (complex64 for float32,
// complex128 for float64).
func complex(r, i FloatType) ComplexType

// The real built-in function returns the real part of the complex number c.
// The return value will be floating point type corresponding to the type of c.
func real(c ComplexType) FloatType

// The imag built-in function returns the imaginary part of the complex
// number c. The return value will be floating point type corresponding to
// the type of c.
func imag(c ComplexType) FloatType

// The close built-in function closes a channel, which must be either
// bidirectional or send-only. It should be executed only by the sender,
// never the receiver, and has the effect of shutting down the channel after
// the last sent value is received. After the last value has been received
// from a closed channel c, any receive from c will succeed without
// blocking, returning the zero value for the channel element. The form
//	x, ok := <-c
// will also set ok to false for a closed channel.
func close(c chan<- Type)

// The panic built-in function stops normal execution of the current
// goroutine. When a function F calls panic, normal execution of F stops
// immediately. Any functions whose execution was deferred by F are run in
// the usual way, and then F returns to its caller. To the caller G, the
// invocation of F then behaves like a call to panic, terminating G's
// execution and running any deferred functions. This continues until all
// functions in the executing goroutine have stopped, in reverse order. At
// that point, the program is terminated and the error condition is reported,
// including the value of the argument to panic. This termination sequence
// is called panicking and can be controlled by the built-in function
// recover.
func panic(v interface{})

// The recover built-in function allows a program to manage behavior of a
// panicking goroutine. Executing a call to recover inside a deferred
// function (but not any function called by it) stops the panicking sequence
// by restoring normal execution and retrieves the error value passed to the
// call of panic. If recover is called outside the deferred function it will
// not stop a panicking sequence. In this case, or when the goroutine is not
// panicking, or if the argument supplied to panic was nil, recover returns
// nil. Thus the return value from recover reports whether the goroutine is
// panicking.
func recover() interface{}

// The print built-in function formats its arguments in an
// implementation-specific way and writes the result to standard error.
// Print is useful for bootstrapping and debugging; it is not guaranteed
// to stay in the language.
func print(args ...Type)

// The println built-in function formats its arguments in an
// implementation-specific way and writes the result to standard error.
// Spaces are always added between arguments and a newline is appended.
// Println is useful for bootstrapping and debugging; it is not guaranteed
// to stay in the language.
func println(args ...Type)

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
	Error() string
}
<|endoftext|>


<commit_before>package triggers

type Donators struct{}

func (d *Donators) Triggers() []string {
	return []string{
		"donators",
		"donations",
		"donate",
		"supporters",
		"support",
		"patreon",
		"patreons",
		"credits",
	}
}

func (d *Donators) Response(trigger string, content string) string {
	return "<:robyulblush:327206930437373952> **These awesome people support me:**\nKakkela 💕\nSunny 💓\nsomicidal minaiac 💞\nOokami 🖤\nKeldra 💗\nTN 💝\nseulguille 💘\nSlenn 💜\nFugu ❣\nWoori 💞\nhikari 💙\nAshton 💖\nKay 💝\njamie 💓\nHomeboywill 💘\nRimbol 💕\nGenisphere 💖\nekgus 💗\nCPark 💞\njungoo 💕\nShawn 💗\nThank you so much!\n_You want to be in this list? <https://www.patreon.com/sekl>!_"
}
<commit_msg>[donators] adds SaltiestPeach!<commit_after>package triggers

type Donators struct{}

func (d *Donators) Triggers() []string {
	return []string{
		"donators",
		"donations",
		"donate",
		"supporters",
		"support",
		"patreon",
		"patreons",
		"credits",
	}
}

func (d *Donators) Response(trigger string, content string) string {
	return "<:robyulblush:327206930437373952> **These awesome people support me:**\nKakkela 💕\nSunny 💓\nsomicidal minaiac 💞\nOokami 🖤\nKeldra 💗\nTN 💝\nseulguille 💘\nSlenn 💜\nFugu ❣\nWoori 💞\nhikari 💙\nAshton 💖\nKay 💝\njamie 💓\nHomeboywill 💘\nRimbol 💕\nGenisphere 💖\nekgus 💗\nCPark 💞\njungoo 💕\nShawn 💗\nSaltiestPeach 💘\nThank you so much!\n_You want to be in this list? <https://www.patreon.com/sekl>!_"
}
<|endoftext|>


<commit_before>package baja

import (
	"log"

	//"bytes"
	"fmt"
	"gopkg.in/yaml.v2"
	"html/template"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

//TODO Move theme reference to config with DI
func Compile(source string) error {
	page := buildPage()

	createRSS(page)
	createHome(page)
	createIssues(page)

	CopyDir("themes/yeo/assets", "public/assets")
	CopyDir("static/", "public/")

	return nil
}

func createRSS(page Page) {
	f, err := os.Create("./public/rss.xml")
	if err != nil {
		log.Println("Error creating file", err)
	}

	funcMap := template.FuncMap{
		"attr": func(s string) template.HTMLAttr {
			return template.HTMLAttr(s)
		},
		"safe": func(s string) template.HTML {
			return template.HTML(s)
		},
	}

	t := template.Must(template.New("").Funcs(funcMap).ParseFiles("themes/yeo/rss.tmpl"))
	if err := t.ExecuteTemplate(f, "base", &page); err != nil {
		log.Fatal(err)
	}
}
func createHome(page Page) {
	t, err := template.ParseFiles("themes/yeo/layout.tmpl", "themes/yeo/index.tmpl")
	if err != nil {
		log.Fatal(err)
	}

	f, err := os.Create("./public/index.html")
	if err != nil {
		log.Println("Error creating file", err)
	}

	//var tpl bytes.Buffer
	if err := t.ExecuteTemplate(f, "base", &page); err != nil {
		log.Fatal(err)
	}
}

func createIssue(layout string, issue Issue) {
	t, err := template.ParseFiles("themes/yeo/"+layout+".tmpl", "themes/yeo/issue.tmpl")
	if err != nil {
		log.Fatal(err)
	}

	directory := "./public/issues/" + issue.Name
	if issue.Draft == true {
		directory = "./public/issues/" + issue.Name + "/draft"
	}

	if layout == "email" {
		directory = "./public/issues/" + issue.Name + "/email"
	}
	os.MkdirAll(directory, 0755)

	f, err := os.Create(directory + "/index.html")
	if err != nil {
		log.Fatalf("Error creating file", err)
	}

	page := &Page{
		Time:  time.Now(),
		Issue: issue,
	}
	//var tpl bytes.Buffer
	if err := t.ExecuteTemplate(f, "base", &page); err != nil {
		log.Fatal(err)
	}

}

func buildPage() Page {
	page := Page{
		Time: time.Now(),
	}

	var issues []Issue
	files, _ := ioutil.ReadDir("./content/issues/")
	for _, f := range files {
		if issue, err := loadIssue(f); err == nil {
			issues = append(issues, issue)
		}
	}

	// Sort Issue
	sort.Sort(Issues(issues))

	page.Issues = issues
	for k := len(page.Issues) - 1; k >= 0; k-- {
		page.Issue = page.Issues[k]
		if page.Issue.Draft == false {
			break
		}
	}
	return page
}

func createIssues(page Page) {
	t, err := template.ParseFiles("themes/yeo/layout.tmpl", "themes/yeo/issues.tmpl")
	if err != nil {
		log.Fatal(err)
	}

	os.Mkdir("./public/issues", 0755)
	f, err := os.Create("./public/issues/index.html")
	if err != nil {
		log.Println("Error creating file", err)
	}

	var publicableIssue Issues
	for _, issue := range page.Issues {
		if issue.Draft == false {
			publicableIssue = append(publicableIssue, issue)
		}

		createIssue("layout", issue)
		createIssue("email", issue)
	}

	//var tpl bytes.Buffer
	page.Issues = publicableIssue
	if err := t.ExecuteTemplate(f, "base", &page); err != nil {
		log.Fatal(err)
	}
}

func loadIssue(f os.FileInfo) (Issue, error) {
	name := strings.Split(f.Name(), ".")
	issue := Issue{
		Name: name[0],
	}

	data, err := ioutil.ReadFile("./content/issues/" + f.Name())
	if err != nil {
		log.Fatal("Fail to parse issue", f.Name(), err)
		return issue, err
	}

	err = yaml.Unmarshal([]byte(data), &issue)
	if err != nil {
		log.Println("Error unmarshal yaml", err)
	}
	return issue, nil
}

func CopyFile(src, dst string) (err error) {
	in, err := os.Open(src)
	if err != nil {
		return
	}
	defer in.Close()

	out, err := os.Create(dst)
	if err != nil {
		return
	}
	defer func() {
		if e := out.Close(); e != nil {
			err = e
		}
	}()

	_, err = io.Copy(out, in)
	if err != nil {
		return
	}

	err = out.Sync()
	if err != nil {
		return
	}

	si, err := os.Stat(src)
	if err != nil {
		return
	}
	err = os.Chmod(dst, si.Mode())
	if err != nil {
		return
	}

	return
}

// CopyDir recursively copies a directory tree, attempting to preserve permissions.
// Source directory must exist, destination directory must *not* exist.
// Symlinks are ignored and skipped.
func CopyDir(src string, dst string) (err error) {
	src = filepath.Clean(src)
	dst = filepath.Clean(dst)

	si, err := os.Stat(src)
	if err != nil {
		return err
	}
	if !si.IsDir() {
		return fmt.Errorf("source is not a directory")
	}

	_, err = os.Stat(dst)
	if err != nil && !os.IsNotExist(err) {
		return
	}
	//if err == nil {
	//		return fmt.Errorf("destination already exists")
	//}

	err = os.MkdirAll(dst, si.Mode())
	if err != nil {
		return
	}

	entries, err := ioutil.ReadDir(src)
	if err != nil {
		return
	}

	for _, entry := range entries {
		srcPath := filepath.Join(src, entry.Name())
		dstPath := filepath.Join(dst, entry.Name())

		if entry.IsDir() {
			err = CopyDir(srcPath, dstPath)
			if err != nil {
				return
			}
		} else {
			// Skip symlinks.
			if entry.Mode()&os.ModeSymlink != 0 {
				continue
			}

			err = CopyFile(srcPath, dstPath)
			if err != nil {
				return
			}
		}
	}

	return
}
<commit_msg>Add pub time<commit_after>package baja

import (
	"log"

	//"bytes"
	"fmt"
	"gopkg.in/yaml.v2"
	"html/template"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

//TODO Move theme reference to config with DI
func Compile(source string) error {
	page := buildPage()

	createRSS(page)
	createHome(page)
	createIssues(page)

	CopyDir("themes/yeo/assets", "public/assets")
	CopyDir("static/", "public/")

	return nil
}

func createRSS(page Page) {
	f, err := os.Create("./public/rss.xml")
	if err != nil {
		log.Println("Error creating file", err)
	}

	funcMap := template.FuncMap{
		"attr": func(s string) template.HTMLAttr {
			return template.HTMLAttr(s)
		},
		"safe": func(s string) template.HTML {
			return template.HTML(s)
		},
	}

	t := template.Must(template.New("").Funcs(funcMap).ParseFiles("themes/yeo/rss.tmpl"))
	if err := t.ExecuteTemplate(f, "base", &page); err != nil {
		log.Fatal(err)
	}
}
func createHome(page Page) {
	t, err := template.ParseFiles("themes/yeo/layout.tmpl", "themes/yeo/index.tmpl")
	if err != nil {
		log.Fatal(err)
	}

	f, err := os.Create("./public/index.html")
	if err != nil {
		log.Println("Error creating file", err)
	}

	//var tpl bytes.Buffer
	if err := t.ExecuteTemplate(f, "base", &page); err != nil {
		log.Fatal(err)
	}
}

func createIssue(layout string, issue Issue) {
	t, err := template.ParseFiles("themes/yeo/"+layout+".tmpl", "themes/yeo/issue.tmpl")
	if err != nil {
		log.Fatal(err)
	}

	directory := "./public/issues/" + issue.Name
	if issue.Draft == true {
		directory = "./public/issues/" + issue.Name + "/draft"
	}

	if layout == "email" {
		directory = "./public/issues/" + issue.Name + "/email"
	}
	os.MkdirAll(directory, 0755)

	f, err := os.Create(directory + "/index.html")
	if err != nil {
		log.Fatalf("Error creating file", err)
	}

	page := &Page{
		Time:  time.Now(),
		Issue: issue,
	}
	//var tpl bytes.Buffer
	if err := t.ExecuteTemplate(f, "base", &page); err != nil {
		log.Fatal(err)
	}

}

func buildPage() Page {
	page := Page{
		Time: time.Now(),
	}

	var issues []Issue
	files, _ := ioutil.ReadDir("./content/issues/")
	for _, f := range files {
		if issue, err := loadIssue(f); err == nil {
			issues = append(issues, issue)
		}
	}

	// Sort Issue
	sort.Sort(Issues(issues))

	page.Issues = issues
	for k := len(page.Issues) - 1; k >= 0; k-- {
		page.Issue = page.Issues[k]
		if page.Issue.Draft == false {
			break
		}
	}
	return page
}

func createIssues(page Page) {
	t, err := template.ParseFiles("themes/yeo/layout.tmpl", "themes/yeo/issues.tmpl")
	if err != nil {
		log.Fatal(err)
	}

	os.Mkdir("./public/issues", 0755)
	f, err := os.Create("./public/issues/index.html")
	if err != nil {
		log.Println("Error creating file", err)
	}

	var publicableIssue Issues
	for _, issue := range page.Issues {
		if issue.Draft == false {
			publicableIssue = append(publicableIssue, issue)
		}

		createIssue("layout", issue)
		createIssue("email", issue)
	}

	//var tpl bytes.Buffer
	page.Issues = publicableIssue
	if err := t.ExecuteTemplate(f, "base", &page); err != nil {
		log.Fatal(err)
	}
}

func loadIssue(f os.FileInfo) (Issue, error) {
	name := strings.Split(f.Name(), ".")
	issue := Issue{
		Name: name[0],
	}

	data, err := ioutil.ReadFile("./content/issues/" + f.Name())
	if err != nil {
		log.Fatal("Fail to parse issue", f.Name(), err)
		return issue, err
	}

	err = yaml.Unmarshal([]byte(data), &issue)
	if err != nil {
		log.Println("Error unmarshal yaml", err)
	}

	if issue.PubTime, err = time.Parse("Jan 2, 2006 15:04:05 -0700", issue.Time+" 05:19:00 -0700"); err != nil {
		log.Fatal("Cannot parse time", err)
	}
	return issue, nil
}

func CopyFile(src, dst string) (err error) {
	in, err := os.Open(src)
	if err != nil {
		return
	}
	defer in.Close()

	out, err := os.Create(dst)
	if err != nil {
		return
	}
	defer func() {
		if e := out.Close(); e != nil {
			err = e
		}
	}()

	_, err = io.Copy(out, in)
	if err != nil {
		return
	}

	err = out.Sync()
	if err != nil {
		return
	}

	si, err := os.Stat(src)
	if err != nil {
		return
	}
	err = os.Chmod(dst, si.Mode())
	if err != nil {
		return
	}

	return
}

// CopyDir recursively copies a directory tree, attempting to preserve permissions.
// Source directory must exist, destination directory must *not* exist.
// Symlinks are ignored and skipped.
func CopyDir(src string, dst string) (err error) {
	src = filepath.Clean(src)
	dst = filepath.Clean(dst)

	si, err := os.Stat(src)
	if err != nil {
		return err
	}
	if !si.IsDir() {
		return fmt.Errorf("source is not a directory")
	}

	_, err = os.Stat(dst)
	if err != nil && !os.IsNotExist(err) {
		return
	}
	//if err == nil {
	//		return fmt.Errorf("destination already exists")
	//}

	err = os.MkdirAll(dst, si.Mode())
	if err != nil {
		return
	}

	entries, err := ioutil.ReadDir(src)
	if err != nil {
		return
	}

	for _, entry := range entries {
		srcPath := filepath.Join(src, entry.Name())
		dstPath := filepath.Join(dst, entry.Name())

		if entry.IsDir() {
			err = CopyDir(srcPath, dstPath)
			if err != nil {
				return
			}
		} else {
			// Skip symlinks.
			if entry.Mode()&os.ModeSymlink != 0 {
				continue
			}

			err = CopyFile(srcPath, dstPath)
			if err != nil {
				return
			}
		}
	}

	return
}
<|endoftext|>