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

package config

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	yaml "gopkg.in/yaml.v2"
)

// BasicAuth contains basic HTTP authentication credentials.
type BasicAuth struct {
	Username string `yaml:"username"`
	Password Secret `yaml:"password"`

	// Catches all undefined fields and must be empty after parsing.
	XXX map[string]interface{} `yaml:",inline"`
}

// URL is a custom URL type that allows validation at configuration load time.
type URL struct {
	*url.URL
}

// UnmarshalYAML implements the yaml.Unmarshaler interface for URLs.
func (u *URL) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	if err := unmarshal(&s); err != nil {
		return err
	}

	urlp, err := url.Parse(s)
	if err != nil {
		return err
	}
	u.URL = urlp
	return nil
}

// MarshalYAML implements the yaml.Marshaler interface for URLs.
func (u URL) MarshalYAML() (interface{}, error) {
	if u.URL != nil {
		return u.String(), nil
	}
	return nil, nil
}

// HTTPClientConfig configures an HTTP client.
type HTTPClientConfig struct {
	// The HTTP basic authentication credentials for the targets.
	BasicAuth *BasicAuth `yaml:"basic_auth,omitempty"`
	// The bearer token for the targets.
	BearerToken Secret `yaml:"bearer_token,omitempty"`
	// The bearer token file for the targets.
	BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
	// HTTP proxy server to use to connect to the targets.
	ProxyURL URL `yaml:"proxy_url,omitempty"`
	// TLSConfig to use to connect to the targets.
	TLSConfig TLSConfig `yaml:"tls_config,omitempty"`

	// Catches all undefined fields and must be empty after parsing.
	XXX map[string]interface{} `yaml:",inline"`
}

func (c *HTTPClientConfig) validate() error {
	if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 {
		return fmt.Errorf("at most one of bearer_token & bearer_token_file must be configured")
	}
	if c.BasicAuth != nil && (len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0) {
		return fmt.Errorf("at most one of basic_auth, bearer_token & bearer_token_file must be configured")
	}
	return nil
}

// UnmarshalYAML implements the yaml.Unmarshaler interface
func (c *HTTPClientConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type plain HTTPClientConfig
	err := unmarshal((*plain)(c))
	if err != nil {
		return err
	}
	err = c.validate()
	if err != nil {
		return c.validate()
	}
	return checkOverflow(c.XXX, "http_client_config")
}

// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (a *BasicAuth) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type plain BasicAuth
	err := unmarshal((*plain)(a))
	if err != nil {
		return err
	}
	return checkOverflow(a.XXX, "basic_auth")
}

// NewHTTPClientFromConfig returns a new HTTP client configured for the
// given config.HTTPClientConfig.
func NewHTTPClientFromConfig(cfg *HTTPClientConfig) (*http.Client, error) {
	tlsConfig, err := NewTLSConfig(&cfg.TLSConfig)
	if err != nil {
		return nil, err
	}

	// It's the caller's job to handle timeouts
	var rt http.RoundTripper = &http.Transport{
		Proxy:             http.ProxyURL(cfg.ProxyURL.URL),
		DisableKeepAlives: true,
		TLSClientConfig:   tlsConfig,
	}

	// If a bearer token is provided, create a round tripper that will set the
	// Authorization header correctly on each request.
	bearerToken := cfg.BearerToken
	if len(bearerToken) == 0 && len(cfg.BearerTokenFile) > 0 {
		b, err := ioutil.ReadFile(cfg.BearerTokenFile)
		if err != nil {
			return nil, fmt.Errorf("unable to read bearer token file %s: %s", cfg.BearerTokenFile, err)
		}
		bearerToken = Secret(strings.TrimSpace(string(b)))
	}

	if len(bearerToken) > 0 {
		rt = NewBearerAuthRoundTripper(bearerToken, rt)
	}

	if cfg.BasicAuth != nil {
		rt = NewBasicAuthRoundTripper(cfg.BasicAuth.Username, Secret(cfg.BasicAuth.Password), rt)
	}

	// Return a new client with the configured round tripper.
	return &http.Client{Transport: rt}, nil
}

type bearerAuthRoundTripper struct {
	bearerToken Secret
	rt          http.RoundTripper
}

type basicAuthRoundTripper struct {
	username string
	password Secret
	rt       http.RoundTripper
}

// NewBasicAuthRoundTripper will apply a BASIC auth authorization header to a request unless it has
// already been set.
func NewBasicAuthRoundTripper(username string, password Secret, rt http.RoundTripper) http.RoundTripper {
	return &basicAuthRoundTripper{username, password, rt}
}

func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	if len(req.Header.Get("Authorization")) == 0 {
		req = cloneRequest(req)
		req.Header.Set("Authorization", "Bearer "+string(rt.bearerToken))
	}

	return rt.rt.RoundTrip(req)
}

// NewBearerAuthRoundTripper adds the provided bearer token to a request unless the authorization
// header has already been set.
func NewBearerAuthRoundTripper(bearer Secret, rt http.RoundTripper) http.RoundTripper {
	return &bearerAuthRoundTripper{bearer, rt}
}

func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	if len(req.Header.Get("Authorization")) != 0 {
		return rt.RoundTrip(req)
	}
	req = cloneRequest(req)
	req.SetBasicAuth(rt.username, string(rt.password))
	return rt.rt.RoundTrip(req)
}

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
	// Shallow copy of the struct.
	r2 := new(http.Request)
	*r2 = *r
	// Deep copy of the Header.
	r2.Header = make(http.Header)
	for k, s := range r.Header {
		r2.Header[k] = s
	}
	return r2
}

// NewTLSConfig creates a new tls.Config from the given config.TLSConfig.
func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) {
	tlsConfig := &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify}

	// If a CA cert is provided then let's read it in so we can validate the
	// scrape target's certificate properly.
	if len(cfg.CAFile) > 0 {
		caCertPool := x509.NewCertPool()
		// Load CA cert.
		caCert, err := ioutil.ReadFile(cfg.CAFile)
		if err != nil {
			return nil, fmt.Errorf("unable to use specified CA cert %s: %s", cfg.CAFile, err)
		}
		caCertPool.AppendCertsFromPEM(caCert)
		tlsConfig.RootCAs = caCertPool
	}

	if len(cfg.ServerName) > 0 {
		tlsConfig.ServerName = cfg.ServerName
	}

	// If a client cert & key is provided then configure TLS config accordingly.
	if len(cfg.CertFile) > 0 && len(cfg.KeyFile) == 0 {
		return nil, fmt.Errorf("client cert file %q specified without client key file", cfg.CertFile)
	} else if len(cfg.KeyFile) > 0 && len(cfg.CertFile) == 0 {
		return nil, fmt.Errorf("client key file %q specified without client cert file", cfg.KeyFile)
	} else if len(cfg.CertFile) > 0 && len(cfg.KeyFile) > 0 {
		cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile)
		if err != nil {
			return nil, fmt.Errorf("unable to use specified client cert (%s) & key (%s): %s", cfg.CertFile, cfg.KeyFile, err)
		}
		tlsConfig.Certificates = []tls.Certificate{cert}
	}
	tlsConfig.BuildNameToCertificate()

	return tlsConfig, nil
}

// TLSConfig configures the options for TLS connections.
type TLSConfig struct {
	// The CA cert to use for the targets.
	CAFile string `yaml:"ca_file,omitempty"`
	// The client cert file for the targets.
	CertFile string `yaml:"cert_file,omitempty"`
	// The client key file for the targets.
	KeyFile string `yaml:"key_file,omitempty"`
	// Used to verify the hostname for the targets.
	ServerName string `yaml:"server_name,omitempty"`
	// Disable target certificate validation.
	InsecureSkipVerify bool `yaml:"insecure_skip_verify"`

	// Catches all undefined fields and must be empty after parsing.
	XXX map[string]interface{} `yaml:",inline"`
}

// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (c *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type plain TLSConfig
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}
	return checkOverflow(c.XXX, "TLS config")
}

func (c HTTPClientConfig) String() string {
	b, err := yaml.Marshal(c)
	if err != nil {
		return fmt.Sprintf("<error creating http client config string: %s>", err)
	}
	return string(b)
}
<commit_msg>export HTTPClientConfig's validate() method<commit_after>// Copyright 2016 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"gopkg.in/yaml.v2"
)

// BasicAuth contains basic HTTP authentication credentials.
type BasicAuth struct {
	Username string `yaml:"username"`
	Password Secret `yaml:"password"`

	// Catches all undefined fields and must be empty after parsing.
	XXX map[string]interface{} `yaml:",inline"`
}

// URL is a custom URL type that allows validation at configuration load time.
type URL struct {
	*url.URL
}

// UnmarshalYAML implements the yaml.Unmarshaler interface for URLs.
func (u *URL) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	if err := unmarshal(&s); err != nil {
		return err
	}

	urlp, err := url.Parse(s)
	if err != nil {
		return err
	}
	u.URL = urlp
	return nil
}

// MarshalYAML implements the yaml.Marshaler interface for URLs.
func (u URL) MarshalYAML() (interface{}, error) {
	if u.URL != nil {
		return u.String(), nil
	}
	return nil, nil
}

// HTTPClientConfig configures an HTTP client.
type HTTPClientConfig struct {
	// The HTTP basic authentication credentials for the targets.
	BasicAuth *BasicAuth `yaml:"basic_auth,omitempty"`
	// The bearer token for the targets.
	BearerToken Secret `yaml:"bearer_token,omitempty"`
	// The bearer token file for the targets.
	BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
	// HTTP proxy server to use to connect to the targets.
	ProxyURL URL `yaml:"proxy_url,omitempty"`
	// TLSConfig to use to connect to the targets.
	TLSConfig TLSConfig `yaml:"tls_config,omitempty"`

	// Catches all undefined fields and must be empty after parsing.
	XXX map[string]interface{} `yaml:",inline"`
}

// Validate validates the HTTPClientConfig to check only one of BearerToken,
// BasicAuth and BearerTokenFile is configured.
func (c *HTTPClientConfig) Validate() error {
	if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 {
		return fmt.Errorf("at most one of bearer_token & bearer_token_file must be configured")
	}
	if c.BasicAuth != nil && (len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0) {
		return fmt.Errorf("at most one of basic_auth, bearer_token & bearer_token_file must be configured")
	}
	return nil
}

// UnmarshalYAML implements the yaml.Unmarshaler interface
func (c *HTTPClientConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type plain HTTPClientConfig
	err := unmarshal((*plain)(c))
	if err != nil {
		return err
	}
	err = c.Validate()
	if err != nil {
		return c.Validate()
	}
	return checkOverflow(c.XXX, "http_client_config")
}

// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (a *BasicAuth) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type plain BasicAuth
	err := unmarshal((*plain)(a))
	if err != nil {
		return err
	}
	return checkOverflow(a.XXX, "basic_auth")
}

// NewHTTPClientFromConfig returns a new HTTP client configured for the
// given config.HTTPClientConfig.
func NewHTTPClientFromConfig(cfg *HTTPClientConfig) (*http.Client, error) {
	tlsConfig, err := NewTLSConfig(&cfg.TLSConfig)
	if err != nil {
		return nil, err
	}

	// It's the caller's job to handle timeouts
	var rt http.RoundTripper = &http.Transport{
		Proxy:             http.ProxyURL(cfg.ProxyURL.URL),
		DisableKeepAlives: true,
		TLSClientConfig:   tlsConfig,
	}

	// If a bearer token is provided, create a round tripper that will set the
	// Authorization header correctly on each request.
	bearerToken := cfg.BearerToken
	if len(bearerToken) == 0 && len(cfg.BearerTokenFile) > 0 {
		b, err := ioutil.ReadFile(cfg.BearerTokenFile)
		if err != nil {
			return nil, fmt.Errorf("unable to read bearer token file %s: %s", cfg.BearerTokenFile, err)
		}
		bearerToken = Secret(strings.TrimSpace(string(b)))
	}

	if len(bearerToken) > 0 {
		rt = NewBearerAuthRoundTripper(bearerToken, rt)
	}

	if cfg.BasicAuth != nil {
		rt = NewBasicAuthRoundTripper(cfg.BasicAuth.Username, Secret(cfg.BasicAuth.Password), rt)
	}

	// Return a new client with the configured round tripper.
	return &http.Client{Transport: rt}, nil
}

type bearerAuthRoundTripper struct {
	bearerToken Secret
	rt          http.RoundTripper
}

type basicAuthRoundTripper struct {
	username string
	password Secret
	rt       http.RoundTripper
}

// NewBasicAuthRoundTripper will apply a BASIC auth authorization header to a request unless it has
// already been set.
func NewBasicAuthRoundTripper(username string, password Secret, rt http.RoundTripper) http.RoundTripper {
	return &basicAuthRoundTripper{username, password, rt}
}

func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	if len(req.Header.Get("Authorization")) == 0 {
		req = cloneRequest(req)
		req.Header.Set("Authorization", "Bearer "+string(rt.bearerToken))
	}

	return rt.rt.RoundTrip(req)
}

// NewBearerAuthRoundTripper adds the provided bearer token to a request unless the authorization
// header has already been set.
func NewBearerAuthRoundTripper(bearer Secret, rt http.RoundTripper) http.RoundTripper {
	return &bearerAuthRoundTripper{bearer, rt}
}

func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	if len(req.Header.Get("Authorization")) != 0 {
		return rt.RoundTrip(req)
	}
	req = cloneRequest(req)
	req.SetBasicAuth(rt.username, string(rt.password))
	return rt.rt.RoundTrip(req)
}

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
	// Shallow copy of the struct.
	r2 := new(http.Request)
	*r2 = *r
	// Deep copy of the Header.
	r2.Header = make(http.Header)
	for k, s := range r.Header {
		r2.Header[k] = s
	}
	return r2
}

// NewTLSConfig creates a new tls.Config from the given config.TLSConfig.
func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) {
	tlsConfig := &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify}

	// If a CA cert is provided then let's read it in so we can Validate the
	// scrape target's certificate properly.
	if len(cfg.CAFile) > 0 {
		caCertPool := x509.NewCertPool()
		// Load CA cert.
		caCert, err := ioutil.ReadFile(cfg.CAFile)
		if err != nil {
			return nil, fmt.Errorf("unable to use specified CA cert %s: %s", cfg.CAFile, err)
		}
		caCertPool.AppendCertsFromPEM(caCert)
		tlsConfig.RootCAs = caCertPool
	}

	if len(cfg.ServerName) > 0 {
		tlsConfig.ServerName = cfg.ServerName
	}

	// If a client cert & key is provided then configure TLS config accordingly.
	if len(cfg.CertFile) > 0 && len(cfg.KeyFile) == 0 {
		return nil, fmt.Errorf("client cert file %q specified without client key file", cfg.CertFile)
	} else if len(cfg.KeyFile) > 0 && len(cfg.CertFile) == 0 {
		return nil, fmt.Errorf("client key file %q specified without client cert file", cfg.KeyFile)
	} else if len(cfg.CertFile) > 0 && len(cfg.KeyFile) > 0 {
		cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile)
		if err != nil {
			return nil, fmt.Errorf("unable to use specified client cert (%s) & key (%s): %s", cfg.CertFile, cfg.KeyFile, err)
		}
		tlsConfig.Certificates = []tls.Certificate{cert}
	}
	tlsConfig.BuildNameToCertificate()

	return tlsConfig, nil
}

// TLSConfig configures the options for TLS connections.
type TLSConfig struct {
	// The CA cert to use for the targets.
	CAFile string `yaml:"ca_file,omitempty"`
	// The client cert file for the targets.
	CertFile string `yaml:"cert_file,omitempty"`
	// The client key file for the targets.
	KeyFile string `yaml:"key_file,omitempty"`
	// Used to verify the hostname for the targets.
	ServerName string `yaml:"server_name,omitempty"`
	// Disable target certificate validation.
	InsecureSkipVerify bool `yaml:"insecure_skip_verify"`

	// Catches all undefined fields and must be empty after parsing.
	XXX map[string]interface{} `yaml:",inline"`
}

// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (c *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type plain TLSConfig
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}
	return checkOverflow(c.XXX, "TLS config")
}

func (c HTTPClientConfig) String() string {
	b, err := yaml.Marshal(c)
	if err != nil {
		return fmt.Sprintf("<error creating http client config string: %s>", err)
	}
	return string(b)
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const HCLInput = `
listen {
  address = "10.0.0.1"
  port = 4040
}

consul {
  enable = true
  address = "localhost:8500"
  datacenter = "dc1"
  scheme = "https"
  token = "asdfasfdasf"

  service {
    id = "nginx-exporter"
    name = "nginx-exporter"
    tags = ["foo", "bar"]
  }
}

namespace "nginx" {
  source_files = [
    "test.log",
    "foo.log"
  ]
  format = "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$http_x_forwarded_for\""

  labels {
    app = "magicapp"
    foo = "bar"
  }

  relabel "user" {
    from = "remote_user"
    whitelist = ["-", "user1", "user2"]
  }

  relabel "request_uri" {
    from = "request"
    split = 2

    match "^/users/[0-9]+" {
      replacement = "/users/:id"
    }
    match "^/profile" {
      replacement = "/profile"
    }
  }
}
`

const YAMLInput = `
listen:
  address: "10.0.0.1"
  port: 4040

consul:
  enable: true
  address: "localhost:8500"
  datacenter: "dc1"
  scheme: "https"
  token: "asdfasfdasf"

  service:
    id: "nginx-exporter"
    name: "nginx-exporter"
    tags:
      - foo
      - bar

namespaces:
  - name: nginx
    source_files:
      - test.log
      - foo.log
    format: "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$http_x_forwarded_for\""
    labels:
      app: "magicapp"
      foo: "bar"
    relabel_configs:
      - target_label: user
        from: "remote_user"
        whitelist: ["-", "user1", "user2"]
      - target_label: request_uri
        from: request
        split: 2
        matches:
          - regexp: "^/users/[0-9]+"
            replacement: "/users/:id"
          - regexp: "^/profile"
            replacement: "/profile"
`

func assertConfigContents(t *testing.T, cfg Config) {
	assert.Equal(t, "10.0.0.1", cfg.Listen.Address)
	assert.Equal(t, 4040, cfg.Listen.Port)

	assert.True(t, cfg.Consul.Enable)
	assert.Equal(t, "localhost:8500", cfg.Consul.Address)
	assert.Equal(t, "nginx-exporter", cfg.Consul.Service.ID)
	assert.Equal(t, "nginx-exporter", cfg.Consul.Service.Name)
	assert.Equal(t, []string{"foo", "bar"}, cfg.Consul.Service.Tags)
	assert.Equal(t, "dc1", cfg.Consul.Datacenter)
	assert.Equal(t, "https", cfg.Consul.Scheme)
	assert.Equal(t, "asdfasfdasf", cfg.Consul.Token)

	require.Len(t, cfg.Namespaces, 1)

	n := cfg.Namespaces[0]
	assert.Equal(t, "nginx", n.Name)
	assert.Equal(t, "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$http_x_forwarded_for\"", n.Format)
	assert.Equal(t, []string{"test.log", "foo.log"}, n.SourceFiles)
	assert.Equal(t, "magicapp", n.Labels["app"])

	require.Len(t, n.RelabelConfigs, 2)
	assert.Equal(t, "user", n.RelabelConfigs[0].TargetLabel)
	assert.Equal(t, "request_uri", n.RelabelConfigs[1].TargetLabel)

	assert.Len(t, n.RelabelConfigs[1].Matches, 2)
	assert.Equal(t, "^/users/[0-9]+", n.RelabelConfigs[1].Matches[0].RegexpString)
}

func TestLoadsHCLConfigFile(t *testing.T) {
	t.Parallel()

	buf := bytes.NewBufferString(HCLInput)
	cfg := Config{}

	err := LoadConfigFromStream(&cfg, buf, TYPE_HCL)
	assert.Nil(t, err, "unexpected error: %v", err)
	assertConfigContents(t, cfg)
}

func TestLoadsYAMLConfigFile(t *testing.T) {
	t.Parallel()

	buf := bytes.NewBufferString(YAMLInput)
	cfg := Config{}

	err := LoadConfigFromStream(&cfg, buf, TYPE_YAML)
	assert.Nil(t, err, "unexpected error: %v", err)
	assertConfigContents(t, cfg)
}
<commit_msg>Update loader_test.go<commit_after>package config

import (
	"bytes"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const HCLInput = `
listen {
  address = "10.0.0.1"
  port = 4040
}

consul {
  enable = true
  address = "localhost:8500"
  datacenter = "dc1"
  scheme = "https"
  token = "asdfasfdasf"

  service {
    id = "nginx-exporter"
    name = "nginx-exporter"
    tags = ["foo", "bar"]
  }
}

namespace "nginx" {
  source_files = [
    "test.log",
    "foo.log"
  ]
  format = "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$http_x_forwarded_for\""

  labels {
    app = "magicapp"
    foo = "bar"
  }

  relabel "user" {
    from = "remote_user"
    whitelist = ["-", "user1", "user2"]
  }

  relabel "request_uri" {
    from = "request"
    split = 2

    match "^/users/[0-9]+" {
      replacement = "/users/:id"
    }
    match "^/profile" {
      replacement = "/profile"
    }
  }
}
`

const YAMLInput = `
listen:
  address: "10.0.0.1"
  port: 4040

consul:
  enable: true
  address: "localhost:8500"
  datacenter: "dc1"
  scheme: "https"
  token: "asdfasfdasf"

  service:
    id: "nginx-exporter"
    name: "nginx-exporter"
    tags:
      - foo
      - bar

namespaces:
  - name: nginx
    source_files:
      - test.log
      - foo.log
    format: "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$http_x_forwarded_for\""
    labels:
      app: "magicapp"
      foo: "bar"
    relabel_configs:
      - target_label: user
        from: "remote_user"
        whitelist: ["-", "user1", "user2"]
      - target_label: request_uri
        from: request
        split: 2
        matches:
          - regexp: "^/users/[0-9]+"
            replacement: "/users/:id"
          - regexp: "^/profile"
            replacement: "/profile"
`

func assertConfigContents(t *testing.T, cfg Config) {
	assert.Equal(t, "10.0.0.1", cfg.Listen.Address)
	assert.Equal(t, 4040, cfg.Listen.Port)

	assert.True(t, cfg.Consul.Enable)
	assert.Equal(t, "localhost:8500", cfg.Consul.Address)
	assert.Equal(t, "nginx-exporter", cfg.Consul.Service.ID)
	assert.Equal(t, "nginx-exporter", cfg.Consul.Service.Name)
	assert.Equal(t, []string{"foo", "bar"}, cfg.Consul.Service.Tags)
	assert.Equal(t, "dc1", cfg.Consul.Datacenter)
	assert.Equal(t, "https", cfg.Consul.Scheme)
	assert.Equal(t, "asdfasfdasf", cfg.Consul.Token)

	require.Len(t, cfg.Namespaces, 1)

	n := cfg.Namespaces[0]
	assert.Equal(t, "nginx", n.Name)
	assert.Equal(t, "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$http_x_forwarded_for\"", n.Format)
	assert.Equal(t, []string{"test.log", "foo.log"}, n.SourceFiles)
	assert.Equal(t, "magicapp", n.Labels["app"])

	require.Len(t, n.RelabelConfigs, 2)
	assert.Equal(t, "user", n.RelabelConfigs[0].TargetLabel)
	assert.Equal(t, "request_uri", n.RelabelConfigs[1].TargetLabel)

	assert.Len(t, n.RelabelConfigs[1].Matches, 2)
	assert.Equal(t, "^/users/[0-9]+", n.RelabelConfigs[1].Matches[0].RegexpString)
}

func TestLoadsHCLConfigFile(t *testing.T) {
	t.Parallel()

	buf := bytes.NewBufferString(HCLInput)
	cfg := Config{}

	err := LoadConfigFromStream(&cfg, buf, TypeHCL)
	assert.Nil(t, err, "unexpected error: %v", err)
	assertConfigContents(t, cfg)
}

func TestLoadsYAMLConfigFile(t *testing.T) {
	t.Parallel()

	buf := bytes.NewBufferString(YAMLInput)
	cfg := Config{}

	err := LoadConfigFromStream(&cfg, buf, TypeYAML)
	assert.Nil(t, err, "unexpected error: %v", err)
	assertConfigContents(t, cfg)
}
<|endoftext|>


<commit_before>// Copyright 2013 Alexandre Fiori
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package main

import (
	"bytes"
	"database/sql"
	_	"github.com/mattn/go-sqlite3"
	"encoding/binary"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"github.com/bradfitz/gomemcache/memcache"
	"github.com/fiorix/web"
	"net"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
)

// API limits
const (
	maxRequestsPerIP = 1000
	expirySeconds = 3600
)

type GeoIP struct {
    XMLName	xml.Name	`json:"-" xml:"Response"`
    Ip		string		`json:"ip"`
    CountryCode string		`json:"country_code"`
    CountryName string		`json:"country_name"`
    RegionCode	string		`json:"region_code"`
    RegionName	string		`json:"region_name"`
    CityName	string		`json:"city" xml:"City"`
    ZipCode	string		`json:"zipcode"`
    Latitude	float32		`json:"latitude"`
    Longitude	float32		`json:"longitude"`
    MetroCode	string		`json:"metro_code"`
    AreaCode	string		`json:"areacode"`
}

// http://en.wikipedia.org/wiki/Reserved_IP_addresses
var reservedIPs = []net.IPNet{
	{net.IPv4(0, 0, 0, 0),		net.IPv4Mask(255, 0, 0, 0)},
	{net.IPv4(0, 0, 0, 0),		net.IPv4Mask(255, 0, 0, 0)},
	{net.IPv4(10, 0, 0, 0),		net.IPv4Mask(255, 192, 0, 0)},
	{net.IPv4(100, 64, 0, 0),	net.IPv4Mask(255, 0, 0, 0)},
	{net.IPv4(127, 0, 0, 0),	net.IPv4Mask(255, 0, 0, 0)},
	{net.IPv4(169, 254, 0, 0),	net.IPv4Mask(255, 255, 0, 0)},
	{net.IPv4(172, 16, 0, 0),	net.IPv4Mask(255, 240, 0, 0)},
	{net.IPv4(192, 0, 0, 0),	net.IPv4Mask(255, 255, 255, 248)},
	{net.IPv4(192, 0, 2, 0),	net.IPv4Mask(255, 255, 255, 0)},
	{net.IPv4(192, 88, 99, 0),	net.IPv4Mask(255, 255, 255, 0)},
	{net.IPv4(192, 168, 0, 0),	net.IPv4Mask(255, 255, 0, 0)},
	{net.IPv4(198, 18, 0, 0),	net.IPv4Mask(255, 254, 0, 0)},
	{net.IPv4(198, 51, 100, 0),	net.IPv4Mask(255, 255, 255, 0)},
	{net.IPv4(203, 0, 113, 0),	net.IPv4Mask(255, 255, 255, 0)},
	{net.IPv4(224, 0, 0, 0),	net.IPv4Mask(240, 0, 0, 0)},
	{net.IPv4(240, 0, 0, 0),	net.IPv4Mask(240, 0, 0, 0)},
	{net.IPv4(255, 255, 255, 255),	net.IPv4Mask(255, 255, 255, 255)},
}

func LookupHandler(req web.RequestHandler, db *sql.DB) {
        req.SetHeader("Access-Control-Allow-Origin", "*")
	format, addr := req.Vars[1], req.Vars[2]
	if addr == "" {
		addr = strings.Split(req.HTTP.RemoteAddr, ":")[0]
	} else {
		addrs, err := net.LookupHost(addr)
		if err != nil {
			req.HTTPError(400, err)
			return
		}
		addr = addrs[0]
	}

	IP := net.ParseIP(addr)
	geoip := GeoIP{Ip: addr}

	reserved := false
	for _, net := range reservedIPs {
		if net.Contains(IP) {
			reserved = true
			break
		}
	}

	if reserved {
		geoip.CountryCode = "RD"
		geoip.CountryName = "Reserved"
	} else {
		q := "SELECT "+
		"  city_location.country_code, country_blocks.country_name, "+
		"  city_location.region_code, region_names.region_name, "+
		"  city_location.city_name, city_location.postal_code, "+
		"  city_location.latitude, city_location.longitude, "+
		"  city_location.metro_code, city_location.area_code "+
		"FROM city_blocks "+
		"  NATURAL JOIN city_location "+
		"  INNER JOIN country_blocks ON "+
		"    city_location.country_code = country_blocks.country_code "+
		"  INNER JOIN region_names ON "+
		"    city_location.country_code = region_names.country_code "+
		"    AND "+
		"    city_location.region_code = region_names.region_code "+
		"WHERE city_blocks.ip_start <= ? "+
		"ORDER BY city_blocks.ip_start DESC LIMIT 1"

		stmt, err := db.Prepare(q)
		if err != nil {
			req.HTTPError(500, err)
			return
		}

		defer stmt.Close()

		var uintIP uint32
		b := bytes.NewBuffer(IP.To4())
		binary.Read(b, binary.BigEndian, &uintIP)
		err = stmt.QueryRow(uintIP).Scan(
			&geoip.CountryCode,
			&geoip.CountryName,
			&geoip.RegionCode,
			&geoip.RegionName,
			&geoip.CityName,
			&geoip.ZipCode,
			&geoip.Latitude,
			&geoip.Longitude,
			&geoip.MetroCode,
			&geoip.AreaCode)
		if err != nil {
			req.HTTPError(500, err)
			return
		}
	}

	switch format[0] {
	case 'c':
		req.SetHeader("Content-Type", "application/csv")
		req.Write("\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\""+
			  "\"%s\",\"%0.4f\",\"%0.4f\",\"%s\",\"%s\"\r\n",
			  geoip.Ip,
			  geoip.CountryCode, geoip.CountryName,
			  geoip.RegionCode, geoip.RegionName,
			  geoip.CityName, geoip.ZipCode,
			  geoip.Latitude, geoip.Longitude,
			  geoip.MetroCode, geoip.AreaCode)
	case 'j':
		req.SetHeader("Content-Type", "application/json")
		resp, err := json.Marshal(geoip)
		if err != nil {
			req.HTTPError(500, err)
			return
		}
		req.Write("%s\r\n", resp)
	case 'x':
		req.SetHeader("Content-Type", "application/xml")
		resp, err := xml.MarshalIndent(geoip, " ", " ")
		if err != nil {
			req.HTTPError(500, err)
			return
		}
		req.Write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+
			  "%s\r\n", resp)
	}
}

func checkQuota(mc *memcache.Client, db *sql.DB,
		fn func(web.RequestHandler, *sql.DB)) web.HandlerFunc {
	return func(req web.RequestHandler) {
		k := strings.Split(req.HTTP.RemoteAddr, ":")[0]
		el, err := mc.Get(k)
		if err == memcache.ErrCacheMiss {
			err = mc.Set(&memcache.Item{
					Key: k, Value: []byte("1"),
					Expiration: expirySeconds})
		}

		if err != nil {
			req.HTTPError(503, err)
			return
		}

		if el != nil {
			count, _ := strconv.Atoi(string(el.Value))
			if count < maxRequestsPerIP {
				mc.Increment(k, 1)
			} else {
				req.HTTPError(403)
				return
			}
		}

		fn(req, db)  // do the lookup
	}
}

// This is just for backwards compatibility with freegeoip.net
func IndexHandler(req web.RequestHandler) {
	req.Redirect("/static/index.html")
}

var static_re = regexp.MustCompile("..[/\\\\]")  // gtfo
func StaticHandler(req web.RequestHandler) {
	filename := req.Vars[1]
	if static_re.MatchString(filename) {
		req.NotFound()
		return
	}
	req.ServeFile(filepath.Join("./static", filename))
}

func main() {
	db, err := sql.Open("sqlite3", "db/ipdb.sqlite")
	if err != nil {
		fmt.Println(err)
		return
	}
	mc := memcache.New("127.0.0.1:11211")
	handlers := []web.Handler{
		{"^/$", IndexHandler},
		{"^/static/(.*)$", StaticHandler},
		{"^/(crossdomain.xml)$", StaticHandler},
		{"^/(csv|json|xml)/(.*)$", checkQuota(mc, db, LookupHandler)},
	}
	web.Application(":8080", handlers,
			&web.Settings{Debug:true, XHeaders:false})
}
<commit_msg>minor fixes<commit_after>// Copyright 2013 Alexandre Fiori
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package main

import (
	"bytes"
	"database/sql"
	_ "github.com/mattn/go-sqlite3"
	"encoding/binary"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"github.com/bradfitz/gomemcache/memcache"
	"github.com/fiorix/web"
	"net"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
)

// API limits
const (
	maxRequestsPerIP = 1000
	expirySeconds = 3600
)

type GeoIP struct {
    XMLName	xml.Name	`json:"-" xml:"Response"`
    Ip		string		`json:"ip"`
    CountryCode string		`json:"country_code"`
    CountryName string		`json:"country_name"`
    RegionCode	string		`json:"region_code"`
    RegionName	string		`json:"region_name"`
    CityName	string		`json:"city" xml:"City"`
    ZipCode	string		`json:"zipcode"`
    Latitude	float32		`json:"latitude"`
    Longitude	float32		`json:"longitude"`
    MetroCode	string		`json:"metro_code"`
    AreaCode	string		`json:"areacode"`
}

// http://en.wikipedia.org/wiki/Reserved_IP_addresses
var reservedIPs = []net.IPNet{
	{net.IPv4(0, 0, 0, 0),		net.IPv4Mask(255, 0, 0, 0)},
	{net.IPv4(0, 0, 0, 0),		net.IPv4Mask(255, 0, 0, 0)},
	{net.IPv4(10, 0, 0, 0),		net.IPv4Mask(255, 192, 0, 0)},
	{net.IPv4(100, 64, 0, 0),	net.IPv4Mask(255, 0, 0, 0)},
	{net.IPv4(127, 0, 0, 0),	net.IPv4Mask(255, 0, 0, 0)},
	{net.IPv4(169, 254, 0, 0),	net.IPv4Mask(255, 255, 0, 0)},
	{net.IPv4(172, 16, 0, 0),	net.IPv4Mask(255, 240, 0, 0)},
	{net.IPv4(192, 0, 0, 0),	net.IPv4Mask(255, 255, 255, 248)},
	{net.IPv4(192, 0, 2, 0),	net.IPv4Mask(255, 255, 255, 0)},
	{net.IPv4(192, 88, 99, 0),	net.IPv4Mask(255, 255, 255, 0)},
	{net.IPv4(192, 168, 0, 0),	net.IPv4Mask(255, 255, 0, 0)},
	{net.IPv4(198, 18, 0, 0),	net.IPv4Mask(255, 254, 0, 0)},
	{net.IPv4(198, 51, 100, 0),	net.IPv4Mask(255, 255, 255, 0)},
	{net.IPv4(203, 0, 113, 0),	net.IPv4Mask(255, 255, 255, 0)},
	{net.IPv4(224, 0, 0, 0),	net.IPv4Mask(240, 0, 0, 0)},
	{net.IPv4(240, 0, 0, 0),	net.IPv4Mask(240, 0, 0, 0)},
	{net.IPv4(255, 255, 255, 255),	net.IPv4Mask(255, 255, 255, 255)},
}

func LookupHandler(req web.RequestHandler, db *sql.DB) {
	format, addr := req.Vars[1], req.Vars[2]
	if addr == "" {
		addr = strings.Split(req.HTTP.RemoteAddr, ":")[0]
	} else {
		addrs, err := net.LookupHost(addr)
		if err != nil {
			req.HTTPError(400, err)
			return
		}
		addr = addrs[0]
	}

	IP := net.ParseIP(addr)
	geoip := GeoIP{Ip: addr}

	reserved := false
	for _, net := range reservedIPs {
		if net.Contains(IP) {
			reserved = true
			break
		}
	}

	if reserved {
		geoip.CountryCode = "RD"
		geoip.CountryName = "Reserved"
	} else {
		q := "SELECT "+
		"  city_location.country_code, country_blocks.country_name, "+
		"  city_location.region_code, region_names.region_name, "+
		"  city_location.city_name, city_location.postal_code, "+
		"  city_location.latitude, city_location.longitude, "+
		"  city_location.metro_code, city_location.area_code "+
		"FROM city_blocks "+
		"  NATURAL JOIN city_location "+
		"  INNER JOIN country_blocks ON "+
		"    city_location.country_code = country_blocks.country_code "+
		"  INNER JOIN region_names ON "+
		"    city_location.country_code = region_names.country_code "+
		"    AND "+
		"    city_location.region_code = region_names.region_code "+
		"WHERE city_blocks.ip_start <= ? "+
		"ORDER BY city_blocks.ip_start DESC LIMIT 1"

		stmt, err := db.Prepare(q)
		if err != nil {
			req.HTTPError(500, err)
			return
		}

		defer stmt.Close()

		var uintIP uint32
		b := bytes.NewBuffer(IP.To4())
		binary.Read(b, binary.BigEndian, &uintIP)
		err = stmt.QueryRow(uintIP).Scan(
			&geoip.CountryCode,
			&geoip.CountryName,
			&geoip.RegionCode,
			&geoip.RegionName,
			&geoip.CityName,
			&geoip.ZipCode,
			&geoip.Latitude,
			&geoip.Longitude,
			&geoip.MetroCode,
			&geoip.AreaCode)
		if err != nil {
			req.HTTPError(500, err)
			return
		}
	}

	switch format[0] {
	case 'c':
		req.SetHeader("Content-Type", "application/csv")
		req.Write("\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\""+
			  "\"%s\",\"%0.4f\",\"%0.4f\",\"%s\",\"%s\"\r\n",
			  geoip.Ip,
			  geoip.CountryCode, geoip.CountryName,
			  geoip.RegionCode, geoip.RegionName,
			  geoip.CityName, geoip.ZipCode,
			  geoip.Latitude, geoip.Longitude,
			  geoip.MetroCode, geoip.AreaCode)
	case 'j':
		req.SetHeader("Content-Type", "application/json")
		resp, err := json.Marshal(geoip)
		if err != nil {
			req.HTTPError(500, err)
			return
		}
		req.Write("%s\r\n", resp)
	case 'x':
		req.SetHeader("Content-Type", "application/xml")
		resp, err := xml.MarshalIndent(geoip, " ", " ")
		if err != nil {
			req.HTTPError(500, err)
			return
		}
		req.Write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+
			  "%s\r\n", resp)
	}
}

func checkQuota(mc *memcache.Client, db *sql.DB,
		fn func(web.RequestHandler, *sql.DB)) web.HandlerFunc {
	return func(req web.RequestHandler) {
		req.SetHeader("Access-Control-Allow-Origin", "*")
		k := strings.Split(req.HTTP.RemoteAddr, ":")[0]
		el, err := mc.Get(k)
		if err == memcache.ErrCacheMiss {
			err = mc.Set(&memcache.Item{
					Key: k, Value: []byte("1"),
					Expiration: expirySeconds})
		}

		if err != nil {
			req.HTTPError(503, err)
			return
		}

		if el != nil {
			count, _ := strconv.Atoi(string(el.Value))
			if count < maxRequestsPerIP {
				mc.Increment(k, 1)
			} else {
				req.HTTPError(403)
				return
			}
		}

		fn(req, db)  // do the lookup
	}
}

// This is just for backwards compatibility with freegeoip.net
func IndexHandler(req web.RequestHandler) {
	req.Redirect("/static/index.html")
}

var static_re = regexp.MustCompile("..[/\\\\]")  // gtfo
func StaticHandler(req web.RequestHandler) {
	filename := req.Vars[1]
	if static_re.MatchString(filename) {
		req.NotFound()
		return
	}
	req.ServeFile(filepath.Join("./static", filename))
}

func main() {
	db, err := sql.Open("sqlite3", "db/ipdb.sqlite")
	if err != nil {
		fmt.Println(err)
		return
	}
	mc := memcache.New("127.0.0.1:11211")
	handlers := []web.Handler{
		{"^/$", IndexHandler},
		{"^/static/(.*)$", StaticHandler},
		{"^/(crossdomain.xml)$", StaticHandler},
		{"^/(csv|json|xml)/(.*)$", checkQuota(mc, db, LookupHandler)},
	}
	web.Application(":8080", handlers,
			&web.Settings{Debug:true, XHeaders:false})
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Aaron Meihm ameihm@mozilla.com [:alm]
package migoval

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"encoding/json"
	"fmt"
	ovallib "github.com/ameihm0912/mozoval/go/src/oval"
	"io/ioutil"
	"mig/modules"
	"time"
)

var stats Statistics

// Various counters used to populate module statistics at the end of the
// run.
var counters struct {
	startTime time.Time
}

func startCounters() {
	counters.startTime = time.Now()
}

func endCounters() {
	stats.OvalRuntime = time.Now().Sub(counters.startTime)
}

func init() {
	modules.Register("oval", func() interface{} {
		return new(Runner)
	})
}

type Runner struct {
	Parameters Parameters
	Results    modules.Result
}

func (r Runner) Run() (resStr string) {
	defer func() {
		if e := recover(); e != nil {
			// return error in json
			r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e))
			r.Results.Success = false
			endCounters()
			r.Results.Statistics = stats
			err, _ := json.Marshal(r.Results)
			resStr = string(err)
			return
		}
	}()

	startCounters()

	// Read module parameters from stdin
	err := modules.ReadInputParameters(&r.Parameters)
	if err != nil {
		panic(err)
	}

	err = r.ValidateParameters()
	if err != nil {
		panic(err)
	}

	ovallib.Init()
	ovallib.SetMaxChecks(r.Parameters.MaxConcurrentEval)

	e := &elements{}

	if len(r.Parameters.PkgMatch.Matches) > 0 {
		oresp := ovallib.PackageQuery(r.Parameters.PkgMatch.Matches)
		for _, x := range oresp {
			npi := &PkgInfo{PkgName: x.Name, PkgVersion: x.Version, PkgType: x.PkgType}
			e.Matches = append(e.Matches, *npi)
		}

		r.Results.Success = true
		if len(e.Matches) > 0 {
			r.Results.FoundAnything = true
		}
		r.Results.Elements = e
		endCounters()
		r.Results.Statistics = stats
		buf, err := json.Marshal(r.Results)
		if err != nil {
			panic(err)
		}
		resStr = string(buf)
		return
	} else if r.Parameters.OvalDef != "" {
		b := bytes.NewBufferString(r.Parameters.OvalDef)
		decoder := base64.NewDecoder(base64.StdEncoding, b)
		gz, err := gzip.NewReader(decoder)
		if err != nil {
			panic(err)
		}
		ovalbuf, err := ioutil.ReadAll(gz)
		if err != nil {
			panic(err)
		}

		od, err := ovallib.ParseBuffer(string(ovalbuf))
		if err != nil {
			panic(err)
		}
		ovalresults, err := ovallib.Execute(od)
		if err != nil {
			panic(err)
		}
		for _, x := range ovalresults {
			if !r.Parameters.IncludeFalse {
				if x.Status == ovallib.RESULT_FALSE {
					continue
				}
			}
			nmor := &MOResult{}
			nmor.Title = x.Title
			nmor.Status = x.StatusString()
			nmor.ID = x.ID
			e.OvalResults = append(e.OvalResults, *nmor)
		}

		r.Results.Success = true
		if len(e.OvalResults) > 0 {
			r.Results.FoundAnything = true
		}
		r.Results.Elements = e
		endCounters()
		r.Results.Statistics = stats
		buf, err := json.Marshal(r.Results)
		if err != nil {
			panic(err)
		}
		resStr = string(buf)
		return
	}

	panic("no function specified")
	return
}

func (r Runner) ValidateParameters() (err error) {
	if r.Parameters.MaxConcurrentEval <= 0 || r.Parameters.MaxConcurrentEval > 10 {
		return fmt.Errorf("concurrent evaluation must be between > 0 and <= 10")
	}
	return
}

func (r Runner) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) {
	var elem elements

	err = result.GetElements(&elem)
	if err != nil {
		panic(err)
	}

	for _, x := range elem.Matches {
		resStr := fmt.Sprintf("pkgmatch name=%v version=%v type=%v", x.PkgName, x.PkgVersion, x.PkgType)
		prints = append(prints, resStr)
	}

	for _, x := range elem.OvalResults {
		resStr := fmt.Sprintf("ovalresult id=%v title=\"%v\" outcome=%v", x.ID, x.Title, x.Status)
		prints = append(prints, resStr)
	}

	return
}

type elements struct {
	// In package match mode, the packages the agent has found that match
	// the query parameters.
	Matches []PkgInfo `json:"matches"`

	// Results of OVAL definition checks in OVAL mode
	OvalResults []MOResult `json:"ovalresults"`
}

type MOResult struct {
	Title  string `json:"title"`
	ID     string `json:"id"`
	Status string `json:"status"`
}

type PkgInfo struct {
	PkgName    string `json:"name"`
	PkgVersion string `json:"version"`
	PkgType    string `json:"type"`
}

type Statistics struct {
	OvalRuntime time.Duration `json:"ovalruntime"`
}

type Parameters struct {
	// Package match mode, contains a list of strings to use as substring
	// matches
	PkgMatch PkgMatch `json:"pkgmatch"`

	// A compressed, base64 encoded OVAL definition file for processing
	// using OVAL library on agent.
	OvalDef string `json:"ovaldef"`

	// Concurrent checks to run on agent
	MaxConcurrentEval int `json:"maxconneval"`

	// Include false results for checks
	IncludeFalse bool `json:"includefalse"`
}

type PkgMatch struct {
	Matches []string `json:"matches"`
}

func newParameters() *Parameters {
	return &Parameters{}
}
<commit_msg>[minor] update migoval Run to support Reader parameter<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Aaron Meihm ameihm@mozilla.com [:alm]
package migoval

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"encoding/json"
	"fmt"
	ovallib "github.com/ameihm0912/mozoval/go/src/oval"
	"io"
	"io/ioutil"
	"mig/modules"
	"time"
)

var stats Statistics

// Various counters used to populate module statistics at the end of the
// run.
var counters struct {
	startTime time.Time
}

func startCounters() {
	counters.startTime = time.Now()
}

func endCounters() {
	stats.OvalRuntime = time.Now().Sub(counters.startTime)
}

func init() {
	modules.Register("oval", func() interface{} {
		return new(Runner)
	})
}

type Runner struct {
	Parameters Parameters
	Results    modules.Result
}

func (r Runner) Run(in io.Reader) (resStr string) {
	defer func() {
		if e := recover(); e != nil {
			// return error in json
			r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", e))
			r.Results.Success = false
			endCounters()
			r.Results.Statistics = stats
			err, _ := json.Marshal(r.Results)
			resStr = string(err)
			return
		}
	}()

	startCounters()

	// Read module parameters from stdin
	err := modules.ReadInputParameters(in, &r.Parameters)
	if err != nil {
		panic(err)
	}

	err = r.ValidateParameters()
	if err != nil {
		panic(err)
	}

	ovallib.Init()
	ovallib.SetMaxChecks(r.Parameters.MaxConcurrentEval)

	e := &elements{}

	if len(r.Parameters.PkgMatch.Matches) > 0 {
		oresp := ovallib.PackageQuery(r.Parameters.PkgMatch.Matches)
		for _, x := range oresp {
			npi := &PkgInfo{PkgName: x.Name, PkgVersion: x.Version, PkgType: x.PkgType}
			e.Matches = append(e.Matches, *npi)
		}

		r.Results.Success = true
		if len(e.Matches) > 0 {
			r.Results.FoundAnything = true
		}
		r.Results.Elements = e
		endCounters()
		r.Results.Statistics = stats
		buf, err := json.Marshal(r.Results)
		if err != nil {
			panic(err)
		}
		resStr = string(buf)
		return
	} else if r.Parameters.OvalDef != "" {
		b := bytes.NewBufferString(r.Parameters.OvalDef)
		decoder := base64.NewDecoder(base64.StdEncoding, b)
		gz, err := gzip.NewReader(decoder)
		if err != nil {
			panic(err)
		}
		ovalbuf, err := ioutil.ReadAll(gz)
		if err != nil {
			panic(err)
		}

		od, err := ovallib.ParseBuffer(string(ovalbuf))
		if err != nil {
			panic(err)
		}
		ovalresults, err := ovallib.Execute(od)
		if err != nil {
			panic(err)
		}
		for _, x := range ovalresults {
			if !r.Parameters.IncludeFalse {
				if x.Status == ovallib.RESULT_FALSE {
					continue
				}
			}
			nmor := &MOResult{}
			nmor.Title = x.Title
			nmor.Status = x.StatusString()
			nmor.ID = x.ID
			e.OvalResults = append(e.OvalResults, *nmor)
		}

		r.Results.Success = true
		if len(e.OvalResults) > 0 {
			r.Results.FoundAnything = true
		}
		r.Results.Elements = e
		endCounters()
		r.Results.Statistics = stats
		buf, err := json.Marshal(r.Results)
		if err != nil {
			panic(err)
		}
		resStr = string(buf)
		return
	}

	panic("no function specified")
	return
}

func (r Runner) ValidateParameters() (err error) {
	if r.Parameters.MaxConcurrentEval <= 0 || r.Parameters.MaxConcurrentEval > 10 {
		return fmt.Errorf("concurrent evaluation must be between > 0 and <= 10")
	}
	return
}

func (r Runner) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) {
	var elem elements

	err = result.GetElements(&elem)
	if err != nil {
		panic(err)
	}

	for _, x := range elem.Matches {
		resStr := fmt.Sprintf("pkgmatch name=%v version=%v type=%v", x.PkgName, x.PkgVersion, x.PkgType)
		prints = append(prints, resStr)
	}

	for _, x := range elem.OvalResults {
		resStr := fmt.Sprintf("ovalresult id=%v title=\"%v\" outcome=%v", x.ID, x.Title, x.Status)
		prints = append(prints, resStr)
	}

	return
}

type elements struct {
	// In package match mode, the packages the agent has found that match
	// the query parameters.
	Matches []PkgInfo `json:"matches"`

	// Results of OVAL definition checks in OVAL mode
	OvalResults []MOResult `json:"ovalresults"`
}

type MOResult struct {
	Title  string `json:"title"`
	ID     string `json:"id"`
	Status string `json:"status"`
}

type PkgInfo struct {
	PkgName    string `json:"name"`
	PkgVersion string `json:"version"`
	PkgType    string `json:"type"`
}

type Statistics struct {
	OvalRuntime time.Duration `json:"ovalruntime"`
}

type Parameters struct {
	// Package match mode, contains a list of strings to use as substring
	// matches
	PkgMatch PkgMatch `json:"pkgmatch"`

	// A compressed, base64 encoded OVAL definition file for processing
	// using OVAL library on agent.
	OvalDef string `json:"ovaldef"`

	// Concurrent checks to run on agent
	MaxConcurrentEval int `json:"maxconneval"`

	// Include false results for checks
	IncludeFalse bool `json:"includefalse"`
}

type PkgMatch struct {
	Matches []string `json:"matches"`
}

func newParameters() *Parameters {
	return &Parameters{}
}
<|endoftext|>


<commit_before>package graval

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
)

const (
	welcomeMessage = "Welcome to the Go FTP Server"
)

type FTPConn struct {
	cwd           string
	conn          *net.TCPConn
	controlReader *bufio.Reader
	controlWriter *bufio.Writer
	data          *net.TCPConn
	driver        FTPDriver
	reqUser       string
	user          string
}

// NewFTPConn constructs a new object that will handle the FTP protocol over
// an active net.TCPConn. The TCP connection should already be open before
// it is handed to this functions. driver is an instance of FTPDrive that
// will handle all auth and persistence details.
func NewFTPConn(tcpConn *net.TCPConn, driver FTPDriver) *FTPConn {
	c := new(FTPConn)
	c.cwd = "/"
	c.conn = tcpConn
	c.controlReader = bufio.NewReader(tcpConn)
	c.controlWriter = bufio.NewWriter(tcpConn)
	c.driver = driver
	return c
}

// Serve starts an endless loop that reads FTP commands from the client and
// responds appropriately. terminated is a channel that will receive a true
// message when the connection closes. This loop will be running inside a
// goroutine, so use this channel to be notified when the connection can be
// cleaned up.
func (ftpConn *FTPConn) Serve(terminated chan bool) {
	log.Print("Connection Established")
	// send welcome
	ftpConn.writeMessage(220, welcomeMessage)
	// read commands
	for {
		line, err := ftpConn.controlReader.ReadString('\n')
		if err == nil {
			ftpConn.receiveLine(line)
		}
	}
	terminated <- true
	log.Print("Connection Terminated")
}

// Close will manually close this connection, even if the client isn't ready.
func (ftpConn *FTPConn) Close() {
	ftpConn.conn.Close()
	if ftpConn.data != nil {
		ftpConn.data.Close()
	}
}

// receiveLine accepts a single line FTP command and co-ordinates an
// appropriate response.
func (ftpConn *FTPConn) receiveLine(line string) {
	log.Print(line)
	command, param := ftpConn.parseLine(line)
	switch command {
	case "MODE":
		ftpConn.cmdMode(param)
		break
	case "NOOP":
		ftpConn.cmdNoop()
		break
	case "PASS":
		ftpConn.cmdPass(param)
		break
	case "QUIT":
		ftpConn.Close()
		break
	case "STRU":
		ftpConn.cmdStru(param)
		break
	case "SYST":
		ftpConn.cmdSyst()
		break
	case "USER":
		ftpConn.cmdUser(param)
		break
	default:
		ftpConn.writeMessage(500, "Command not found")
	}
}

// cmdMode responds to the MODE FTP command.
//
// the original FTP spec had various options for hosts to negotiate how data
// would be sent over the data socket, In reality these days (S)tream mode
// is all that is used for the mode - data is just streamed down the data
// socket unchanged.
func (ftpConn *FTPConn) cmdMode(param string) {
	if strings.ToUpper(param) == "S" {
		ftpConn.writeMessage(200, "OK")
	} else {
		ftpConn.writeMessage(504, "MODE is an obsolete command")
	}
}

// cmdNoop responds to the NOOP FTP command.
//
// This is essentially a ping from the client so we just respond with an
// basic 200 message.
func (ftpConn *FTPConn) cmdNoop() {
	ftpConn.writeMessage(200, "OK")
}

// cmdPass respond to the PASS FTP command by asking the driver if the supplied
// username and password are valid
func (ftpConn *FTPConn) cmdPass(param string) {
	if ftpConn.driver.Authenticate(ftpConn.reqUser, param) {
		ftpConn.user = ftpConn.reqUser
		ftpConn.reqUser = ""
		ftpConn.writeMessage(230, "Password ok, continue")
	} else {
		ftpConn.writeMessage(530, "Incorrect password, not logged in")
	}
}

// cmdStru responds to the STRU FTP command.
//
// like the MODE and TYPE commands, stru[cture] dates back to a time when the
// FTP protocol was more aware of the content of the files it was transferring,
// and would sometimes be expected to translate things like EOL markers on the
// fly.
//
// These days files are sent unmodified, and F(ile) mode is the only one we
// really need to support.
func (ftpConn *FTPConn) cmdStru(param string) {
	if strings.ToUpper(param) == "F" {
		ftpConn.writeMessage(200, "OK")
	} else {
		ftpConn.writeMessage(504, "STRU is an obsolete command")
	}
}

// cmdSyst responds to the SYST FTP command by providing a canned response.
func (ftpConn *FTPConn) cmdSyst() {
	ftpConn.writeMessage(215, "UNIX Type: L8")
}

// cmdUser responds to the USER FTP command by asking for the password
func (ftpConn *FTPConn) cmdUser(param string) {
	ftpConn.reqUser = param
	ftpConn.writeMessage(331, "User name ok, password required")
}

func (ftpConn *FTPConn) parseLine(line string) (string, string) {
	params := strings.SplitN(strings.Trim(line, "\r\n"), " ", 2)
	if len(params) == 1 {
		return params[0], ""
	}
	return params[0], params[1]
}

// writeMessage will send a standard FTP response back to the client.
func (ftpConn *FTPConn) writeMessage(code int, message string) (wrote int, err error) {
	line := fmt.Sprintf("%d %s\r\n", code, message)
	wrote, err = ftpConn.controlWriter.WriteString(line)
	ftpConn.controlWriter.Flush()
	log.Print(message)
	return
}
<commit_msg>recognise the ALLO command<commit_after>package graval

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
)

const (
	welcomeMessage = "Welcome to the Go FTP Server"
)

type FTPConn struct {
	cwd           string
	conn          *net.TCPConn
	controlReader *bufio.Reader
	controlWriter *bufio.Writer
	data          *net.TCPConn
	driver        FTPDriver
	reqUser       string
	user          string
}

// NewFTPConn constructs a new object that will handle the FTP protocol over
// an active net.TCPConn. The TCP connection should already be open before
// it is handed to this functions. driver is an instance of FTPDrive that
// will handle all auth and persistence details.
func NewFTPConn(tcpConn *net.TCPConn, driver FTPDriver) *FTPConn {
	c := new(FTPConn)
	c.cwd = "/"
	c.conn = tcpConn
	c.controlReader = bufio.NewReader(tcpConn)
	c.controlWriter = bufio.NewWriter(tcpConn)
	c.driver = driver
	return c
}

// Serve starts an endless loop that reads FTP commands from the client and
// responds appropriately. terminated is a channel that will receive a true
// message when the connection closes. This loop will be running inside a
// goroutine, so use this channel to be notified when the connection can be
// cleaned up.
func (ftpConn *FTPConn) Serve(terminated chan bool) {
	log.Print("Connection Established")
	// send welcome
	ftpConn.writeMessage(220, welcomeMessage)
	// read commands
	for {
		line, err := ftpConn.controlReader.ReadString('\n')
		if err == nil {
			ftpConn.receiveLine(line)
		}
	}
	terminated <- true
	log.Print("Connection Terminated")
}

// Close will manually close this connection, even if the client isn't ready.
func (ftpConn *FTPConn) Close() {
	ftpConn.conn.Close()
	if ftpConn.data != nil {
		ftpConn.data.Close()
	}
}

// receiveLine accepts a single line FTP command and co-ordinates an
// appropriate response.
func (ftpConn *FTPConn) receiveLine(line string) {
	log.Print(line)
	command, param := ftpConn.parseLine(line)
	switch command {
	case "ALLO":
		ftpConn.cmdAllo()
		break
	case "MODE":
		ftpConn.cmdMode(param)
		break
	case "NOOP":
		ftpConn.cmdNoop()
		break
	case "PASS":
		ftpConn.cmdPass(param)
		break
	case "QUIT":
		ftpConn.Close()
		break
	case "STRU":
		ftpConn.cmdStru(param)
		break
	case "SYST":
		ftpConn.cmdSyst()
		break
	case "USER":
		ftpConn.cmdUser(param)
		break
	default:
		ftpConn.writeMessage(500, "Command not found")
	}
}

// cmdNoop responds to the ALLO FTP command.
//
// This is essentially a ping from the client so we just respond with an
// basic OK message.
func (ftpConn *FTPConn) cmdAllo() {
	ftpConn.writeMessage(202, "Obsolete")
}

// cmdMode responds to the MODE FTP command.
//
// the original FTP spec had various options for hosts to negotiate how data
// would be sent over the data socket, In reality these days (S)tream mode
// is all that is used for the mode - data is just streamed down the data
// socket unchanged.
func (ftpConn *FTPConn) cmdMode(param string) {
	if strings.ToUpper(param) == "S" {
		ftpConn.writeMessage(200, "OK")
	} else {
		ftpConn.writeMessage(504, "MODE is an obsolete command")
	}
}

// cmdNoop responds to the NOOP FTP command.
//
// This is essentially a ping from the client so we just respond with an
// basic 200 message.
func (ftpConn *FTPConn) cmdNoop() {
	ftpConn.writeMessage(200, "OK")
}

// cmdPass respond to the PASS FTP command by asking the driver if the supplied
// username and password are valid
func (ftpConn *FTPConn) cmdPass(param string) {
	if ftpConn.driver.Authenticate(ftpConn.reqUser, param) {
		ftpConn.user = ftpConn.reqUser
		ftpConn.reqUser = ""
		ftpConn.writeMessage(230, "Password ok, continue")
	} else {
		ftpConn.writeMessage(530, "Incorrect password, not logged in")
	}
}

// cmdStru responds to the STRU FTP command.
//
// like the MODE and TYPE commands, stru[cture] dates back to a time when the
// FTP protocol was more aware of the content of the files it was transferring,
// and would sometimes be expected to translate things like EOL markers on the
// fly.
//
// These days files are sent unmodified, and F(ile) mode is the only one we
// really need to support.
func (ftpConn *FTPConn) cmdStru(param string) {
	if strings.ToUpper(param) == "F" {
		ftpConn.writeMessage(200, "OK")
	} else {
		ftpConn.writeMessage(504, "STRU is an obsolete command")
	}
}

// cmdSyst responds to the SYST FTP command by providing a canned response.
func (ftpConn *FTPConn) cmdSyst() {
	ftpConn.writeMessage(215, "UNIX Type: L8")
}

// cmdUser responds to the USER FTP command by asking for the password
func (ftpConn *FTPConn) cmdUser(param string) {
	ftpConn.reqUser = param
	ftpConn.writeMessage(331, "User name ok, password required")
}

func (ftpConn *FTPConn) parseLine(line string) (string, string) {
	params := strings.SplitN(strings.Trim(line, "\r\n"), " ", 2)
	if len(params) == 1 {
		return params[0], ""
	}
	return params[0], params[1]
}

// writeMessage will send a standard FTP response back to the client.
func (ftpConn *FTPConn) writeMessage(code int, message string) (wrote int, err error) {
	line := fmt.Sprintf("%d %s\r\n", code, message)
	wrote, err = ftpConn.controlWriter.WriteString(line)
	ftpConn.controlWriter.Flush()
	log.Print(message)
	return
}
<|endoftext|>


<commit_before>package app

import (
	"fmt"

	goflag "flag"

	"github.com/golang/glog"
	"github.com/openebs/maya/cmd/maya-agent/app/exporter"
	"github.com/openebs/maya/cmd/maya-agent/storage/block"
	"github.com/openebs/maya/pkg/util"
	"github.com/spf13/cobra"
)

var (
	cmdName = "maya-agent"
	usage   = fmt.Sprintf("%s", cmdName)
)

// Define a type for the options of MayaAgent
type MayaAgentOptions struct {
	KubeConfig string
	Namespace  string
}

func AddKubeConfigFlag(cmd *cobra.Command, value *string) {
	cmd.Flags().StringVarP(value, "kubeconfig", "", *value,
		"Path to a kube config. Only required if out-of-cluster.")
}

func AddNamespaceFlag(cmd *cobra.Command, value *string) {
	cmd.Flags().StringVarP(value, "namespace", "n", *value,
		"Namespace to deploy in. If no namespace is provided, POD_NAMESPACE env.var is used. Lastly, the 'default' namespace will be used as a last option.")
}

// NewCmdOptions creates an options Cobra command to return usage
func NewCmdOptions() *cobra.Command {
	cmd := &cobra.Command{
		Use: "options",
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Usage()
		},
	}

	return cmd
}

// Create a new maya-agent. This cmd includes logging,
// cmd option parsing from flags
func NewMayaAgent() (*cobra.Command, error) {
	// Define the options for MayaAgent
	options := MayaAgentOptions{}

	// Create a new command
	cmd := &cobra.Command{
		Use:   usage,
		Short: "",
		Run: func(cmd *cobra.Command, args []string) {
			util.CheckErr(Run(cmd, &options), util.Fatal)
		},
	}

	// Bind & parse flags defined by external projects.
	// e.g. This imports the golang/glog pkg flags into the cmd flagset
	cmd.Flags().AddGoFlagSet(goflag.CommandLine)
	goflag.CommandLine.Parse([]string{})
	cmd.AddCommand(
		block.NewCmdBlockDevice(), //Add new command on block device
		exporter.NewCmdVolumeExporter(),
	)
	// Define the flags allowed in this command & store each option provided
	// as a flag, into the MayaAgentOptions
	AddKubeConfigFlag(cmd, &options.KubeConfig)
	AddNamespaceFlag(cmd, &options.Namespace)

	return cmd, nil
}

// Run maya-agent
func Run(cmd *cobra.Command, options *MayaAgentOptions) error {
	glog.Infof("Starting maya-agent...")

	return nil
}
<commit_msg>Fixed golint issues  (#165)<commit_after>package app

import (
	"fmt"

	goflag "flag"

	"github.com/golang/glog"
	"github.com/openebs/maya/cmd/maya-agent/app/exporter"
	"github.com/openebs/maya/cmd/maya-agent/storage/block"
	"github.com/openebs/maya/pkg/util"
	"github.com/spf13/cobra"
)

var (
	cmdName = "maya-agent"
	usage   = fmt.Sprintf("%s", cmdName)
)

// MayaAgentOptions defines a type for the options of MayaAgent
type MayaAgentOptions struct {
	KubeConfig string
	Namespace  string
}

//AddKubeConfigFlag is used to add a config flag
func AddKubeConfigFlag(cmd *cobra.Command, value *string) {
	cmd.Flags().StringVarP(value, "kubeconfig", "", *value,
		"Path to a kube config. Only required if out-of-cluster.")
}

//AddNamespaceFlag is used to add a namespace flag
func AddNamespaceFlag(cmd *cobra.Command, value *string) {
	cmd.Flags().StringVarP(value, "namespace", "n", *value,
		"Namespace to deploy in. If no namespace is provided, POD_NAMESPACE env.var is used. Lastly, the 'default' namespace will be used as a last option.")
}

// NewCmdOptions creates an options Cobra command to return usage
func NewCmdOptions() *cobra.Command {
	cmd := &cobra.Command{
		Use: "options",
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Usage()
		},
	}

	return cmd
}

// NewMayaAgent creates a new maya-agent. This cmd includes logging,
// cmd option parsing from flags
func NewMayaAgent() (*cobra.Command, error) {
	// Define the options for MayaAgent
	options := MayaAgentOptions{}

	// Create a new command
	cmd := &cobra.Command{
		Use:   usage,
		Short: "",
		Run: func(cmd *cobra.Command, args []string) {
			util.CheckErr(Run(cmd, &options), util.Fatal)
		},
	}

	// Bind & parse flags defined by external projects.
	// e.g. This imports the golang/glog pkg flags into the cmd flagset
	cmd.Flags().AddGoFlagSet(goflag.CommandLine)
	goflag.CommandLine.Parse([]string{})
	cmd.AddCommand(
		block.NewCmdBlockDevice(), //Add new command on block device
		exporter.NewCmdVolumeExporter(),
	)
	// Define the flags allowed in this command & store each option provided
	// as a flag, into the MayaAgentOptions
	AddKubeConfigFlag(cmd, &options.KubeConfig)
	AddNamespaceFlag(cmd, &options.Namespace)

	return cmd, nil
}

// Run maya-agent
func Run(cmd *cobra.Command, options *MayaAgentOptions) error {
	glog.Infof("Starting maya-agent...")

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package logic

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"k8s.io/api/admission/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/pod"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/pod/patch"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/limitrange"
	metrics_admission "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/admission"
	"k8s.io/klog/v2"
)

// AdmissionServer is an admission webhook server that modifies pod resources request based on VPA recommendation
type AdmissionServer struct {
	limitsChecker    limitrange.LimitRangeCalculator
	resourceHandlers map[metav1.GroupResource]resource.Handler
}

// NewAdmissionServer constructs new AdmissionServer
func NewAdmissionServer(podPreProcessor pod.PreProcessor,
	vpaPreProcessor vpa.PreProcessor,
	limitsChecker limitrange.LimitRangeCalculator,
	vpaMatcher vpa.Matcher,
	patchCalculators []patch.Calculator) *AdmissionServer {
	as := &AdmissionServer{limitsChecker, map[metav1.GroupResource]resource.Handler{}}
	as.RegisterResourceHandler(pod.NewResourceHandler(podPreProcessor, vpaMatcher, patchCalculators))
	as.RegisterResourceHandler(vpa.NewResourceHandler(vpaPreProcessor))
	return as
}

// RegisterResourceHandler allows to register a custom logic for handling given types of resources.
func (s *AdmissionServer) RegisterResourceHandler(resourceHandler resource.Handler) {
	s.resourceHandlers[resourceHandler.GroupResource()] = resourceHandler
}

func (s *AdmissionServer) admit(data []byte) (*v1.AdmissionResponse, metrics_admission.AdmissionStatus, metrics_admission.AdmissionResource) {
	// we don't block the admission by default, even on unparsable JSON
	response := v1.AdmissionResponse{}
	response.Allowed = true

	ar := v1.AdmissionReview{}
	if err := json.Unmarshal(data, &ar); err != nil {
		klog.Error(err)
		return &response, metrics_admission.Error, metrics_admission.Unknown
	}

	response.UID = ar.Request.UID

	var patches []resource.PatchRecord
	var err error
	resource := metrics_admission.Unknown

	response.UID = ar.Request.UID
	admittedGroupResource := metav1.GroupResource{
		Group:    ar.Request.Resource.Group,
		Resource: ar.Request.Resource.Resource,
	}

	handler, ok := s.resourceHandlers[admittedGroupResource]
	if ok {
		patches, err = handler.GetPatches(ar.Request)
		resource = handler.AdmissionResource()

		if handler.DisallowIncorrectObjects() && err != nil {
			// we don't let in problematic objects - late validation
			status := metav1.Status{}
			status.Status = "Failure"
			status.Message = err.Error()
			response.Result = &status
			response.Allowed = false
		}
	} else {
		patches, err = nil, fmt.Errorf("not supported resource type: %v", admittedGroupResource)
	}

	if err != nil {
		klog.Error(err)
		return &response, metrics_admission.Error, resource
	}

	if len(patches) > 0 {
		patch, err := json.Marshal(patches)
		if err != nil {
			klog.Errorf("Cannot marshal the patch %v: %v", patches, err)
			return &response, metrics_admission.Error, resource
		}
		patchType := v1.PatchTypeJSONPatch
		response.PatchType = &patchType
		response.Patch = patch
		klog.V(4).Infof("Sending patches: %v", patches)
	}

	var status metrics_admission.AdmissionStatus
	if len(patches) > 0 {
		status = metrics_admission.Applied
	} else {
		status = metrics_admission.Skipped
	}
	if resource == metrics_admission.Pod {
		metrics_admission.OnAdmittedPod(status == metrics_admission.Applied)
	}

	return &response, status, resource
}

// Serve is a handler function of AdmissionServer
func (s *AdmissionServer) Serve(w http.ResponseWriter, r *http.Request) {
	timer := metrics_admission.NewAdmissionLatency()

	var body []byte
	if r.Body != nil {
		if data, err := ioutil.ReadAll(r.Body); err == nil {
			body = data
		}
	}

	// verify the content type is accurate
	contentType := r.Header.Get("Content-Type")
	if contentType != "application/json" {
		klog.Errorf("contentType=%s, expect application/json", contentType)
		timer.Observe(metrics_admission.Error, metrics_admission.Unknown)
		return
	}

	reviewResponse, status, resource := s.admit(body)
	ar := v1.AdmissionReview{
		Response: reviewResponse,
	}

	resp, err := json.Marshal(ar)
	if err != nil {
		klog.Error(err)
		timer.Observe(metrics_admission.Error, resource)
		return
	}

	if _, err := w.Write(resp); err != nil {
		klog.Error(err)
		timer.Observe(metrics_admission.Error, resource)
		return
	}

	timer.Observe(status, resource)
}
<commit_msg>Fix admission controller<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package logic

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"k8s.io/api/admission/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/pod"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/pod/patch"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/limitrange"
	metrics_admission "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/admission"
	"k8s.io/klog/v2"
)

// AdmissionServer is an admission webhook server that modifies pod resources request based on VPA recommendation
type AdmissionServer struct {
	limitsChecker    limitrange.LimitRangeCalculator
	resourceHandlers map[metav1.GroupResource]resource.Handler
}

// NewAdmissionServer constructs new AdmissionServer
func NewAdmissionServer(podPreProcessor pod.PreProcessor,
	vpaPreProcessor vpa.PreProcessor,
	limitsChecker limitrange.LimitRangeCalculator,
	vpaMatcher vpa.Matcher,
	patchCalculators []patch.Calculator) *AdmissionServer {
	as := &AdmissionServer{limitsChecker, map[metav1.GroupResource]resource.Handler{}}
	as.RegisterResourceHandler(pod.NewResourceHandler(podPreProcessor, vpaMatcher, patchCalculators))
	as.RegisterResourceHandler(vpa.NewResourceHandler(vpaPreProcessor))
	return as
}

// RegisterResourceHandler allows to register a custom logic for handling given types of resources.
func (s *AdmissionServer) RegisterResourceHandler(resourceHandler resource.Handler) {
	s.resourceHandlers[resourceHandler.GroupResource()] = resourceHandler
}

func (s *AdmissionServer) admit(data []byte) (*v1.AdmissionResponse, metrics_admission.AdmissionStatus, metrics_admission.AdmissionResource) {
	// we don't block the admission by default, even on unparsable JSON
	response := v1.AdmissionResponse{}
	response.Allowed = true

	ar := v1.AdmissionReview{}
	if err := json.Unmarshal(data, &ar); err != nil {
		klog.Error(err)
		return &response, metrics_admission.Error, metrics_admission.Unknown
	}

	response.UID = ar.Request.UID

	var patches []resource.PatchRecord
	var err error
	resource := metrics_admission.Unknown

	admittedGroupResource := metav1.GroupResource{
		Group:    ar.Request.Resource.Group,
		Resource: ar.Request.Resource.Resource,
	}

	handler, ok := s.resourceHandlers[admittedGroupResource]
	if ok {
		patches, err = handler.GetPatches(ar.Request)
		resource = handler.AdmissionResource()

		if handler.DisallowIncorrectObjects() && err != nil {
			// we don't let in problematic objects - late validation
			status := metav1.Status{}
			status.Status = "Failure"
			status.Message = err.Error()
			response.Result = &status
			response.Allowed = false
		}
	} else {
		patches, err = nil, fmt.Errorf("not supported resource type: %v", admittedGroupResource)
	}

	if err != nil {
		klog.Error(err)
		return &response, metrics_admission.Error, resource
	}

	if len(patches) > 0 {
		patch, err := json.Marshal(patches)
		if err != nil {
			klog.Errorf("Cannot marshal the patch %v: %v", patches, err)
			return &response, metrics_admission.Error, resource
		}
		patchType := v1.PatchTypeJSONPatch
		response.PatchType = &patchType
		response.Patch = patch
		klog.V(4).Infof("Sending patches: %v", patches)
	}

	var status metrics_admission.AdmissionStatus
	if len(patches) > 0 {
		status = metrics_admission.Applied
	} else {
		status = metrics_admission.Skipped
	}
	if resource == metrics_admission.Pod {
		metrics_admission.OnAdmittedPod(status == metrics_admission.Applied)
	}

	return &response, status, resource
}

// Serve is a handler function of AdmissionServer
func (s *AdmissionServer) Serve(w http.ResponseWriter, r *http.Request) {
	timer := metrics_admission.NewAdmissionLatency()

	var body []byte
	if r.Body != nil {
		if data, err := ioutil.ReadAll(r.Body); err == nil {
			body = data
		}
	}

	// verify the content type is accurate
	contentType := r.Header.Get("Content-Type")
	if contentType != "application/json" {
		klog.Errorf("contentType=%s, expect application/json", contentType)
		timer.Observe(metrics_admission.Error, metrics_admission.Unknown)
		return
	}

	reviewResponse, status, resource := s.admit(body)
	ar := v1.AdmissionReview{
		Response: reviewResponse,
		TypeMeta: metav1.TypeMeta{
			Kind:       "AdmissionReview",
			APIVersion: "admission.k8s.io/v1",
		},
	}

	resp, err := json.Marshal(ar)
	if err != nil {
		klog.Error(err)
		timer.Observe(metrics_admission.Error, resource)
		return
	}

	if _, err := w.Write(resp); err != nil {
		klog.Error(err)
		timer.Observe(metrics_admission.Error, resource)
		return
	}

	timer.Observe(status, resource)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package logic

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"k8s.io/api/admission/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/pod"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/pod/patch"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/limitrange"
	metrics_admission "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/admission"
	"k8s.io/klog/v2"
)

// AdmissionServer is an admission webhook server that modifies pod resources request based on VPA recommendation
type AdmissionServer struct {
	limitsChecker    limitrange.LimitRangeCalculator
	resourceHandlers map[metav1.GroupResource]resource.Handler
}

// NewAdmissionServer constructs new AdmissionServer
func NewAdmissionServer(podPreProcessor pod.PreProcessor,
	vpaPreProcessor vpa.PreProcessor,
	limitsChecker limitrange.LimitRangeCalculator,
	vpaMatcher vpa.Matcher,
	patchCalculators []patch.Calculator) *AdmissionServer {
	as := &AdmissionServer{limitsChecker, map[metav1.GroupResource]resource.Handler{}}
	as.RegisterResourceHandler(pod.NewResourceHandler(podPreProcessor, vpaMatcher, patchCalculators))
	as.RegisterResourceHandler(vpa.NewResourceHandler(vpaPreProcessor))
	return as
}

// RegisterResourceHandler allows to register a custom logic for handling given types of resources.
func (s *AdmissionServer) RegisterResourceHandler(resourceHandler resource.Handler) {
	s.resourceHandlers[resourceHandler.GroupResource()] = resourceHandler
}

func (s *AdmissionServer) admit(data []byte) (*v1beta1.AdmissionResponse, metrics_admission.AdmissionStatus, metrics_admission.AdmissionResource) {
	// we don't block the admission by default, even on unparsable JSON
	response := v1beta1.AdmissionResponse{}
	response.Allowed = true

	ar := v1beta1.AdmissionReview{}
	if err := json.Unmarshal(data, &ar); err != nil {
		klog.Error(err)
		return &response, metrics_admission.Error, metrics_admission.Unknown
	}

	var patches []resource.PatchRecord
	var err error
	resource := metrics_admission.Unknown

	admittedGroupResource := metav1.GroupResource{
		Group:    ar.Request.Resource.Group,
		Resource: ar.Request.Resource.Resource,
	}

	handler, ok := s.resourceHandlers[admittedGroupResource]
	if ok {
		patches, err = handler.GetPatches(ar.Request)
		resource = handler.AdmissionResource()

		if handler.DisallowIncorrectObjects() && err != nil {
			// we don't let in problematic objects - late validation
			status := metav1.Status{}
			status.Status = "Failure"
			status.Message = err.Error()
			response.Result = &status
			response.Allowed = false
		}
	} else {
		patches, err = nil, fmt.Errorf("not supported resource type: %v", admittedGroupResource)
	}

	if err != nil {
		klog.Error(err)
		return &response, metrics_admission.Error, resource
	}

	if len(patches) > 0 {
		patch, err := json.Marshal(patches)
		if err != nil {
			klog.Errorf("Cannot marshal the patch %v: %v", patches, err)
			return &response, metrics_admission.Error, resource
		}
		patchType := v1beta1.PatchTypeJSONPatch
		response.PatchType = &patchType
		response.Patch = patch
		klog.V(4).Infof("Sending patches: %v", patches)
	}

	var status metrics_admission.AdmissionStatus
	if len(patches) > 0 {
		status = metrics_admission.Applied
	} else {
		status = metrics_admission.Skipped
	}
	if resource == metrics_admission.Pod {
		metrics_admission.OnAdmittedPod(status == metrics_admission.Applied)
	}

	return &response, status, resource
}

// Serve is a handler function of AdmissionServer
func (s *AdmissionServer) Serve(w http.ResponseWriter, r *http.Request) {
	timer := metrics_admission.NewAdmissionLatency()

	var body []byte
	if r.Body != nil {
		if data, err := ioutil.ReadAll(r.Body); err == nil {
			body = data
		}
	}

	// verify the content type is accurate
	contentType := r.Header.Get("Content-Type")
	if contentType != "application/json" {
		klog.Errorf("contentType=%s, expect application/json", contentType)
		timer.Observe(metrics_admission.Error, metrics_admission.Unknown)
		return
	}

	reviewResponse, status, resource := s.admit(body)
	ar := v1beta1.AdmissionReview{
		Response: reviewResponse,
	}

	resp, err := json.Marshal(ar)
	if err != nil {
		klog.Error(err)
		timer.Observe(metrics_admission.Error, resource)
		return
	}

	if _, err := w.Write(resp); err != nil {
		klog.Error(err)
		timer.Observe(metrics_admission.Error, resource)
		return
	}

	timer.Observe(status, resource)
}
<commit_msg>Send UID too in AdmissionReview response<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package logic

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"k8s.io/api/admission/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/pod"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/pod/patch"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/limitrange"
	metrics_admission "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/admission"
	"k8s.io/klog/v2"
)

// AdmissionServer is an admission webhook server that modifies pod resources request based on VPA recommendation
type AdmissionServer struct {
	limitsChecker    limitrange.LimitRangeCalculator
	resourceHandlers map[metav1.GroupResource]resource.Handler
}

// NewAdmissionServer constructs new AdmissionServer
func NewAdmissionServer(podPreProcessor pod.PreProcessor,
	vpaPreProcessor vpa.PreProcessor,
	limitsChecker limitrange.LimitRangeCalculator,
	vpaMatcher vpa.Matcher,
	patchCalculators []patch.Calculator) *AdmissionServer {
	as := &AdmissionServer{limitsChecker, map[metav1.GroupResource]resource.Handler{}}
	as.RegisterResourceHandler(pod.NewResourceHandler(podPreProcessor, vpaMatcher, patchCalculators))
	as.RegisterResourceHandler(vpa.NewResourceHandler(vpaPreProcessor))
	return as
}

// RegisterResourceHandler allows to register a custom logic for handling given types of resources.
func (s *AdmissionServer) RegisterResourceHandler(resourceHandler resource.Handler) {
	s.resourceHandlers[resourceHandler.GroupResource()] = resourceHandler
}

func (s *AdmissionServer) admit(data []byte) (*v1beta1.AdmissionResponse, metrics_admission.AdmissionStatus, metrics_admission.AdmissionResource) {
	// we don't block the admission by default, even on unparsable JSON
	response := v1beta1.AdmissionResponse{}
	response.Allowed = true

	ar := v1beta1.AdmissionReview{}
	if err := json.Unmarshal(data, &ar); err != nil {
		klog.Error(err)
		return &response, metrics_admission.Error, metrics_admission.Unknown
	}

	response.UID = ar.Request.UID

	var patches []resource.PatchRecord
	var err error
	resource := metrics_admission.Unknown

	admittedGroupResource := metav1.GroupResource{
		Group:    ar.Request.Resource.Group,
		Resource: ar.Request.Resource.Resource,
	}

	handler, ok := s.resourceHandlers[admittedGroupResource]
	if ok {
		patches, err = handler.GetPatches(ar.Request)
		resource = handler.AdmissionResource()

		if handler.DisallowIncorrectObjects() && err != nil {
			// we don't let in problematic objects - late validation
			status := metav1.Status{}
			status.Status = "Failure"
			status.Message = err.Error()
			response.Result = &status
			response.Allowed = false
		}
	} else {
		patches, err = nil, fmt.Errorf("not supported resource type: %v", admittedGroupResource)
	}

	if err != nil {
		klog.Error(err)
		return &response, metrics_admission.Error, resource
	}

	if len(patches) > 0 {
		patch, err := json.Marshal(patches)
		if err != nil {
			klog.Errorf("Cannot marshal the patch %v: %v", patches, err)
			return &response, metrics_admission.Error, resource
		}
		patchType := v1beta1.PatchTypeJSONPatch
		response.PatchType = &patchType
		response.Patch = patch
		klog.V(4).Infof("Sending patches: %v", patches)
	}

	var status metrics_admission.AdmissionStatus
	if len(patches) > 0 {
		status = metrics_admission.Applied
	} else {
		status = metrics_admission.Skipped
	}
	if resource == metrics_admission.Pod {
		metrics_admission.OnAdmittedPod(status == metrics_admission.Applied)
	}

	return &response, status, resource
}

// Serve is a handler function of AdmissionServer
func (s *AdmissionServer) Serve(w http.ResponseWriter, r *http.Request) {
	timer := metrics_admission.NewAdmissionLatency()

	var body []byte
	if r.Body != nil {
		if data, err := ioutil.ReadAll(r.Body); err == nil {
			body = data
		}
	}

	// verify the content type is accurate
	contentType := r.Header.Get("Content-Type")
	if contentType != "application/json" {
		klog.Errorf("contentType=%s, expect application/json", contentType)
		timer.Observe(metrics_admission.Error, metrics_admission.Unknown)
		return
	}

	reviewResponse, status, resource := s.admit(body)
	ar := v1beta1.AdmissionReview{
		Response: reviewResponse,
	}

	resp, err := json.Marshal(ar)
	if err != nil {
		klog.Error(err)
		timer.Observe(metrics_admission.Error, resource)
		return
	}

	if _, err := w.Write(resp); err != nil {
		klog.Error(err)
		timer.Observe(metrics_admission.Error, resource)
		return
	}

	timer.Observe(status, resource)
}
<|endoftext|>


<commit_before>// +build linux

/*
   Copyright The containerd Authors.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package sys

import "golang.org/x/sys/unix"

// EpollCreate1 directly calls unix.EpollCreate1
func EpollCreate1(flag int) (int, error) {
	return unix.EpollCreate1(flag)
}

// EpollCtl directly calls unix.EpollCtl
func EpollCtl(epfd int, op int, fd int, event *unix.EpollEvent) error {
	return unix.EpollCtl(epfd, op, fd, event)
}

// EpollWait directly calls unix.EpollWait
func EpollWait(epfd int, events []unix.EpollEvent, msec int) (int, error) {
	return unix.EpollWait(epfd, events, msec)
}
<commit_msg>sys: deprecate EpollCreate1, EpollCtl, EpollWait<commit_after>// +build linux

/*
   Copyright The containerd Authors.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package sys

import "golang.org/x/sys/unix"

// EpollCreate1 is an alias for unix.EpollCreate1
// Deprecated: use golang.org/x/sys/unix.EpollCreate1
var EpollCreate1 = unix.EpollCreate1

// EpollCtl is an alias for unix.EpollCtl
// Deprecated: use golang.org/x/sys/unix.EpollCtl
var EpollCtl = unix.EpollCtl

// EpollWait is an alias for unix.EpollWait
// Deprecated: use golang.org/x/sys/unix.EpollWait
var EpollWait = unix.EpollWait
<|endoftext|>


<commit_before>package analytics

import (
	"bytes"
	"net/http"
	"net/url"
	"runtime"
	"strconv"

	"github.com/getlantern/flashlight/util"
	"github.com/getlantern/golog"
)

const (
	ApiEndpoint       = `https://ssl.google-analytics.com/collect`
	ProtocolVersion   = "1"
	DefaultInstanceId = "555"
)

var (
	log        = golog.LoggerFor("analytics")
	httpClient *http.Client
)

type HitType string

const (
	PageViewType HitType = "pageview"
	EventType    HitType = "event"
)

type PageView struct {
	Hostname string `param:"dh"`
	Pagename string `param:"dp"`
	Title    string `param:"dt"`
}

type Event struct {
	Category string `param:"ec"`
	Action   string `param:"ea"`
	Label    string `param:"el,omitempty"`
	Value    string `param:"ev,omitempty"`
}

type Payload struct {
	ClientId string `json:"clientId"`

	ClientVersion string `json:"clientVersion,omitempty"`

	ViewPortSize string `json:"viewPortSize,omitempty"`

	TrackingId string `json:"trackingId"`

	Language string `json:"language,omitempty"`

	ScreenColors string `json:"screenColors,omitempty"`

	ScreenResolution string `json:"screenResolution,omitempty"`

	Hostname string `json:"hostname,omitempty"`

	HitType HitType `json:"hitType,omitempty"`

	CustomVars map[string]string

	UserAgent string

	Event *Event
}

func Configure(trackingId string, version string, proxyAddr string) {
	var err error
	go func() {
		httpClient, err = util.HTTPClient("", proxyAddr)
		if err != nil {
			log.Errorf("Could not create HTTP client via %s: %s", proxyAddr, err)
			return
		}
		// Store new session info whenever client proxy is ready
		sessionEvent(trackingId, version)
	}()
}

// assemble list of parameters to send to GA
func collectArgs(payload *Payload) string {
	vals := make(url.Values, 0)

	// Add default payload
	vals.Add("v", ProtocolVersion)
	if payload.ClientVersion != "" {
		vals.Add("_v", payload.ClientVersion)
	}
	if payload.TrackingId != "" {
		vals.Add("tid", payload.TrackingId)
	}
	if payload.ClientId != "" {
		vals.Add("cid", payload.ClientId)
	}

	if payload.ScreenResolution != "" {
		vals.Add("sr", payload.ScreenResolution)
	}
	if payload.Language != "" {
		vals.Add("ul", payload.Language)
	}

	vals.Add("dh", payload.Hostname)

	vals.Add("t", string(payload.HitType))

	if payload.HitType == EventType && payload.Event != nil {
		vals.Add("ec", payload.Event.Category)
		vals.Add("ea", payload.Event.Action)
		if payload.Event.Label != "" {
			vals.Add("el", payload.Event.Label)
		}
		if payload.Event.Value != "" {
			vals.Add("ev", payload.Event.Value)
		}
	}

	for dim, customVar := range payload.CustomVars {
		if customVar != "" {
			vals.Add(dim, customVar)
		}
	}

	return vals.Encode()
}

// Makes a tracking request to Google Analytics
func SendRequest(payload *Payload) (status bool, err error) {
	if httpClient == nil {
		log.Error("No HTTP client; could not send HTTP request to GA")
		return false, nil
	}

	args := collectArgs(payload)

	r, err := http.NewRequest("POST", ApiEndpoint, bytes.NewBufferString(args))

	if err != nil {
		log.Errorf("Error constructing GA request: %s", err)
		return false, err
	}

	r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	r.Header.Add("Content-Length", strconv.Itoa(len(args)))

	resp, err := httpClient.Do(r)
	if err != nil {
		log.Errorf("Could not send HTTP request to GA: %s", err)
		return false, err
	}
	log.Debugf("Successfully sent request to GA: %s", resp.Status)
	defer resp.Body.Close()

	return true, nil
}

// Fired whenever a new Lanern session is initiated
func sessionEvent(trackingId string, version string) (status bool, err error) {

	sessionPayload := &Payload{
		HitType:    EventType,
		TrackingId: trackingId,
		Hostname:   "localhost",
		ClientId:   DefaultInstanceId,
		Event: &Event{
			Category: "Session",
			Action:   "Start",
			Label:    runtime.GOOS,
		},
	}

	if version != "" {
		sessionPayload.CustomVars = map[string]string{
			"cd1": version,
		}
	}
	return SendRequest(sessionPayload)
}
<commit_msg>Errcheck'ed github.com/getlantern/analytics<commit_after>package analytics

import (
	"bytes"
	"net/http"
	"net/url"
	"runtime"
	"strconv"

	"github.com/getlantern/flashlight/util"
	"github.com/getlantern/golog"
)

const (
	ApiEndpoint       = `https://ssl.google-analytics.com/collect`
	ProtocolVersion   = "1"
	DefaultInstanceId = "555"
)

var (
	log        = golog.LoggerFor("analytics")
	httpClient *http.Client
)

type HitType string

const (
	PageViewType HitType = "pageview"
	EventType    HitType = "event"
)

type PageView struct {
	Hostname string `param:"dh"`
	Pagename string `param:"dp"`
	Title    string `param:"dt"`
}

type Event struct {
	Category string `param:"ec"`
	Action   string `param:"ea"`
	Label    string `param:"el,omitempty"`
	Value    string `param:"ev,omitempty"`
}

type Payload struct {
	ClientId string `json:"clientId"`

	ClientVersion string `json:"clientVersion,omitempty"`

	ViewPortSize string `json:"viewPortSize,omitempty"`

	TrackingId string `json:"trackingId"`

	Language string `json:"language,omitempty"`

	ScreenColors string `json:"screenColors,omitempty"`

	ScreenResolution string `json:"screenResolution,omitempty"`

	Hostname string `json:"hostname,omitempty"`

	HitType HitType `json:"hitType,omitempty"`

	CustomVars map[string]string

	UserAgent string

	Event *Event
}

func Configure(trackingId string, version string, proxyAddr string) {
	var err error
	go func() {
		httpClient, err = util.HTTPClient("", proxyAddr)
		if err != nil {
			log.Errorf("Could not create HTTP client via %s: %s", proxyAddr, err)
			return
		}
		// Store new session info whenever client proxy is ready
		if status, err := sessionEvent(trackingId, version); err != nil {
			log.Debugf("Unable to store new session info: %v", err)
		} else {
			log.Tracef("Storing new session info: %v", status)
		}
	}()
}

// assemble list of parameters to send to GA
func collectArgs(payload *Payload) string {
	vals := make(url.Values, 0)

	// Add default payload
	vals.Add("v", ProtocolVersion)
	if payload.ClientVersion != "" {
		vals.Add("_v", payload.ClientVersion)
	}
	if payload.TrackingId != "" {
		vals.Add("tid", payload.TrackingId)
	}
	if payload.ClientId != "" {
		vals.Add("cid", payload.ClientId)
	}

	if payload.ScreenResolution != "" {
		vals.Add("sr", payload.ScreenResolution)
	}
	if payload.Language != "" {
		vals.Add("ul", payload.Language)
	}

	vals.Add("dh", payload.Hostname)

	vals.Add("t", string(payload.HitType))

	if payload.HitType == EventType && payload.Event != nil {
		vals.Add("ec", payload.Event.Category)
		vals.Add("ea", payload.Event.Action)
		if payload.Event.Label != "" {
			vals.Add("el", payload.Event.Label)
		}
		if payload.Event.Value != "" {
			vals.Add("ev", payload.Event.Value)
		}
	}

	for dim, customVar := range payload.CustomVars {
		if customVar != "" {
			vals.Add(dim, customVar)
		}
	}

	return vals.Encode()
}

// Makes a tracking request to Google Analytics
func SendRequest(payload *Payload) (status bool, err error) {
	if httpClient == nil {
		log.Error("No HTTP client; could not send HTTP request to GA")
		return false, nil
	}

	args := collectArgs(payload)

	r, err := http.NewRequest("POST", ApiEndpoint, bytes.NewBufferString(args))

	if err != nil {
		log.Errorf("Error constructing GA request: %s", err)
		return false, err
	}

	r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	r.Header.Add("Content-Length", strconv.Itoa(len(args)))

	resp, err := httpClient.Do(r)
	if err != nil {
		log.Errorf("Could not send HTTP request to GA: %s", err)
		return false, err
	}
	log.Debugf("Successfully sent request to GA: %s", resp.Status)
	defer func() {
		if err := resp.Body.Close(); err != nil {
			log.Debugf("Unable to close response body: %v", err)
		}
	}()

	return true, nil
}

// Fired whenever a new Lanern session is initiated
func sessionEvent(trackingId string, version string) (status bool, err error) {

	sessionPayload := &Payload{
		HitType:    EventType,
		TrackingId: trackingId,
		Hostname:   "localhost",
		ClientId:   DefaultInstanceId,
		Event: &Event{
			Category: "Session",
			Action:   "Start",
			Label:    runtime.GOOS,
		},
	}

	if version != "" {
		sessionPayload.CustomVars = map[string]string{
			"cd1": version,
		}
	}
	return SendRequest(sessionPayload)
}
<|endoftext|>