<commit_before>// Copyright 2016 The prometheus-operator 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 prometheus

import (
	"fmt"
	"regexp"
	"strings"

	yaml "gopkg.in/yaml.v2"
	"k8s.io/client-go/pkg/api/unversioned"

	"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1alpha1"
)

var (
	invalidLabelCharRE = regexp.MustCompile(`[^a-zA-Z0-9_]`)
)

func sanitizeLabelName(name string) string {
	return invalidLabelCharRE.ReplaceAllString(name, "_")
}

func configMapRuleFileFolder(configMapNumber int) string {
	return fmt.Sprintf("/etc/prometheus/rules/rules-%d/", configMapNumber)
}

func generateConfig(p *v1alpha1.Prometheus, mons map[string]*v1alpha1.ServiceMonitor, ruleConfigMaps int) ([]byte, error) {
	cfg := map[string]interface{}{}

	cfg["global"] = map[string]string{
		"evaluation_interval": "30s",
		"scrape_interval":     "30s",
	}

	if ruleConfigMaps > 0 {
		configMaps := make([]string, ruleConfigMaps)
		for i := 0; i < ruleConfigMaps; i++ {
			configMaps[i] = configMapRuleFileFolder(i) + "*.rules"
		}
		cfg["rule_files"] = configMaps
	}

	var scrapeConfigs []interface{}
	for _, mon := range mons {
		for i, ep := range mon.Spec.Endpoints {
			scrapeConfigs = append(scrapeConfigs, generateServiceMonitorConfig(mon, ep, i))
		}
	}
	var alertmanagerConfigs []interface{}
	for _, am := range p.Spec.Alerting.Alertmanagers {
		alertmanagerConfigs = append(alertmanagerConfigs, generateAlertmanagerConfig(am))
	}

	cfg["scrape_configs"] = scrapeConfigs
	cfg["alerting"] = map[string]interface{}{
		"alertmanagers": alertmanagerConfigs,
	}

	return yaml.Marshal(cfg)
}

func generateServiceMonitorConfig(m *v1alpha1.ServiceMonitor, ep v1alpha1.Endpoint, i int) interface{} {
	cfg := map[string]interface{}{
		"job_name": fmt.Sprintf("%s/%s/%d", m.Namespace, m.Name, i),
		"kubernetes_sd_configs": []map[string]interface{}{
			{
				"role": "endpoints",
			},
		},
	}

	if ep.Interval != "" {
		cfg["scrape_interval"] = ep.Interval
	}
	if ep.Path != "" {
		cfg["metrics_path"] = ep.Path
	}
	if ep.Scheme != "" {
		cfg["scheme"] = ep.Scheme
	}
	if ep.TLSConfig != nil {
		tlsConfig := map[string]interface{}{
			"insecure_skip_verify": ep.TLSConfig.InsecureSkipVerify,
		}
		if ep.TLSConfig.CAFile != "" {
			tlsConfig["ca_file"] = ep.TLSConfig.CAFile
		}
		if ep.TLSConfig.CertFile != "" {
			tlsConfig["cert_file"] = ep.TLSConfig.CertFile
		}
		if ep.TLSConfig.KeyFile != "" {
			tlsConfig["key_file"] = ep.TLSConfig.KeyFile
		}
		if ep.TLSConfig.ServerName != "" {
			tlsConfig["server_name"] = ep.TLSConfig.ServerName
		}
		cfg["tls_config"] = tlsConfig
	}
	if ep.BearerTokenFile != "" {
		cfg["bearer_token_file"] = ep.BearerTokenFile
	}

	var relabelings []interface{}

	// Filter targets by services selected by the monitor.

	// Exact label matches.
	for k, v := range m.Spec.Selector.MatchLabels {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(k)},
			"regex":         v,
		})
	}
	// Set based label matching. We have to map the valid relations
	// `In`, `NotIn`, `Exists`, and `DoesNotExist`, into relabeling rules.
	for _, exp := range m.Spec.Selector.MatchExpressions {
		switch exp.Operator {
		case unversioned.LabelSelectorOpIn:
			relabelings = append(relabelings, map[string]interface{}{
				"action":        "keep",
				"source_labels": []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(exp.Key)},
				"regex":         strings.Join(exp.Values, "|"),
			})
		case unversioned.LabelSelectorOpNotIn:
			relabelings = append(relabelings, map[string]interface{}{
				"action":        "drop",
				"source_labels": []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(exp.Key)},
				"regex":         strings.Join(exp.Values, "|"),
			})
		case unversioned.LabelSelectorOpExists:
			relabelings = append(relabelings, map[string]interface{}{
				"action":        "keep",
				"source_labels": []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(exp.Key)},
				"regex":         ".+",
			})
		case unversioned.LabelSelectorOpDoesNotExist:
			relabelings = append(relabelings, map[string]interface{}{
				"action":        "drop",
				"source_labels": []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(exp.Key)},
				"regex":         ".+",
			})
		}
	}

	// Filter targets based on the namespace selection configuration.
	// By default we only discover services within the namespace of the
	// ServiceMonitor.
	// Selections allow extending this to all namespaces or to a subset
	// of them specified by label or name matching.
	//
	// Label selections are not supported yet as they require either supported
	// in the upstream SD integration or require out-of-band implementation
	// in the operator with configuration reload.
	//
	// There's no explicit nil for the selector, we decide for the default
	// case if it's all zero values.
	nsel := m.Spec.NamespaceSelector

	if !nsel.Any && len(nsel.MatchNames) == 0 {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_namespace"},
			"regex":         m.Namespace,
		})
	} else if len(nsel.MatchNames) > 0 {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_namespace"},
			"regex":         strings.Join(nsel.MatchNames, "|"),
		})
	}

	// Filter targets based on correct port for the endpoint.
	if ep.Port != "" {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_endpoint_port_name"},
			"regex":         ep.Port,
		})
	} else if ep.TargetPort.StrVal != "" {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_container_port_name"},
			"regex":         ep.TargetPort.String(),
		})
	} else if ep.TargetPort.IntVal != 0 {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_container_port_number"},
			"regex":         ep.TargetPort.String(),
		})
	}

	// Relabel namespace and pod and service labels into proper labels.
	relabelings = append(relabelings, []interface{}{
		map[string]interface{}{
			"source_labels": []string{"__meta_kubernetes_namespace"},
			"target_label":  "namespace",
		},
		// If the annotation alpha.monitoring.coreos.com/namespaced is true we
		// want to remove the namespace label.
		map[string]interface{}{
			"source_labels": []string{"__meta_kubernetes_service_annotation_alpha_monitoring_coreos_com_non_namespaced"},
			"regex":         "true",
			"target_label":  "namespace",
			"replacement":   "",
		},
		map[string]interface{}{
			"action":      "labelmap",
			"regex":       "__meta_kubernetes_service_label_(.+)",
			"replacement": "svc_$1",
		},
		map[string]interface{}{
			"action":       "replace",
			"target_label": "__meta_kubernetes_pod_label_pod_template_hash",
			"replacement":  "",
		},
		map[string]interface{}{
			"action":      "labelmap",
			"regex":       "__meta_kubernetes_pod_label_(.+)",
			"replacement": "pod_$1",
		},
	}...)

	// By default, generate a safe job name from the service name.  We also keep
	// this around if a jobLabel is set in case the targets don't actually have a
	// value for it. A single service may potentially have multiple metrics
	// endpoints, therefore the endpoints labels is filled with the ports name or
	// as a fallback the port number.

	relabelings = append(relabelings, map[string]interface{}{
		"source_labels": []string{"__meta_kubernetes_service_name"},
		"target_label":  "job",
		"replacement":   "${1}",
	})
	if m.Spec.JobLabel != "" {
		relabelings = append(relabelings, map[string]interface{}{
			"source_labels": []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(m.Spec.JobLabel)},
			"target_label":  "job",
			"regex":         "(.+)",
			"replacement":   "${1}",
		})
	}

	if ep.Port != "" {
		relabelings = append(relabelings, map[string]interface{}{
			"target_label": "endpoint",
			"replacement":  ep.Port,
		})
	} else if ep.TargetPort.String() != "" {
		relabelings = append(relabelings, map[string]interface{}{
			"target_label": "endpoint",
			"replacement":  ep.TargetPort.String(),
		})
	}

	cfg["relabel_configs"] = relabelings

	return cfg
}

func generateAlertmanagerConfig(am v1alpha1.AlertmanagerEndpoints) interface{} {
	if am.Scheme == "" {
		am.Scheme = "http"
	}
	cfg := map[string]interface{}{
		"kubernetes_sd_configs": []map[string]interface{}{
			{
				"role": "endpoints",
			},
		},
		"scheme": am.Scheme,
	}

	var relabelings []interface{}

	relabelings = append(relabelings, map[string]interface{}{
		"action":        "keep",
		"source_labels": []string{"__meta_kubernetes_service_name"},
		"regex":         am.Name,
	})
	relabelings = append(relabelings, map[string]interface{}{
		"action":        "keep",
		"source_labels": []string{"__meta_kubernetes_namespace"},
		"regex":         am.Namespace,
	})

	if am.Port.StrVal != "" {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_endpoint_port_name"},
			"regex":         am.Port.String(),
		})
	} else if am.Port.IntVal != 0 {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_container_port_number"},
			"regex":         am.Port.String(),
		})
	}

	cfg["relabel_configs"] = relabelings

	return cfg
}
<commit_msg>prometheus: add pod and service label<commit_after>// Copyright 2016 The prometheus-operator 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 prometheus

import (
	"fmt"
	"regexp"
	"strings"

	yaml "gopkg.in/yaml.v2"
	"k8s.io/client-go/pkg/api/unversioned"

	"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1alpha1"
)

var (
	invalidLabelCharRE = regexp.MustCompile(`[^a-zA-Z0-9_]`)
)

func sanitizeLabelName(name string) string {
	return invalidLabelCharRE.ReplaceAllString(name, "_")
}

func configMapRuleFileFolder(configMapNumber int) string {
	return fmt.Sprintf("/etc/prometheus/rules/rules-%d/", configMapNumber)
}

func generateConfig(p *v1alpha1.Prometheus, mons map[string]*v1alpha1.ServiceMonitor, ruleConfigMaps int) ([]byte, error) {
	cfg := map[string]interface{}{}

	cfg["global"] = map[string]string{
		"evaluation_interval": "30s",
		"scrape_interval":     "30s",
	}

	if ruleConfigMaps > 0 {
		configMaps := make([]string, ruleConfigMaps)
		for i := 0; i < ruleConfigMaps; i++ {
			configMaps[i] = configMapRuleFileFolder(i) + "*.rules"
		}
		cfg["rule_files"] = configMaps
	}

	var scrapeConfigs []interface{}
	for _, mon := range mons {
		for i, ep := range mon.Spec.Endpoints {
			scrapeConfigs = append(scrapeConfigs, generateServiceMonitorConfig(mon, ep, i))
		}
	}
	var alertmanagerConfigs []interface{}
	for _, am := range p.Spec.Alerting.Alertmanagers {
		alertmanagerConfigs = append(alertmanagerConfigs, generateAlertmanagerConfig(am))
	}

	cfg["scrape_configs"] = scrapeConfigs
	cfg["alerting"] = map[string]interface{}{
		"alertmanagers": alertmanagerConfigs,
	}

	return yaml.Marshal(cfg)
}

func generateServiceMonitorConfig(m *v1alpha1.ServiceMonitor, ep v1alpha1.Endpoint, i int) interface{} {
	cfg := map[string]interface{}{
		"job_name": fmt.Sprintf("%s/%s/%d", m.Namespace, m.Name, i),
		"kubernetes_sd_configs": []map[string]interface{}{
			{
				"role": "endpoints",
			},
		},
	}

	if ep.Interval != "" {
		cfg["scrape_interval"] = ep.Interval
	}
	if ep.Path != "" {
		cfg["metrics_path"] = ep.Path
	}
	if ep.Scheme != "" {
		cfg["scheme"] = ep.Scheme
	}
	if ep.TLSConfig != nil {
		tlsConfig := map[string]interface{}{
			"insecure_skip_verify": ep.TLSConfig.InsecureSkipVerify,
		}
		if ep.TLSConfig.CAFile != "" {
			tlsConfig["ca_file"] = ep.TLSConfig.CAFile
		}
		if ep.TLSConfig.CertFile != "" {
			tlsConfig["cert_file"] = ep.TLSConfig.CertFile
		}
		if ep.TLSConfig.KeyFile != "" {
			tlsConfig["key_file"] = ep.TLSConfig.KeyFile
		}
		if ep.TLSConfig.ServerName != "" {
			tlsConfig["server_name"] = ep.TLSConfig.ServerName
		}
		cfg["tls_config"] = tlsConfig
	}
	if ep.BearerTokenFile != "" {
		cfg["bearer_token_file"] = ep.BearerTokenFile
	}

	var relabelings []interface{}

	// Filter targets by services selected by the monitor.

	// Exact label matches.
	for k, v := range m.Spec.Selector.MatchLabels {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(k)},
			"regex":         v,
		})
	}
	// Set based label matching. We have to map the valid relations
	// `In`, `NotIn`, `Exists`, and `DoesNotExist`, into relabeling rules.
	for _, exp := range m.Spec.Selector.MatchExpressions {
		switch exp.Operator {
		case unversioned.LabelSelectorOpIn:
			relabelings = append(relabelings, map[string]interface{}{
				"action":        "keep",
				"source_labels": []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(exp.Key)},
				"regex":         strings.Join(exp.Values, "|"),
			})
		case unversioned.LabelSelectorOpNotIn:
			relabelings = append(relabelings, map[string]interface{}{
				"action":        "drop",
				"source_labels": []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(exp.Key)},
				"regex":         strings.Join(exp.Values, "|"),
			})
		case unversioned.LabelSelectorOpExists:
			relabelings = append(relabelings, map[string]interface{}{
				"action":        "keep",
				"source_labels": []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(exp.Key)},
				"regex":         ".+",
			})
		case unversioned.LabelSelectorOpDoesNotExist:
			relabelings = append(relabelings, map[string]interface{}{
				"action":        "drop",
				"source_labels": []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(exp.Key)},
				"regex":         ".+",
			})
		}
	}

	// Filter targets based on the namespace selection configuration.
	// By default we only discover services within the namespace of the
	// ServiceMonitor.
	// Selections allow extending this to all namespaces or to a subset
	// of them specified by label or name matching.
	//
	// Label selections are not supported yet as they require either supported
	// in the upstream SD integration or require out-of-band implementation
	// in the operator with configuration reload.
	//
	// There's no explicit nil for the selector, we decide for the default
	// case if it's all zero values.
	nsel := m.Spec.NamespaceSelector

	if !nsel.Any && len(nsel.MatchNames) == 0 {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_namespace"},
			"regex":         m.Namespace,
		})
	} else if len(nsel.MatchNames) > 0 {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_namespace"},
			"regex":         strings.Join(nsel.MatchNames, "|"),
		})
	}

	// Filter targets based on correct port for the endpoint.
	if ep.Port != "" {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_endpoint_port_name"},
			"regex":         ep.Port,
		})
	} else if ep.TargetPort.StrVal != "" {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_container_port_name"},
			"regex":         ep.TargetPort.String(),
		})
	} else if ep.TargetPort.IntVal != 0 {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_container_port_number"},
			"regex":         ep.TargetPort.String(),
		})
	}

	// Relabel namespace and pod and service labels into proper labels.
	relabelings = append(relabelings, []interface{}{
		map[string]interface{}{
			"source_labels": []string{"__meta_kubernetes_namespace"},
			"target_label":  "namespace",
		},
		// If the annotation alpha.monitoring.coreos.com/namespaced is true we
		// want to remove the namespace label.
		map[string]interface{}{
			"source_labels": []string{"__meta_kubernetes_service_annotation_alpha_monitoring_coreos_com_non_namespaced"},
			"regex":         "true",
			"target_label":  "namespace",
			"replacement":   "",
		},
		map[string]interface{}{
			"action":      "labelmap",
			"regex":       "__meta_kubernetes_service_label_(.+)",
			"replacement": "svc_$1",
		},
		map[string]interface{}{
			"action":       "replace",
			"target_label": "__meta_kubernetes_pod_label_pod_template_hash",
			"replacement":  "",
		},
		map[string]interface{}{
			"action":      "labelmap",
			"regex":       "__meta_kubernetes_pod_label_(.+)",
			"replacement": "pod_$1",
		},
		map[string]interface{}{
			"source_labels": []string{"__meta_kubernetes_pod_name"},
			"target_label":  "pod",
		},
		map[string]interface{}{
			"source_labels": []string{"__meta_kubernetes_service_name"},
			"target_label":  "service",
		},
	}...)

	// By default, generate a safe job name from the service name.  We also keep
	// this around if a jobLabel is set in case the targets don't actually have a
	// value for it. A single service may potentially have multiple metrics
	// endpoints, therefore the endpoints labels is filled with the ports name or
	// as a fallback the port number.

	relabelings = append(relabelings, map[string]interface{}{
		"source_labels": []string{"__meta_kubernetes_service_name"},
		"target_label":  "job",
		"replacement":   "${1}",
	})
	if m.Spec.JobLabel != "" {
		relabelings = append(relabelings, map[string]interface{}{
			"source_labels": []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(m.Spec.JobLabel)},
			"target_label":  "job",
			"regex":         "(.+)",
			"replacement":   "${1}",
		})
	}

	if ep.Port != "" {
		relabelings = append(relabelings, map[string]interface{}{
			"target_label": "endpoint",
			"replacement":  ep.Port,
		})
	} else if ep.TargetPort.String() != "" {
		relabelings = append(relabelings, map[string]interface{}{
			"target_label": "endpoint",
			"replacement":  ep.TargetPort.String(),
		})
	}

	cfg["relabel_configs"] = relabelings

	return cfg
}

func generateAlertmanagerConfig(am v1alpha1.AlertmanagerEndpoints) interface{} {
	if am.Scheme == "" {
		am.Scheme = "http"
	}
	cfg := map[string]interface{}{
		"kubernetes_sd_configs": []map[string]interface{}{
			{
				"role": "endpoints",
			},
		},
		"scheme": am.Scheme,
	}

	var relabelings []interface{}

	relabelings = append(relabelings, map[string]interface{}{
		"action":        "keep",
		"source_labels": []string{"__meta_kubernetes_service_name"},
		"regex":         am.Name,
	})
	relabelings = append(relabelings, map[string]interface{}{
		"action":        "keep",
		"source_labels": []string{"__meta_kubernetes_namespace"},
		"regex":         am.Namespace,
	})

	if am.Port.StrVal != "" {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_endpoint_port_name"},
			"regex":         am.Port.String(),
		})
	} else if am.Port.IntVal != 0 {
		relabelings = append(relabelings, map[string]interface{}{
			"action":        "keep",
			"source_labels": []string{"__meta_kubernetes_container_port_number"},
			"regex":         am.Port.String(),
		})
	}

	cfg["relabel_configs"] = relabelings

	return cfg
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package isupport

import (
	"fmt"
	"sort"
	"strings"
)

const (
	maxLastArgLength = 400
)

// List holds a list of ISUPPORT tokens
type List struct {
	Tokens      map[string]*string
	CachedReply [][]string
}

// NewList returns a new List
func NewList() *List {
	var il List
	il.Initialize()
	return &il
}

func (il *List) Initialize() {
	il.Tokens = make(map[string]*string)
	il.CachedReply = make([][]string, 0)
}

// Add adds an RPL_ISUPPORT token to our internal list
func (il *List) Add(name string, value string) {
	il.Tokens[name] = &value
}

// AddNoValue adds an RPL_ISUPPORT token that does not have a value
func (il *List) AddNoValue(name string) {
	il.Tokens[name] = nil
}

// getTokenString gets the appropriate string for a token+value.
func getTokenString(name string, value *string) string {
	if value == nil || len(*value) == 0 {
		return name
	}

	return fmt.Sprintf("%s=%s", name, *value)
}

// GetDifference returns the difference between two token lists.
func (il *List) GetDifference(newil *List) [][]string {
	var outTokens sort.StringSlice

	// append removed tokens
	for name := range il.Tokens {
		_, exists := newil.Tokens[name]
		if exists {
			continue
		}

		token := fmt.Sprintf("-%s", name)

		outTokens = append(outTokens, token)
	}

	// append added tokens
	for name, value := range newil.Tokens {
		newval, exists := il.Tokens[name]
		if exists && ((value == nil && newval == nil) || (value != nil && newval != nil && *value == *newval)) {
			continue
		}

		token := getTokenString(name, value)

		outTokens = append(outTokens, token)
	}

	sort.Sort(outTokens)

	// create output list
	replies := make([][]string, 0)
	var length int     // Length of the current cache
	var cache []string // Token list cache

	for _, token := range outTokens {
		if len(token)+length <= maxLastArgLength {
			// account for the space separating tokens
			if len(cache) > 0 {
				length++
			}
			cache = append(cache, token)
			length += len(token)
		}

		if len(cache) == 13 || len(token)+length >= maxLastArgLength {
			replies = append(replies, cache)
			cache = make([]string, 0)
			length = 0
		}
	}

	if len(cache) > 0 {
		replies = append(replies, cache)
	}

	return replies
}

// RegenerateCachedReply regenerates the cached RPL_ISUPPORT reply
func (il *List) RegenerateCachedReply() (err error) {
	il.CachedReply = make([][]string, 0)
	var length int     // Length of the current cache
	var cache []string // Token list cache

	// make sure we get a sorted list of tokens, needed for tests and looks nice
	var tokens sort.StringSlice
	for name := range il.Tokens {
		tokens = append(tokens, name)
	}
	sort.Sort(tokens)

	for _, name := range tokens {
		token := getTokenString(name, il.Tokens[name])
		if token[0] == ':' || strings.Contains(token, " ") {
			err = fmt.Errorf("bad isupport token (cannot contain spaces or start with :): %s", token)
			continue
		}

		if len(token)+length <= maxLastArgLength {
			// account for the space separating tokens
			if len(cache) > 0 {
				length++
			}
			cache = append(cache, token)
			length += len(token)
		}

		if len(cache) == 13 || len(token)+length >= maxLastArgLength {
			il.CachedReply = append(il.CachedReply, cache)
			cache = make([]string, 0)
			length = 0
		}
	}

	if len(cache) > 0 {
		il.CachedReply = append(il.CachedReply, cache)
	}

	return
}
<commit_msg>isupport List.Tokens map[string]*string -> map[string]string<commit_after>// Copyright (c) 2016 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package isupport

import (
	"fmt"
	"sort"
	"strings"
)

const (
	maxLastArgLength = 400
)

// List holds a list of ISUPPORT tokens
type List struct {
	Tokens      map[string]string
	CachedReply [][]string
}

// NewList returns a new List
func NewList() *List {
	var il List
	il.Initialize()
	return &il
}

func (il *List) Initialize() {
	il.Tokens = make(map[string]string)
	il.CachedReply = make([][]string, 0)
}

// Add adds an RPL_ISUPPORT token to our internal list
func (il *List) Add(name string, value string) {
	il.Tokens[name] = value
}

// AddNoValue adds an RPL_ISUPPORT token that does not have a value
func (il *List) AddNoValue(name string) {
	il.Tokens[name] = ""
}

// getTokenString gets the appropriate string for a token+value.
func getTokenString(name string, value string) string {
	if len(value) == 0 {
		return name
	}

	return fmt.Sprintf("%s=%s", name, value)
}

// GetDifference returns the difference between two token lists.
func (il *List) GetDifference(newil *List) [][]string {
	var outTokens sort.StringSlice

	// append removed tokens
	for name := range il.Tokens {
		_, exists := newil.Tokens[name]
		if exists {
			continue
		}

		token := fmt.Sprintf("-%s", name)

		outTokens = append(outTokens, token)
	}

	// append added tokens
	for name, value := range newil.Tokens {
		newval, exists := il.Tokens[name]
		if exists && value == newval {
			continue
		}

		token := getTokenString(name, value)

		outTokens = append(outTokens, token)
	}

	sort.Sort(outTokens)

	// create output list
	replies := make([][]string, 0)
	var length int     // Length of the current cache
	var cache []string // Token list cache

	for _, token := range outTokens {
		if len(token)+length <= maxLastArgLength {
			// account for the space separating tokens
			if len(cache) > 0 {
				length++
			}
			cache = append(cache, token)
			length += len(token)
		}

		if len(cache) == 13 || len(token)+length >= maxLastArgLength {
			replies = append(replies, cache)
			cache = make([]string, 0)
			length = 0
		}
	}

	if len(cache) > 0 {
		replies = append(replies, cache)
	}

	return replies
}

// RegenerateCachedReply regenerates the cached RPL_ISUPPORT reply
func (il *List) RegenerateCachedReply() (err error) {
	il.CachedReply = make([][]string, 0)
	var length int     // Length of the current cache
	var cache []string // Token list cache

	// make sure we get a sorted list of tokens, needed for tests and looks nice
	var tokens sort.StringSlice
	for name := range il.Tokens {
		tokens = append(tokens, name)
	}
	sort.Sort(tokens)

	for _, name := range tokens {
		token := getTokenString(name, il.Tokens[name])
		if token[0] == ':' || strings.Contains(token, " ") {
			err = fmt.Errorf("bad isupport token (cannot contain spaces or start with :): %s", token)
			continue
		}

		if len(token)+length <= maxLastArgLength {
			// account for the space separating tokens
			if len(cache) > 0 {
				length++
			}
			cache = append(cache, token)
			length += len(token)
		}

		if len(cache) == 13 || len(token)+length >= maxLastArgLength {
			il.CachedReply = append(il.CachedReply, cache)
			cache = make([]string, 0)
			length = 0
		}
	}

	if len(cache) > 0 {
		il.CachedReply = append(il.CachedReply, cache)
	}

	return
}
<|endoftext|>


<commit_before>package kernel

import (
	"time"
	"crypto/sha512" // XXX questo non serve piú
)

// XXX ho segnato con XXX i punti dove farei alcune correzioni
// Correggo solo per la prima volta, non voglio scassarti la minchia

type User struct {
	BaseModel
	UserName string
	PswHash  string
	LastLog  time.Time
}

func NewUser(name string, psw string) *User {
	u := new(User)
	u.BaseModel = *NewBaseModel()
	u.UserName = name
	u.PswHash = PswToHash(psw)
	// XXX ricorda il return!
}

// XXX questa forse puoi scriverla minuscola (se
// la funzione viene sicuramente usata solo in questo file
// [in realta solo in kernel] allora puoi mettere all'inizio
// la lettera minuscola, cosí dall'esterno non si
// vede)
func PswToHash(psw string) hash {
	//ByteHash := sha512.Sum512_256( []byte(psw) )
	salt :=
	// XXX da un occhiata al 4096, credo sia la lunghezza (in bit?) della stringa
	// meglio se non é proprio 1 KB per password.
	// Devi anche importare la libreria che probabilmente va installata
	pbkdf2.Key([]byte(psw), salt, 4096, 32, sha1.New)
	hash = string(ByteHash[:32])
	return hash
}

// XXX No credo si possano comparare le stringhe con ==
// not sure if we need this
func CompareHash(hash1, hash2 string) bool {
	ans := Compare(hash1, hash2)
	if ans == 0 {
		return true
	} // XXX else deve essere sulla stessa riga, Go spacca le balle su queste cose
	else {
		return false
	}
}

func LogIn(name string, psw string) {
	u := FindUser(name)
	if CompareHash(PswToHash(psw),u.Pswhash) {
		u.LastLogin = time.UTC()
	}
	else {
		LoginFailed()
	}
}

func FindUser(name string) *User {

}

func LoginFailed() {

}
<commit_msg>user authentication saved<commit_after>package kernel

import (
	"time"
	"strings"
	"golang.org/x/crypto/pbkdf2"
	"math/rand"
	"sync"
	"crypto/sha512"
)

const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
    letterIdxBits = 6                    // 6 bits to represent a letter index (52 letters + 10 digits = 62 < 2^6=64)
    letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
    letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
)
const saltLen = 10
var src = rand.NewSource(time.Now().UnixNano())

var mutex sync.Mutex

func int63() int64 {
	mutex.Lock()
	v := src.Int63()
	mutex.Unlock()
	return v
}

func RandStringBytesMaskImprSrc(n int) string {
	// default n := 10
    b := make([]byte, n)
    // A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
    for i, cache, remain := n-1, int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
            b[i] = letterBytes[idx]
            i--
        }
        cache >>= letterIdxBits
        remain--
    }

    return string(b)
}

// XXX ho segnato con XXX i punti dove farei alcune correzioni
// Correggo solo per la prima volta, non voglio scassarti la minchia

type User struct {
	BaseModel
	UserName string
	Salt string
	PswHash  string
	CreatedOn time.Time
	LastLog  time.Time
}

func NewUser(name string, psw string) *User {
	u := new(User)
	u.BaseModel = *NewBaseModel()
	u.UserName = name

	u.Salt = RandStringBytesMaskImprSrc(saltLen)
	u.PswHash = pswToHash(psw, u.Salt)

	u.CreatedOn = time.Now().UTC()
	u.LastLog = u.CreatedOn

	return u
}

func pswToHash(psw string, salt string) string {
	// XXX da un occhiata al 4096, credo sia la lunghezza (in bit?) della stringa
	// meglio se non é proprio 1 KB per password.
	// dovrebbe essere il numero di iterazioni di hash: eg. hash(hash(hash(...hash(salt||psw) ...)))
	ByteHash := pbkdf2.Key([]byte(psw), []byte(salt), 4096, 32, sha512.New)
	hash := string(ByteHash[:32])

	return hash
}

// not sure if we need this
/*func CompareHash(hash1, hash2 string) bool {
	ans := Compare(hash1, hash2)
	if ans == 0 {
		return true
	} // XXX else deve essere sulla stessa riga, Go spacca le balle su queste cose
	else {
		return false
	}
}*/

func LogIn(name string, psw string) {
	var u User
	All("User").Filter("username", "=", name).Get(&u)

	switch strings.Compare(pswToHash(psw, u.Salt),u.PswHash) {
	case 0:
		u.LastLog = time.Now().UTC()
	case 1,-1:
		LogInFailed()
	}
}

/* used only in case of many repetition of the function
 * func FindUser(name string) *User {
	return All("User").Filter("username", "=", name).Get(&u)
}*/

func LogInFailed() {

}
<|endoftext|>


<commit_before>package smokescreen

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strconv"
	"sync/atomic"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	log "github.com/sirupsen/logrus"
	acl "github.com/stripe/smokescreen/pkg/smokescreen/acl/v1"
	"github.com/stripe/smokescreen/pkg/smokescreen/conntrack"
)

type RuleRange struct {
	Net  net.IPNet
	Port int
}

type Config struct {
	Ip                           string
	Port                         uint16
	Listener                     net.Listener
	DenyRanges                   []RuleRange
	AllowRanges                  []RuleRange
	Resolver                     *net.Resolver
	ConnectTimeout               time.Duration
	ExitTimeout                  time.Duration
	StatsdClient                 *statsd.Client
	EgressACL                    acl.Decider
	SupportProxyProtocol         bool
	TlsConfig                    *tls.Config
	CrlByAuthorityKeyId          map[string]*pkix.CertificateList
	RoleFromRequest              func(subject *http.Request) (string, error)
	clientCasBySubjectKeyId      map[string]*x509.Certificate
	AdditionalErrorMessageOnDeny string
	Log                          *log.Logger
	DisabledAclPolicyActions     []string
	AllowMissingRole             bool
	StatsSocketDir               string
	StatsSocketFileMode          os.FileMode
	StatsServer                  *StatsServer // StatsServer
	ConnTracker                  *conntrack.Tracker
	Healthcheck                  http.Handler // User defined http.Handler for optional requests to a /healthcheck endpoint
	ShuttingDown                 atomic.Value // Stores a boolean value indicating whether the proxy is actively shutting down

	// A connection is idle if it has been inactive (no bytes in/out) for this many seconds.
	IdleTimeout time.Duration

	// These are *only* used for traditional HTTP proxy requests
	TransportMaxIdleConns        int
	TransportMaxIdleConnsPerHost int

	// Used for logging connection time
	TimeConnect bool

	// Custom Dial Timeout function to be called
	ProxyDialTimeout             func(network, address string, timeout time.Duration) (Conn, error)
}

type missingRoleError struct {
	error
}

func MissingRoleError(s string) error {
	return missingRoleError{errors.New(s)}
}

func IsMissingRoleError(err error) bool {
	_, ok := err.(missingRoleError)
	return ok
}

func parseRanges(rangeStrings []string) ([]RuleRange, error) {
	outRanges := make([]RuleRange, len(rangeStrings))
	for i, str := range rangeStrings {
		_, ipnet, err := net.ParseCIDR(str)
		if err != nil {
			return outRanges, err
		}
		outRanges[i].Net = *ipnet
	}
	return outRanges, nil
}

func parseAddresses(addressStrings []string) ([]RuleRange, error) {
	outRanges := make([]RuleRange, len(addressStrings))
	for i, str := range addressStrings {
		ip := net.ParseIP(str)
		if ip == nil {
			ipStr, portStr, err := net.SplitHostPort(str)
			if err != nil {
				return outRanges, fmt.Errorf("address must be in the form ip[:port], got %s", str)
			}

			ip = net.ParseIP(ipStr)
			if ip == nil {
				return outRanges, fmt.Errorf("invalid IP address '%s'", ipStr)
			}

			port, err := strconv.Atoi(portStr)
			if err != nil {
				return outRanges, fmt.Errorf("invalid port number '%s'", portStr)
			}

			outRanges[i].Port = port
		}

		var mask net.IPMask
		if ip.To4() != nil {
			mask = net.CIDRMask(32, 32)
		} else {
			mask = net.CIDRMask(128, 128)
		}

		outRanges[i].Net = net.IPNet{
			IP:   ip,
			Mask: mask,
		}
	}
	return outRanges, nil
}

func (config *Config) SetDenyRanges(rangeStrings []string) error {
	var err error
	ranges, err := parseRanges(rangeStrings)
	if err != nil {
		return err
	}
	config.DenyRanges = append(config.DenyRanges, ranges...)
	return nil
}

func (config *Config) SetAllowRanges(rangeStrings []string) error {
	var err error
	ranges, err := parseRanges(rangeStrings)
	if err != nil {
		return err
	}
	config.AllowRanges = append(config.AllowRanges, ranges...)
	return nil
}

func (config *Config) SetDenyAddresses(addressStrings []string) error {
	var err error
	ranges, err := parseAddresses(addressStrings)
	if err != nil {
		return err
	}
	config.DenyRanges = append(config.DenyRanges, ranges...)
	return nil
}

func (config *Config) SetAllowAddresses(addressStrings []string) error {
	var err error
	ranges, err := parseAddresses(addressStrings)
	if err != nil {
		return err
	}
	config.AllowRanges = append(config.AllowRanges, ranges...)
	return nil
}

func (config *Config) SetResolverAddresses(resolverAddresses []string) error {
	// TODO: support round-robin between multiple addresses
	if len(resolverAddresses) > 1 {
		return fmt.Errorf("only one resolver address allowed, %d provided", len(resolverAddresses))
	}

	// No resolver specified, use the system resolver
	if len(resolverAddresses) == 0 {
		return nil
	}

	addr := resolverAddresses[0]
	_, _, err := net.SplitHostPort(addr)
	if err != nil {
		return err
	}

	r := net.Resolver{
		PreferGo: true,
		Dial: func(ctx context.Context, _, _ string) (net.Conn, error) {
			d := net.Dialer{}
			return d.DialContext(ctx, "udp", addr)
		},
	}
	config.Resolver = &r
	return nil
}

// RFC 5280,  4.2.1.1
type authKeyId struct {
	Id []byte `asn1:"optional,tag:0"`
}

func NewConfig() *Config {
	return &Config{
		CrlByAuthorityKeyId:     make(map[string]*pkix.CertificateList),
		clientCasBySubjectKeyId: make(map[string]*x509.Certificate),
		Log:                     log.New(),
		Port:                    4750,
		ExitTimeout:             500 * time.Minute,
		StatsSocketFileMode:     os.FileMode(0700),
		ShuttingDown:            atomic.Value{},
	}
}

func (config *Config) SetupCrls(crlFiles []string) error {
	for _, crlFile := range crlFiles {
		crlBytes, err := ioutil.ReadFile(crlFile)
		if err != nil {
			return err
		}

		certList, err := x509.ParseCRL(crlBytes)
		if err != nil {
			log.Printf("Failed to parse CRL in '%s': %#v\n", crlFile, err)
		}

		// find the X509v3 Authority Key Identifier in the extensions (2.5.29.35)
		crlIssuerId := ""
		extensionOid := []int{2, 5, 29, 35}
		for _, v := range certList.TBSCertList.Extensions {
			if v.Id.Equal(extensionOid) { // Hurray, we found it
				// Boo, it's ASN.1.
				var crlAuthorityKey authKeyId
				_, err := asn1.Unmarshal(v.Value, &crlAuthorityKey)
				if err != nil {
					fmt.Printf("error: Failed to read AuthorityKey: %#v\n", err)
					continue
				}
				crlIssuerId = string(crlAuthorityKey.Id)
				break
			}
		}
		if crlIssuerId == "" {
			log.Print(fmt.Errorf("error: CRL from '%s' has no Authority Key Identifier: ignoring it\n", crlFile))
			continue
		}

		// Make sure we have a CA for this CRL or warn
		caCert, ok := config.clientCasBySubjectKeyId[crlIssuerId]

		if !ok {
			log.Printf("warn: CRL loaded for issuer '%s' but no such CA loaded: ignoring it\n", hex.EncodeToString([]byte(crlIssuerId)))
			fmt.Printf("%#v loaded certs\n", len(config.clientCasBySubjectKeyId))
			continue
		}

		// At this point, we have the CA certificate and the CRL. All that's left before evicting the CRL we currently trust is to verify the new CRL's signature
		err = caCert.CheckCRLSignature(certList)
		if err != nil {
			fmt.Printf("error: Could not trust CRL. Error during signature check: %#v\n", err)
			continue
		}

		// At this point, we have a new CRL which we trust. Let's evict the old one.
		config.CrlByAuthorityKeyId[crlIssuerId] = certList
		fmt.Printf("info: Loaded CRL for Authority ID '%s'\n", hex.EncodeToString([]byte(crlIssuerId)))
	}

	// Verify that all CAs loaded have a CRL
	for k, _ := range config.clientCasBySubjectKeyId {
		_, ok := config.CrlByAuthorityKeyId[k]
		if !ok {
			fmt.Printf("warn: no CRL loaded for Authority ID '%s'\n", hex.EncodeToString([]byte(k)))
		}
	}
	return nil
}

func (config *Config) SetupStatsdWithNamespace(addr, namespace string) error {
	if addr == "" {
		config.StatsdClient = nil
		return nil
	}

	client, err := statsd.New(addr)
	if err != nil {
		return err
	}

	config.StatsdClient = client

	config.StatsdClient.Namespace = namespace

	return nil
}

func (config *Config) SetupStatsd(addr string) error {
	return config.SetupStatsdWithNamespace(addr, DefaultStatsdNamespace)
}

func (config *Config) SetupEgressAcl(aclFile string) error {
	if aclFile == "" {
		config.EgressACL = nil
		return nil
	}

	log.Printf("Loading egress ACL from %s", aclFile)

	egressACL, err := acl.New(config.Log, acl.NewYAMLLoader(aclFile), config.DisabledAclPolicyActions)
	if err != nil {
		log.Print(err)
		return err
	}
	config.EgressACL = egressACL

	return nil
}

func addCertsFromFile(config *Config, pool *x509.CertPool, fileName string) error {
	data, err := ioutil.ReadFile(fileName)

	//TODO this is a bit awkward
	config.populateClientCaMap(data)

	if err != nil {
		return err
	}
	ok := pool.AppendCertsFromPEM(data)
	if !ok {
		return fmt.Errorf("Failed to load any certificates from file '%s'", fileName)
	}
	return nil
}

// certFile and keyFile may be the same file containing concatenated PEM blocks
func (config *Config) SetupTls(certFile, keyFile string, clientCAFiles []string) error {
	if certFile == "" || keyFile == "" {
		return errors.New("both certificate and key files must be specified to set up TLS")
	}

	serverCert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	clientAuth := tls.NoClientCert
	clientCAs := x509.NewCertPool()

	if len(clientCAFiles) != 0 {
		clientAuth = tls.VerifyClientCertIfGiven
		for _, caFile := range clientCAFiles {
			err = addCertsFromFile(config, clientCAs, caFile)
			if err != nil {
				return err
			}
		}
	}

	config.TlsConfig = &tls.Config{
		Certificates: []tls.Certificate{serverCert},
		ClientAuth:   clientAuth,
		ClientCAs:    clientCAs,
	}

	return nil
}

func (config *Config) populateClientCaMap(pemCerts []byte) (ok bool) {

	for len(pemCerts) > 0 {
		var block *pem.Block
		block, pemCerts = pem.Decode(pemCerts)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}

		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			continue
		}
		fmt.Printf("info: Loaded CA with Authority ID '%s'\n", hex.EncodeToString(cert.SubjectKeyId))
		config.clientCasBySubjectKeyId[string(cert.SubjectKeyId)] = cert
		ok = true
	}
	return
}
<commit_msg>fix import issue<commit_after>package smokescreen

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strconv"
	"sync/atomic"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	log "github.com/sirupsen/logrus"
	acl "github.com/stripe/smokescreen/pkg/smokescreen/acl/v1"
	"github.com/stripe/smokescreen/pkg/smokescreen/conntrack"
)

type RuleRange struct {
	Net  net.IPNet
	Port int
}

type Config struct {
	Ip                           string
	Port                         uint16
	Listener                     net.Listener
	DenyRanges                   []RuleRange
	AllowRanges                  []RuleRange
	Resolver                     *net.Resolver
	ConnectTimeout               time.Duration
	ExitTimeout                  time.Duration
	StatsdClient                 *statsd.Client
	EgressACL                    acl.Decider
	SupportProxyProtocol         bool
	TlsConfig                    *tls.Config
	CrlByAuthorityKeyId          map[string]*pkix.CertificateList
	RoleFromRequest              func(subject *http.Request) (string, error)
	clientCasBySubjectKeyId      map[string]*x509.Certificate
	AdditionalErrorMessageOnDeny string
	Log                          *log.Logger
	DisabledAclPolicyActions     []string
	AllowMissingRole             bool
	StatsSocketDir               string
	StatsSocketFileMode          os.FileMode
	StatsServer                  *StatsServer // StatsServer
	ConnTracker                  *conntrack.Tracker
	Healthcheck                  http.Handler // User defined http.Handler for optional requests to a /healthcheck endpoint
	ShuttingDown                 atomic.Value // Stores a boolean value indicating whether the proxy is actively shutting down

	// A connection is idle if it has been inactive (no bytes in/out) for this many seconds.
	IdleTimeout time.Duration

	// These are *only* used for traditional HTTP proxy requests
	TransportMaxIdleConns        int
	TransportMaxIdleConnsPerHost int

	// Used for logging connection time
	TimeConnect bool

	// Custom Dial Timeout function to be called
	ProxyDialTimeout             func(network, address string, timeout time.Duration) (net.Conn, error)
}

type missingRoleError struct {
	error
}

func MissingRoleError(s string) error {
	return missingRoleError{errors.New(s)}
}

func IsMissingRoleError(err error) bool {
	_, ok := err.(missingRoleError)
	return ok
}

func parseRanges(rangeStrings []string) ([]RuleRange, error) {
	outRanges := make([]RuleRange, len(rangeStrings))
	for i, str := range rangeStrings {
		_, ipnet, err := net.ParseCIDR(str)
		if err != nil {
			return outRanges, err
		}
		outRanges[i].Net = *ipnet
	}
	return outRanges, nil
}

func parseAddresses(addressStrings []string) ([]RuleRange, error) {
	outRanges := make([]RuleRange, len(addressStrings))
	for i, str := range addressStrings {
		ip := net.ParseIP(str)
		if ip == nil {
			ipStr, portStr, err := net.SplitHostPort(str)
			if err != nil {
				return outRanges, fmt.Errorf("address must be in the form ip[:port], got %s", str)
			}

			ip = net.ParseIP(ipStr)
			if ip == nil {
				return outRanges, fmt.Errorf("invalid IP address '%s'", ipStr)
			}

			port, err := strconv.Atoi(portStr)
			if err != nil {
				return outRanges, fmt.Errorf("invalid port number '%s'", portStr)
			}

			outRanges[i].Port = port
		}

		var mask net.IPMask
		if ip.To4() != nil {
			mask = net.CIDRMask(32, 32)
		} else {
			mask = net.CIDRMask(128, 128)
		}

		outRanges[i].Net = net.IPNet{
			IP:   ip,
			Mask: mask,
		}
	}
	return outRanges, nil
}

func (config *Config) SetDenyRanges(rangeStrings []string) error {
	var err error
	ranges, err := parseRanges(rangeStrings)
	if err != nil {
		return err
	}
	config.DenyRanges = append(config.DenyRanges, ranges...)
	return nil
}

func (config *Config) SetAllowRanges(rangeStrings []string) error {
	var err error
	ranges, err := parseRanges(rangeStrings)
	if err != nil {
		return err
	}
	config.AllowRanges = append(config.AllowRanges, ranges...)
	return nil
}

func (config *Config) SetDenyAddresses(addressStrings []string) error {
	var err error
	ranges, err := parseAddresses(addressStrings)
	if err != nil {
		return err
	}
	config.DenyRanges = append(config.DenyRanges, ranges...)
	return nil
}

func (config *Config) SetAllowAddresses(addressStrings []string) error {
	var err error
	ranges, err := parseAddresses(addressStrings)
	if err != nil {
		return err
	}
	config.AllowRanges = append(config.AllowRanges, ranges...)
	return nil
}

func (config *Config) SetResolverAddresses(resolverAddresses []string) error {
	// TODO: support round-robin between multiple addresses
	if len(resolverAddresses) > 1 {
		return fmt.Errorf("only one resolver address allowed, %d provided", len(resolverAddresses))
	}

	// No resolver specified, use the system resolver
	if len(resolverAddresses) == 0 {
		return nil
	}

	addr := resolverAddresses[0]
	_, _, err := net.SplitHostPort(addr)
	if err != nil {
		return err
	}

	r := net.Resolver{
		PreferGo: true,
		Dial: func(ctx context.Context, _, _ string) (net.Conn, error) {
			d := net.Dialer{}
			return d.DialContext(ctx, "udp", addr)
		},
	}
	config.Resolver = &r
	return nil
}

// RFC 5280,  4.2.1.1
type authKeyId struct {
	Id []byte `asn1:"optional,tag:0"`
}

func NewConfig() *Config {
	return &Config{
		CrlByAuthorityKeyId:     make(map[string]*pkix.CertificateList),
		clientCasBySubjectKeyId: make(map[string]*x509.Certificate),
		Log:                     log.New(),
		Port:                    4750,
		ExitTimeout:             500 * time.Minute,
		StatsSocketFileMode:     os.FileMode(0700),
		ShuttingDown:            atomic.Value{},
	}
}

func (config *Config) SetupCrls(crlFiles []string) error {
	for _, crlFile := range crlFiles {
		crlBytes, err := ioutil.ReadFile(crlFile)
		if err != nil {
			return err
		}

		certList, err := x509.ParseCRL(crlBytes)
		if err != nil {
			log.Printf("Failed to parse CRL in '%s': %#v\n", crlFile, err)
		}

		// find the X509v3 Authority Key Identifier in the extensions (2.5.29.35)
		crlIssuerId := ""
		extensionOid := []int{2, 5, 29, 35}
		for _, v := range certList.TBSCertList.Extensions {
			if v.Id.Equal(extensionOid) { // Hurray, we found it
				// Boo, it's ASN.1.
				var crlAuthorityKey authKeyId
				_, err := asn1.Unmarshal(v.Value, &crlAuthorityKey)
				if err != nil {
					fmt.Printf("error: Failed to read AuthorityKey: %#v\n", err)
					continue
				}
				crlIssuerId = string(crlAuthorityKey.Id)
				break
			}
		}
		if crlIssuerId == "" {
			log.Print(fmt.Errorf("error: CRL from '%s' has no Authority Key Identifier: ignoring it\n", crlFile))
			continue
		}

		// Make sure we have a CA for this CRL or warn
		caCert, ok := config.clientCasBySubjectKeyId[crlIssuerId]

		if !ok {
			log.Printf("warn: CRL loaded for issuer '%s' but no such CA loaded: ignoring it\n", hex.EncodeToString([]byte(crlIssuerId)))
			fmt.Printf("%#v loaded certs\n", len(config.clientCasBySubjectKeyId))
			continue
		}

		// At this point, we have the CA certificate and the CRL. All that's left before evicting the CRL we currently trust is to verify the new CRL's signature
		err = caCert.CheckCRLSignature(certList)
		if err != nil {
			fmt.Printf("error: Could not trust CRL. Error during signature check: %#v\n", err)
			continue
		}

		// At this point, we have a new CRL which we trust. Let's evict the old one.
		config.CrlByAuthorityKeyId[crlIssuerId] = certList
		fmt.Printf("info: Loaded CRL for Authority ID '%s'\n", hex.EncodeToString([]byte(crlIssuerId)))
	}

	// Verify that all CAs loaded have a CRL
	for k, _ := range config.clientCasBySubjectKeyId {
		_, ok := config.CrlByAuthorityKeyId[k]
		if !ok {
			fmt.Printf("warn: no CRL loaded for Authority ID '%s'\n", hex.EncodeToString([]byte(k)))
		}
	}
	return nil
}

func (config *Config) SetupStatsdWithNamespace(addr, namespace string) error {
	if addr == "" {
		config.StatsdClient = nil
		return nil
	}

	client, err := statsd.New(addr)
	if err != nil {
		return err
	}

	config.StatsdClient = client

	config.StatsdClient.Namespace = namespace

	return nil
}

func (config *Config) SetupStatsd(addr string) error {
	return config.SetupStatsdWithNamespace(addr, DefaultStatsdNamespace)
}

func (config *Config) SetupEgressAcl(aclFile string) error {
	if aclFile == "" {
		config.EgressACL = nil
		return nil
	}

	log.Printf("Loading egress ACL from %s", aclFile)

	egressACL, err := acl.New(config.Log, acl.NewYAMLLoader(aclFile), config.DisabledAclPolicyActions)
	if err != nil {
		log.Print(err)
		return err
	}
	config.EgressACL = egressACL

	return nil
}

func addCertsFromFile(config *Config, pool *x509.CertPool, fileName string) error {
	data, err := ioutil.ReadFile(fileName)

	//TODO this is a bit awkward
	config.populateClientCaMap(data)

	if err != nil {
		return err
	}
	ok := pool.AppendCertsFromPEM(data)
	if !ok {
		return fmt.Errorf("Failed to load any certificates from file '%s'", fileName)
	}
	return nil
}

// certFile and keyFile may be the same file containing concatenated PEM blocks
func (config *Config) SetupTls(certFile, keyFile string, clientCAFiles []string) error {
	if certFile == "" || keyFile == "" {
		return errors.New("both certificate and key files must be specified to set up TLS")
	}

	serverCert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	clientAuth := tls.NoClientCert
	clientCAs := x509.NewCertPool()

	if len(clientCAFiles) != 0 {
		clientAuth = tls.VerifyClientCertIfGiven
		for _, caFile := range clientCAFiles {
			err = addCertsFromFile(config, clientCAs, caFile)
			if err != nil {
				return err
			}
		}
	}

	config.TlsConfig = &tls.Config{
		Certificates: []tls.Certificate{serverCert},
		ClientAuth:   clientAuth,
		ClientCAs:    clientCAs,
	}

	return nil
}

func (config *Config) populateClientCaMap(pemCerts []byte) (ok bool) {

	for len(pemCerts) > 0 {
		var block *pem.Block
		block, pemCerts = pem.Decode(pemCerts)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}

		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			continue
		}
		fmt.Printf("info: Loaded CA with Authority ID '%s'\n", hex.EncodeToString(cert.SubjectKeyId))
		config.clientCasBySubjectKeyId[string(cert.SubjectKeyId)] = cert
		ok = true
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2015 The etcd 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 transport

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"net"
	"os"
	"path"
	"strings"
	"time"

	"github.com/coreos/etcd/pkg/fileutil"
	"github.com/coreos/etcd/pkg/tlsutil"
)

func NewListener(addr, scheme string, tlscfg *tls.Config) (l net.Listener, err error) {
	if l, err = newListener(addr, scheme); err != nil {
		return nil, err
	}
	return wrapTLS(addr, scheme, tlscfg, l)
}

func newListener(addr string, scheme string) (net.Listener, error) {
	if scheme == "unix" || scheme == "unixs" {
		// unix sockets via unix://laddr
		return NewUnixListener(addr)
	}
	return net.Listen("tcp", addr)
}

func wrapTLS(addr, scheme string, tlscfg *tls.Config, l net.Listener) (net.Listener, error) {
	if scheme != "https" && scheme != "unixs" {
		return l, nil
	}
	if tlscfg == nil {
		l.Close()
		return nil, fmt.Errorf("cannot listen on TLS for %s: KeyFile and CertFile are not presented", scheme+"://"+addr)
	}
	return tls.NewListener(l, tlscfg), nil
}

type TLSInfo struct {
	CertFile       string
	KeyFile        string
	CAFile         string
	TrustedCAFile  string
	ClientCertAuth bool

	// ServerName ensures the cert matches the given host in case of discovery / virtual hosting
	ServerName string

	selfCert bool

	// parseFunc exists to simplify testing. Typically, parseFunc
	// should be left nil. In that case, tls.X509KeyPair will be used.
	parseFunc func([]byte, []byte) (tls.Certificate, error)
}

func (info TLSInfo) String() string {
	return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, client-cert-auth = %v", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth)
}

func (info TLSInfo) Empty() bool {
	return info.CertFile == "" && info.KeyFile == ""
}

func SelfCert(dirpath string, hosts []string) (info TLSInfo, err error) {
	if err = fileutil.TouchDirAll(dirpath); err != nil {
		return
	}

	certPath := path.Join(dirpath, "cert.pem")
	keyPath := path.Join(dirpath, "key.pem")
	_, errcert := os.Stat(certPath)
	_, errkey := os.Stat(keyPath)
	if errcert == nil && errkey == nil {
		info.CertFile = certPath
		info.KeyFile = keyPath
		info.selfCert = true
		return
	}

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return
	}

	tmpl := x509.Certificate{
		SerialNumber: serialNumber,
		Subject:      pkix.Name{Organization: []string{"etcd"}},
		NotBefore:    time.Now(),
		NotAfter:     time.Now().Add(365 * (24 * time.Hour)),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	for _, host := range hosts {
		if ip := net.ParseIP(host); ip != nil {
			tmpl.IPAddresses = append(tmpl.IPAddresses, ip)
		} else {
			tmpl.DNSNames = append(tmpl.DNSNames, strings.Split(host, ":")[0])
		}
	}

	priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
	if err != nil {
		return
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, &priv.PublicKey, priv)
	if err != nil {
		return
	}

	certOut, err := os.Create(certPath)
	if err != nil {
		return
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()

	b, err := x509.MarshalECPrivateKey(priv)
	if err != nil {
		return
	}
	keyOut, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return
	}
	pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
	keyOut.Close()

	return SelfCert(dirpath, hosts)
}

func (info TLSInfo) baseConfig() (*tls.Config, error) {
	if info.KeyFile == "" || info.CertFile == "" {
		return nil, fmt.Errorf("KeyFile and CertFile must both be present[key: %v, cert: %v]", info.KeyFile, info.CertFile)
	}

	tlsCert, err := tlsutil.NewCert(info.CertFile, info.KeyFile, info.parseFunc)
	if err != nil {
		return nil, err
	}

	cfg := &tls.Config{
		Certificates: []tls.Certificate{*tlsCert},
		MinVersion:   tls.VersionTLS12,
		ServerName:   info.ServerName,
	}
	return cfg, nil
}

// cafiles returns a list of CA file paths.
func (info TLSInfo) cafiles() []string {
	cs := make([]string, 0)
	if info.CAFile != "" {
		cs = append(cs, info.CAFile)
	}
	if info.TrustedCAFile != "" {
		cs = append(cs, info.TrustedCAFile)
	}
	return cs
}

// ServerConfig generates a tls.Config object for use by an HTTP server.
func (info TLSInfo) ServerConfig() (*tls.Config, error) {
	cfg, err := info.baseConfig()
	if err != nil {
		return nil, err
	}

	cfg.ClientAuth = tls.NoClientCert
	if info.CAFile != "" || info.ClientCertAuth {
		cfg.ClientAuth = tls.RequireAndVerifyClientCert
	}

	CAFiles := info.cafiles()
	if len(CAFiles) > 0 {
		cp, err := tlsutil.NewCertPool(CAFiles)
		if err != nil {
			return nil, err
		}
		cfg.ClientCAs = cp
	}

	return cfg, nil
}

// ClientConfig generates a tls.Config object for use by an HTTP client.
func (info TLSInfo) ClientConfig() (*tls.Config, error) {
	var cfg *tls.Config
	var err error

	if !info.Empty() {
		cfg, err = info.baseConfig()
		if err != nil {
			return nil, err
		}
	} else {
		cfg = &tls.Config{ServerName: info.ServerName}
	}

	CAFiles := info.cafiles()
	if len(CAFiles) > 0 {
		cfg.RootCAs, err = tlsutil.NewCertPool(CAFiles)
		if err != nil {
			return nil, err
		}
		// if given a CA, trust any host with a cert signed by the CA
		cfg.ServerName = ""
	}

	if info.selfCert {
		cfg.InsecureSkipVerify = true
	}
	return cfg, nil
}

// ShallowCopyTLSConfig copies *tls.Config. This is only
// work-around for go-vet tests, which complains
//
//   assignment copies lock value to p: crypto/tls.Config contains sync.Once contains sync.Mutex
//
// Keep up-to-date with 'go/src/crypto/tls/common.go'
func ShallowCopyTLSConfig(cfg *tls.Config) *tls.Config {
	ncfg := tls.Config{
		Time:                     cfg.Time,
		Certificates:             cfg.Certificates,
		NameToCertificate:        cfg.NameToCertificate,
		GetCertificate:           cfg.GetCertificate,
		RootCAs:                  cfg.RootCAs,
		NextProtos:               cfg.NextProtos,
		ServerName:               cfg.ServerName,
		ClientAuth:               cfg.ClientAuth,
		ClientCAs:                cfg.ClientCAs,
		InsecureSkipVerify:       cfg.InsecureSkipVerify,
		CipherSuites:             cfg.CipherSuites,
		PreferServerCipherSuites: cfg.PreferServerCipherSuites,
		SessionTicketKey:         cfg.SessionTicketKey,
		ClientSessionCache:       cfg.ClientSessionCache,
		MinVersion:               cfg.MinVersion,
		MaxVersion:               cfg.MaxVersion,
		CurvePreferences:         cfg.CurvePreferences,
	}
	return &ncfg
}
<commit_msg>embed: fix go 1.7 http issue<commit_after>// Copyright 2015 The etcd 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 transport

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"net"
	"os"
	"path"
	"strings"
	"time"

	"github.com/coreos/etcd/pkg/fileutil"
	"github.com/coreos/etcd/pkg/tlsutil"
)

func NewListener(addr, scheme string, tlscfg *tls.Config) (l net.Listener, err error) {
	if l, err = newListener(addr, scheme); err != nil {
		return nil, err
	}
	return wrapTLS(addr, scheme, tlscfg, l)
}

func newListener(addr string, scheme string) (net.Listener, error) {
	if scheme == "unix" || scheme == "unixs" {
		// unix sockets via unix://laddr
		return NewUnixListener(addr)
	}
	return net.Listen("tcp", addr)
}

func wrapTLS(addr, scheme string, tlscfg *tls.Config, l net.Listener) (net.Listener, error) {
	if scheme != "https" && scheme != "unixs" {
		return l, nil
	}
	if tlscfg == nil {
		l.Close()
		return nil, fmt.Errorf("cannot listen on TLS for %s: KeyFile and CertFile are not presented", scheme+"://"+addr)
	}
	return tls.NewListener(l, tlscfg), nil
}

type TLSInfo struct {
	CertFile       string
	KeyFile        string
	CAFile         string
	TrustedCAFile  string
	ClientCertAuth bool

	// ServerName ensures the cert matches the given host in case of discovery / virtual hosting
	ServerName string

	selfCert bool

	// parseFunc exists to simplify testing. Typically, parseFunc
	// should be left nil. In that case, tls.X509KeyPair will be used.
	parseFunc func([]byte, []byte) (tls.Certificate, error)
}

func (info TLSInfo) String() string {
	return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, client-cert-auth = %v", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth)
}

func (info TLSInfo) Empty() bool {
	return info.CertFile == "" && info.KeyFile == ""
}

func SelfCert(dirpath string, hosts []string) (info TLSInfo, err error) {
	if err = fileutil.TouchDirAll(dirpath); err != nil {
		return
	}

	certPath := path.Join(dirpath, "cert.pem")
	keyPath := path.Join(dirpath, "key.pem")
	_, errcert := os.Stat(certPath)
	_, errkey := os.Stat(keyPath)
	if errcert == nil && errkey == nil {
		info.CertFile = certPath
		info.KeyFile = keyPath
		info.selfCert = true
		return
	}

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return
	}

	tmpl := x509.Certificate{
		SerialNumber: serialNumber,
		Subject:      pkix.Name{Organization: []string{"etcd"}},
		NotBefore:    time.Now(),
		NotAfter:     time.Now().Add(365 * (24 * time.Hour)),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	for _, host := range hosts {
		if ip := net.ParseIP(host); ip != nil {
			tmpl.IPAddresses = append(tmpl.IPAddresses, ip)
		} else {
			tmpl.DNSNames = append(tmpl.DNSNames, strings.Split(host, ":")[0])
		}
	}

	priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
	if err != nil {
		return
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, &priv.PublicKey, priv)
	if err != nil {
		return
	}

	certOut, err := os.Create(certPath)
	if err != nil {
		return
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()

	b, err := x509.MarshalECPrivateKey(priv)
	if err != nil {
		return
	}
	keyOut, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return
	}
	pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
	keyOut.Close()

	return SelfCert(dirpath, hosts)
}

func (info TLSInfo) baseConfig() (*tls.Config, error) {
	if info.KeyFile == "" || info.CertFile == "" {
		return nil, fmt.Errorf("KeyFile and CertFile must both be present[key: %v, cert: %v]", info.KeyFile, info.CertFile)
	}

	tlsCert, err := tlsutil.NewCert(info.CertFile, info.KeyFile, info.parseFunc)
	if err != nil {
		return nil, err
	}

	cfg := &tls.Config{
		Certificates: []tls.Certificate{*tlsCert},
		MinVersion:   tls.VersionTLS12,
		ServerName:   info.ServerName,
	}
	return cfg, nil
}

// cafiles returns a list of CA file paths.
func (info TLSInfo) cafiles() []string {
	cs := make([]string, 0)
	if info.CAFile != "" {
		cs = append(cs, info.CAFile)
	}
	if info.TrustedCAFile != "" {
		cs = append(cs, info.TrustedCAFile)
	}
	return cs
}

// ServerConfig generates a tls.Config object for use by an HTTP server.
func (info TLSInfo) ServerConfig() (*tls.Config, error) {
	cfg, err := info.baseConfig()
	if err != nil {
		return nil, err
	}

	cfg.ClientAuth = tls.NoClientCert
	if info.CAFile != "" || info.ClientCertAuth {
		cfg.ClientAuth = tls.RequireAndVerifyClientCert
	}

	CAFiles := info.cafiles()
	if len(CAFiles) > 0 {
		cp, err := tlsutil.NewCertPool(CAFiles)
		if err != nil {
			return nil, err
		}
		cfg.ClientCAs = cp
	}

	// "h2" NextProtos is necessary for enabling HTTP2 for go's HTTP server
	cfg.NextProtos = []string{"h2"}

	return cfg, nil
}

// ClientConfig generates a tls.Config object for use by an HTTP client.
func (info TLSInfo) ClientConfig() (*tls.Config, error) {
	var cfg *tls.Config
	var err error

	if !info.Empty() {
		cfg, err = info.baseConfig()
		if err != nil {
			return nil, err
		}
	} else {
		cfg = &tls.Config{ServerName: info.ServerName}
	}

	CAFiles := info.cafiles()
	if len(CAFiles) > 0 {
		cfg.RootCAs, err = tlsutil.NewCertPool(CAFiles)
		if err != nil {
			return nil, err
		}
		// if given a CA, trust any host with a cert signed by the CA
		cfg.ServerName = ""
	}

	if info.selfCert {
		cfg.InsecureSkipVerify = true
	}
	return cfg, nil
}

// ShallowCopyTLSConfig copies *tls.Config. This is only
// work-around for go-vet tests, which complains
//
//   assignment copies lock value to p: crypto/tls.Config contains sync.Once contains sync.Mutex
//
// Keep up-to-date with 'go/src/crypto/tls/common.go'
func ShallowCopyTLSConfig(cfg *tls.Config) *tls.Config {
	ncfg := tls.Config{
		Time:                     cfg.Time,
		Certificates:             cfg.Certificates,
		NameToCertificate:        cfg.NameToCertificate,
		GetCertificate:           cfg.GetCertificate,
		RootCAs:                  cfg.RootCAs,
		NextProtos:               cfg.NextProtos,
		ServerName:               cfg.ServerName,
		ClientAuth:               cfg.ClientAuth,
		ClientCAs:                cfg.ClientCAs,
		InsecureSkipVerify:       cfg.InsecureSkipVerify,
		CipherSuites:             cfg.CipherSuites,
		PreferServerCipherSuites: cfg.PreferServerCipherSuites,
		SessionTicketKey:         cfg.SessionTicketKey,
		ClientSessionCache:       cfg.ClientSessionCache,
		MinVersion:               cfg.MinVersion,
		MaxVersion:               cfg.MaxVersion,
		CurvePreferences:         cfg.CurvePreferences,
	}
	return &ncfg
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative 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 webhook

import (
	"context"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"testing"

	"github.com/google/go-cmp/cmp"
)

func TestCreateCerts(t *testing.T) {
	sKey, serverCertPEM, caCertBytes, err := CreateCerts(context.TODO())
	if err != nil {
		t.Fatalf("Failed to create certs %v", err)
	}

	// Test server private key
	p, _ := pem.Decode(sKey)
	if p.Type != "RSA PRIVATE KEY" {
		t.Fatal("Expected the key to be RSA Private key type")
	}
	key, err := x509.ParsePKCS1PrivateKey(p.Bytes)
	if err != nil {
		t.Fatalf("Failed to parse private key %v", err)
	}
	if err := key.Validate(); err != nil {
		t.Fatalf("Failed to validate private key")
	}

	// Test Server Cert
	sCert, err := validCertificate(serverCertPEM, t)
	if err != nil {
		t.Fatal(err)
	}

	// Test CA Cert
	caParsedCert, err := validCertificate(caCertBytes, t)
	if err != nil {
		t.Fatal(err)
	}

	// Verify domain names
	expectedDNSNames := []string{
		"webhook.knative-serving-system",
		"webhook.knative-serving-system.svc",
		"webhook.knative-serving-system.svc.cluster.local",
	}
	if diff := cmp.Diff(caParsedCert.DNSNames, expectedDNSNames); diff != "" {
		t.Fatalf("Unexpected CA Cert DNS Name (-want +got) : %v", diff)
	}

	// Verify Server Cert is Signed by CA Cert
	if err = sCert.CheckSignatureFrom(caParsedCert); err != nil {
		t.Fatal("Failed to verify that the signature on server certificate is from parent CA cert", err)
	}
}

func validCertificate(cert []byte, t *testing.T) (*x509.Certificate, error) {
	t.Helper()
	caCert, _ := pem.Decode(cert)
	if caCert.Type != "CERTIFICATE" {
		return nil, fmt.Errorf("Expected %s but got %s", "CERTIFICATE", caCert.Type)
	}
	parsedCert, err := x509.ParseCertificate(caCert.Bytes)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse cert %v", err)
	}
	if parsedCert.SignatureAlgorithm != x509.SHA256WithRSA {
		return nil, fmt.Errorf("Failed to match signature. Expect %s but got %s", x509.SHA256WithRSA, parsedCert.SignatureAlgorithm)
	}
	return parsedCert, nil
}
<commit_msg>Correct the namespace in the cert test. (#1372)<commit_after>/*
Copyright 2018 The Knative 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 webhook

import (
	"context"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"testing"

	"github.com/google/go-cmp/cmp"
)

func TestCreateCerts(t *testing.T) {
	sKey, serverCertPEM, caCertBytes, err := CreateCerts(context.TODO())
	if err != nil {
		t.Fatalf("Failed to create certs %v", err)
	}

	// Test server private key
	p, _ := pem.Decode(sKey)
	if p.Type != "RSA PRIVATE KEY" {
		t.Fatal("Expected the key to be RSA Private key type")
	}
	key, err := x509.ParsePKCS1PrivateKey(p.Bytes)
	if err != nil {
		t.Fatalf("Failed to parse private key %v", err)
	}
	if err := key.Validate(); err != nil {
		t.Fatalf("Failed to validate private key")
	}

	// Test Server Cert
	sCert, err := validCertificate(serverCertPEM, t)
	if err != nil {
		t.Fatal(err)
	}

	// Test CA Cert
	caParsedCert, err := validCertificate(caCertBytes, t)
	if err != nil {
		t.Fatal(err)
	}

	// Verify domain names
	expectedDNSNames := []string{
		"webhook.knative-serving",
		"webhook.knative-serving.svc",
		"webhook.knative-serving.svc.cluster.local",
	}
	if diff := cmp.Diff(caParsedCert.DNSNames, expectedDNSNames); diff != "" {
		t.Fatalf("Unexpected CA Cert DNS Name (-want +got) : %v", diff)
	}

	// Verify Server Cert is Signed by CA Cert
	if err = sCert.CheckSignatureFrom(caParsedCert); err != nil {
		t.Fatal("Failed to verify that the signature on server certificate is from parent CA cert", err)
	}
}

func validCertificate(cert []byte, t *testing.T) (*x509.Certificate, error) {
	t.Helper()
	caCert, _ := pem.Decode(cert)
	if caCert.Type != "CERTIFICATE" {
		return nil, fmt.Errorf("Expected %s but got %s", "CERTIFICATE", caCert.Type)
	}
	parsedCert, err := x509.ParseCertificate(caCert.Bytes)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse cert %v", err)
	}
	if parsedCert.SignatureAlgorithm != x509.SHA256WithRSA {
		return nil, fmt.Errorf("Failed to match signature. Expect %s but got %s", x509.SHA256WithRSA, parsedCert.SignatureAlgorithm)
	}
	return parsedCert, nil
}
<|endoftext|>


<commit_before>//
// Copyright 2015 Rakuten Marketing LLC
//
// 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 syslog_test

import (
	"github.com/stretchr/testify/suite"
)

type SyslogTestSuite struct {
	suite.Suite
}

func (s *SyslogTestSuite) Test() {
}
<commit_msg>added syslog tests<commit_after>//
// Copyright 2015 Rakuten Marketing LLC
//
// 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 syslog_test

import (
	"fmt"
	"log/syslog"
	"net"
	"strings"
	"testing"

	"github.com/mediaFORGE/gol"
	"github.com/mediaFORGE/gol/fields/severity"
	"github.com/mediaFORGE/gol/formatters"
	golmock "github.com/mediaFORGE/gol/internal/mock"
	golsys "github.com/mediaFORGE/gol/loggers/syslog"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/suite"
)

// MockUDPServer
type MockUDPServer struct {
	mock.Mock
	t *testing.T
}

func (m *MockUDPServer) ReadMessage(conn *net.UDPConn, buf []byte) {
	n, addr, err := conn.ReadFromUDP(buf)
	assert.Nil(m.t, err)
	assert.NotNil(m.t, "abc", addr)
	assert.True(m.t, n > 0)

	msg := string(buf[0:n])
	m.receivedMessage(msg)

	if err != nil {
		fmt.Println("Error: ", err)
	}
}

func (m *MockUDPServer) receivedMessage(msg string) {
	m.Mock.Called(msg)
}

func (m *MockUDPServer) Run(ch chan bool, readch chan bool) {
	if addr, err := net.ResolveUDPAddr("udp", ":10001"); err != nil {
		assert.Fail(m.t, "could not resolve UDP server address")
	} else {
		/* Now listen at selected port */
		if conn, err := net.ListenUDP("udp", addr); err != nil {
			assert.Fail(m.t, "could not listen to UDP server port")
		} else {
			defer conn.Close()

			buf := make([]byte, 1024)

			ch <- true

			for {
				if !<-readch {
					break // end loop execution and end go routine
				}
				m.ReadMessage(conn, buf)
			}
		}
	}
}

// SyslogTestSuite test suite for the github.com/mediaFORGE/gol/loggers/syslog package.
type SyslogTestSuite struct {
	suite.Suite
	logger gol.Logger
}

func (s *SyslogTestSuite) SetupTest() {
	s.logger = golsys.New("udp", ":10001", syslog.LOG_INFO, "test", &formatters.Text{})
}

func (s *SyslogTestSuite) TestSend() {
	udpserver := MockUDPServer{t: s.T()}
	udpserver.Mock.On("receivedMessage", mock.Anything).Return(nil)

	syncch := make(chan bool, 1)
	readch := make(chan bool, 1)
	go udpserver.Run(syncch, readch)
	<-syncch

	var lvl severity.Type
	for lvl = severity.Emergency; lvl <= severity.Debug; lvl++ {
		s.logger.Send(gol.NewMessage(lvl, "message", "unknown"))
		readch <- true // read message sent
	}

	// invalid severity will be sent with the default logger severity level
	s.logger.Send(gol.NewMessage(severity.Debug+1, "invalid", "severity"))
	readch <- true  // read message sent
	readch <- false // end concurrent go routine

	udpserver.AssertExpectations(s.T())

	found := 0
	for lvl = severity.Emergency; lvl <= severity.Debug; lvl++ {
		for _, call := range udpserver.Calls {
			if call.Method == "receivedMessage" {
				for _, arg := range call.Arguments {
					if strings.Contains(arg.(string), lvl.String()) {
						found++
					}
				}
			}
		}
	}
	assert.Equal(s.T(), 8, found)
}

func (s *SyslogTestSuite) TestSendMessageWithoutSeverity() {
	assert.Error(s.T(), s.logger.Send(&gol.LogMessage{"message": "unknown"}))
}

func (s *SyslogTestSuite) TestSendNil() {
	assert.Nil(s.T(), s.logger.Send(nil))
}

func (s *SyslogTestSuite) TestSendNilFormatter() {
	// reset logger built by SetupTest
	s.logger = golsys.New("udp", ":10001", syslog.LOG_INFO, "test", nil)
	assert.Error(s.T(), s.logger.Send(gol.NewEmergency("message", "unknown")))
}

func (s *SyslogTestSuite) TestSendFormatterError() {
	msg := gol.NewEmergency("message", "unknown")
	m := &golmock.MockLogFormatter{}
	m.On("Format", msg).Return("", fmt.Errorf("internal error"))

	s.logger.SetFormatter(m)
	assert.Error(s.T(), s.logger.Send(msg))

	m.AssertExpectations(s.T())
}
<|endoftext|>


<commit_before>package lxd

import (
	"log"
	"time"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

func resourceLxdContainer() *schema.Resource {
	return &schema.Resource{
		Create: resourceLxdContainerCreate,
		Update: resourceLxdContainerUpdate,
		Delete: resourceLxdContainerDelete,
		Exists: resourceLxdContainerExists,
		Read:   resourceLxdContainerRead,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				ForceNew: true,
				Required: true,
			},

			"image": &schema.Schema{
				Type:     schema.TypeString,
				ForceNew: true,
				Required: true,
			},

			"profiles": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				ForceNew: false,
			},

			"config": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},

			"ephemeral": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

			"privileged": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
				ForceNew: false,
			},

			"ip_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
				ForceNew: false,
			},

			"status": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"mac_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceLxdContainerCreate(d *schema.ResourceData, meta interface{}) error {
	var err error
	client := meta.(*LxdProvider).Client
	remote := meta.(*LxdProvider).Remote

	name := d.Get("name").(string)
	ephem := d.Get("ephemeral").(bool)
	config := resourceLxdContainerConfigMap(d.Get("config"))

	/*
	 * requested_empty_profiles means user requested empty
	 * !requested_empty_profiles but len(profArgs) == 0 means use profile default
	 */
	profiles := []string{}
	if v := d.Get("profiles"); v != nil {
		vs := v.(*schema.Set)
		for _, v := range vs.List() {
			profiles = append(profiles, v.(string))
		}
	}

	//client.Init = (name string, imgremote string, image string, profiles *[]string, config map[string]string, ephem bool)
	var resp *lxd.Response
	if resp, err = client.Init(name, remote, d.Get("image").(string), &profiles, config, nil, ephem); err != nil {
		return err
	}

	err = client.WaitForSuccess(resp.Operation)
	if err != nil {
		return err
	}

	// Start container
	resp, err = client.Action(name, shared.Start, -1, false, false)
	if err != nil {
		// Container has been created, but daemon rejected start request
		return err
	}

	if err := client.WaitForSuccess(resp.Operation); err != nil {
		// Container could not be started
		return err
	}

	d.SetId(name)
	return resourceLxdContainerRead(d, meta)
}

func resourceLxdContainerRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*LxdProvider).Client

	container, err := client.ContainerInfo(d.Id())
	if err != nil {
		return err
	}
	log.Printf("[DEBUG] Retrieved container %s: %#v", d.Id(), container)

	sshIP := ""
	gotIp := false
	cycles := 0

	// wait for NIC to come up and get IP from DHCP
	for !gotIp && cycles < 15 {
		cycles += 1
		ct, _ := client.ContainerState(d.Get("name").(string))
		d.Set("status", ct.Status)
		for iface, net := range ct.Network {
			if iface != "lo" {
				for _, ip := range net.Addresses {
					if ip.Family == "inet" {
						d.Set("ip_address", ip.Address)
						d.Set("mac_address", net.Hwaddr)
						gotIp = true
					}
				}
			}
		}
		time.Sleep(1 * time.Second)
	}

	// Initialize the connection info
	d.SetConnInfo(map[string]string{
		"type": "ssh",
		"host": sshIP,
	})

	return nil
}

func resourceLxdContainerUpdate(d *schema.ResourceData, meta interface{}) error {

	return nil
}

func resourceLxdContainerDelete(d *schema.ResourceData, meta interface{}) (err error) {
	client := meta.(*LxdProvider).Client
	name := d.Get("name").(string)

	ct, _ := client.ContainerState(d.Get("name").(string))
	if ct.Status == "Running" {
		stopResp, err := client.Action(name, shared.Stop, 30, true, false)
		if err == nil {
			err = client.WaitForSuccess(stopResp.Operation)
		}
	}

	if err == nil {
		var resp *lxd.Response
		if resp, err = client.Delete(name); err == nil {
			err = client.WaitForSuccess(resp.Operation)
		}
	}

	return err
}

func resourceLxdContainerExists(d *schema.ResourceData, meta interface{}) (exists bool, err error) {
	client := meta.(*LxdProvider).Client

	exists = false

	ct, err := client.ContainerState(d.Get("name").(string))
	if err == nil && ct != nil {
		exists = true
	}

	return
}

func resourceLxdContainerConfigMap(c interface{}) map[string]string {
	config := make(map[string]string)
	if v, ok := c.(map[string]interface{}); ok {
		for key, val := range v {
			config[key] = val.(string)
		}
	}

	log.Printf("[DEBUG]: LXD Container Configuration Map: %#v", config)

	return config
}

func getContainerState(client *lxd.Client, name string) *shared.ContainerState {
	ct, err := client.ContainerState(name)
	if err != nil {
		return nil
	}
	return ct
}
<commit_msg>Implement Terraform State Refresh<commit_after>package lxd

import (
	"fmt"
	"log"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

func resourceLxdContainer() *schema.Resource {
	return &schema.Resource{
		Create: resourceLxdContainerCreate,
		Update: resourceLxdContainerUpdate,
		Delete: resourceLxdContainerDelete,
		Exists: resourceLxdContainerExists,
		Read:   resourceLxdContainerRead,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				ForceNew: true,
				Required: true,
			},

			"image": &schema.Schema{
				Type:     schema.TypeString,
				ForceNew: true,
				Required: true,
			},

			"profiles": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				ForceNew: false,
			},

			"config": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},

			"ephemeral": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

			"privileged": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
				ForceNew: false,
			},

			"ip_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
				ForceNew: false,
			},

			"status": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"mac_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceLxdContainerCreate(d *schema.ResourceData, meta interface{}) error {
	var err error
	client := meta.(*LxdProvider).Client
	remote := meta.(*LxdProvider).Remote

	name := d.Get("name").(string)
	ephem := d.Get("ephemeral").(bool)
	config := resourceLxdContainerConfigMap(d.Get("config"))

	/*
	 * requested_empty_profiles means user requested empty
	 * !requested_empty_profiles but len(profArgs) == 0 means use profile default
	 */
	profiles := []string{}
	if v := d.Get("profiles"); v != nil {
		vs := v.(*schema.Set)
		for _, v := range vs.List() {
			profiles = append(profiles, v.(string))
		}
	}

	//client.Init = (name string, imgremote string, image string, profiles *[]string, config map[string]string, ephem bool)
	var resp *lxd.Response
	if resp, err = client.Init(name, remote, d.Get("image").(string), &profiles, config, nil, ephem); err != nil {
		return err
	}

	// Wait for the LXC container to be created
	err = client.WaitForSuccess(resp.Operation)
	if err != nil {
		return err
	}

	// Start container
	_, err = client.Action(name, shared.Start, -1, false, false)
	if err != nil {
		// Container has been created, but daemon rejected start request
		return err
	}

	// Wait until the container is in a Running state
	stateConf := &resource.StateChangeConf{
		Target:     []string{"Running"},
		Refresh:    resourceLxdContainerRefresh(client, name),
		Timeout:    3 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	if _, err = stateConf.WaitForState(); err != nil {
		return fmt.Errorf("Error waiting for container (%s) to become active: %s", name, err)
	}

	d.SetId(name)
	return resourceLxdContainerRead(d, meta)
}

func resourceLxdContainerRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*LxdProvider).Client

	container, err := client.ContainerInfo(d.Id())
	if err != nil {
		return err
	}
	log.Printf("[DEBUG] Retrieved container %s: %#v", d.Id(), container)

	sshIP := ""

	ct, err := client.ContainerState(d.Id())
	if err != nil {
		return err
	}

	d.Set("status", ct.Status)

	for iface, net := range ct.Network {
		if iface != "lo" {
			for _, ip := range net.Addresses {
				if ip.Family == "inet" {
					d.Set("ip_address", ip.Address)
					sshIP = ip.Address
					d.Set("mac_address", net.Hwaddr)
				}
			}
		}
	}

	// Initialize the connection info
	d.SetConnInfo(map[string]string{
		"type": "ssh",
		"host": sshIP,
	})

	return nil
}

func resourceLxdContainerUpdate(d *schema.ResourceData, meta interface{}) error {

	return nil
}

func resourceLxdContainerDelete(d *schema.ResourceData, meta interface{}) (err error) {
	client := meta.(*LxdProvider).Client
	name := d.Get("name").(string)

	ct, _ := client.ContainerState(name)
	if ct.Status == "Running" {
		if _, err := client.Action(name, shared.Stop, 30, true, false); err != nil {
			return err
		}

		// Wait until the container is in a Stopped state
		stateConf := &resource.StateChangeConf{
			Target:     []string{"Stopped"},
			Refresh:    resourceLxdContainerRefresh(client, name),
			Timeout:    3 * time.Minute,
			Delay:      10 * time.Second,
			MinTimeout: 3 * time.Second,
		}

		if _, err = stateConf.WaitForState(); err != nil {
			return fmt.Errorf("Error waiting for container (%s) to stop: %s", name, err)
		}
	}

	if _, err = client.Delete(name); err != nil {
		return err
	}

	return nil
}

func resourceLxdContainerExists(d *schema.ResourceData, meta interface{}) (exists bool, err error) {
	client := meta.(*LxdProvider).Client

	exists = false

	ct, err := client.ContainerState(d.Get("name").(string))
	if err == nil && ct != nil {
		exists = true
	}

	return
}

func resourceLxdContainerConfigMap(c interface{}) map[string]string {
	config := make(map[string]string)
	if v, ok := c.(map[string]interface{}); ok {
		for key, val := range v {
			config[key] = val.(string)
		}
	}

	log.Printf("[DEBUG]: LXD Container Configuration Map: %#v", config)

	return config
}

func resourceLxdContainerRefresh(client *lxd.Client, name string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		ct, err := client.ContainerState(name)
		if err != nil {
			return ct, "Error", err
		}

		return ct, ct.Status, nil
	}
}

func getContainerState(client *lxd.Client, name string) *shared.ContainerState {
	ct, err := client.ContainerState(name)
	if err != nil {
		return nil
	}
	return ct
}
<|endoftext|>


<commit_before>package ocn_receiver

import (
	"fmt"
	"math"
	"net/http"
	"strings"
	"time"

	"google.golang.org/api/compute/v1"
	"google.golang.org/appengine"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/appengine/log"
	"google.golang.org/appengine/taskqueue"
	"google.golang.org/appengine/urlfetch"

	"appengine/taskqueue"
	"github.com/pborman/uuid"
)

const INSTANCE_NAME = "conimg"

func init() {
	http.HandleFunc("/api/1/gcemanager", handlerGceManager)
}

func handlerGceManager(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	client := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.AppEngineTokenSource(ctx, compute.ComputeScope),
			Base:   &urlfetch.Transport{Context: ctx},
		},
	}
	s, err := compute.New(client)
	if err != nil {
		log.Errorf(ctx, "ERROR compute.New: %s", err)
		w.WriteHeader(500)
		return
	}
	is := compute.NewInstancesService(s)
	ilc := is.List("cp300demo1", "us-central1-b")
	il, err := ilc.Do()
	if err != nil {
		log.Errorf(ctx, "ERROR instances list: %s", err)
		w.WriteHeader(500)
		return
	}
	count := 0
	for _, item := range il.Items {
		log.Infof(ctx, "id = %s, name = %s, creationTimestamp = %s", item.Id, item.Name, item.CreationTimestamp)
		if strings.HasPrefix(item.Name, INSTANCE_NAME) {
			count++
		}
	}
	threshold := 3
	if count > threshold {
		log.Infof(ctx, "Create a new instance is canceled.")
		w.WriteHeader(200)
		return
	}

	qs, err := taskqueue.QueueStats(ctx, []string{"pull-queue"})
	if err != nil {
		log.Errorf(ctx, "ERROR get queue stats: %s", err)
		w.WriteHeader(500)
		return
	}
	if qs[0].Tasks < 1 {
		log.Infof(ctx, "gce-manager purge.")
		err = taskqueue.Purge(ctx, "gce-manager")
		if err != nil {
			log.Warningf(ctx, "missing gce-manager purge. err = %s", err)
		}
	}
	if count > qs[0].Tasks {
		log.Infof(ctx, "instance count %d > task count %d", count, qs[0].Tasks)
		w.WriteHeader(200)
		return
	}

	threshold = math.MinInt32(threshold, qs[0].Tasks)
	names := make([]string, 0)
	for i := count; i < threshold; i++ {
		name, err := createInstance(ctx, is)
		if err != nil {
			time.Sleep(3 * time.Second)
		}
		names = append(names, name)
	}

	w.WriteHeader(200)
	fmt.Fprint(w, names)
}

func createInstance(ctx context.Context, is *compute.InstancesService) (string, error) {
	name := INSTANCE_NAME + "-" + uuid.New()
	log.Infof(ctx, "instance name = %s", name)

	newIns := &compute.Instance{
		Name:        name,
		Zone:        "https://www.googleapis.com/compute/v1/projects/cp300demo1/zones/us-central1-b",
		MachineType: "https://www.googleapis.com/compute/v1/projects/cp300demo1/zones/us-central1-b/machineTypes/n1-standard-1",
		Disks: []*compute.AttachedDisk{
			&compute.AttachedDisk{
				AutoDelete: true,
				Boot:       true,
				DeviceName: name,
				Mode:       "READ_WRITE",
				InitializeParams: &compute.AttachedDiskInitializeParams{
					SourceImage: "https://www.googleapis.com/compute/v1/projects/cp300demo1/global/images/cp300-06-image",
					DiskType:    "https://www.googleapis.com/compute/v1/projects/cp300demo1/zones/us-central1-b/diskTypes/pd-standard",
					DiskSizeGb:  10,
				},
			},
		},
		CanIpForward: false,
		NetworkInterfaces: []*compute.NetworkInterface{
			&compute.NetworkInterface{
				Network: "https://www.googleapis.com/compute/v1/projects/cp300demo1/global/networks/default",
				AccessConfigs: []*compute.AccessConfig{
					&compute.AccessConfig{
						Name: "External NAT",
						Type: "ONE_TO_ONE_NAT",
					},
				},
			},
		},
		ServiceAccounts: []*compute.ServiceAccount{
			&compute.ServiceAccount{
				Email: "default",
				Scopes: []string{
					compute.ComputeScope,
					compute.DevstorageFullControlScope,
					"https://www.googleapis.com/auth/taskqueue",
					"https://www.googleapis.com/auth/logging.write",
				},
			},
		},
		Scheduling: &compute.Scheduling{
			AutomaticRestart:  false,
			OnHostMaintenance: "TERMINATE",
			Preemptible:       true,
		},
	}
	ope, err := is.Insert("cp300demo1", "us-central1-b", newIns).Do()
	if err != nil {
		log.Errorf(ctx, "ERROR insert instance: %s", err)
		return "", err
	}
	log.Infof(ctx, "create instance ope.name = %s, ope.targetLink = %s, ope.Status = %s", ope.Name, ope.TargetLink, ope.Status)

	return name, nil
}
<commit_msg>GCEのinstance数をinstance groupで制御するようにした<commit_after>package ocn_receiver

import (
	"fmt"
	"math"
	"net/http"
	"strings"

	"google.golang.org/api/compute/v1"
	"google.golang.org/appengine"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/appengine/log"
	"google.golang.org/appengine/taskqueue"
	"google.golang.org/appengine/urlfetch"
)

const INSTANCE_NAME = "conimg"

func init() {
	http.HandleFunc("/api/1/gcemanager", handlerGceManager)
}

func handlerGceManager(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	client := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.AppEngineTokenSource(ctx, compute.ComputeScope),
			Base:   &urlfetch.Transport{Context: ctx},
		},
	}
	s, err := compute.New(client)
	if err != nil {
		log.Errorf(ctx, "ERROR compute.New: %s", err)
		w.WriteHeader(500)
		return
	}
	is := compute.NewInstancesService(s)
	ilc := is.List("cp300demo1", "us-central1-b")
	il, err := ilc.Do()
	if err != nil {
		log.Errorf(ctx, "ERROR instances list: %s", err)
		w.WriteHeader(500)
		return
	}
	count := 0
	for _, item := range il.Items {
		log.Infof(ctx, "id = %s, name = %s, creationTimestamp = %s", item.Id, item.Name, item.CreationTimestamp)
		if strings.HasPrefix(item.Name, INSTANCE_NAME) {
			count++
		}
	}
	threshold := 50
	if count > threshold {
		log.Infof(ctx, "Create a new instance is canceled.")
		w.WriteHeader(200)
		return
	}

	qs, err := taskqueue.QueueStats(ctx, []string{"pull-queue"})
	if err != nil {
		log.Errorf(ctx, "ERROR get queue stats: %s", err)
		w.WriteHeader(500)
		return
	}
	log.Infof(ctx, "task count = %d", qs[0].Tasks)
	if qs[0].Tasks < 1 {
		log.Infof(ctx, "gce-manager purge.")
		err = taskqueue.Purge(ctx, "gce-manager")
		if err != nil {
			log.Warningf(ctx, "missing gce-manager purge. err = %s", err)
		}
	}
	if count > qs[0].Tasks {
		log.Infof(ctx, "instance count %d > task count %d", count, qs[0].Tasks)
		w.WriteHeader(200)
		return
	}

	threshold = int(math.Min(float64(threshold), float64(qs[0].Tasks)))
	igs := compute.NewInstanceGroupManagersService(s)
	ope, err := igs.Resize("cp300demo1", "us-central1-b", "preemptibility-group", int64(threshold)).Do()
	if err != nil {
		log.Errorf(ctx, "ERROR resize instance group: %s", err)
		w.WriteHeader(500)
		return
	}
	log.Infof(ctx, "resize instance group. ope.name = %s, ope.targetLink = %s, ope.Status = %s, size = %d", ope.Name, ope.TargetLink, ope.Status, threshold)

	w.WriteHeader(200)
	fmt.Fprint(w, threshold)
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"time"
)

// HomeHandler renders both loggedin and loggedout page for user.
// When user is loggedin, we send some extra data with the payload
// so the client doesn't need to fetch them after the page loads.
func HomeHandler(w http.ResponseWriter, r *http.Request) {
	userInfo, err := fetchUserInfo(w, r)
	if err != nil {
		writeLoggedOutHomeToResp(w)
		return
	}

	onItem := make(chan Item, 0)   // individual prefetched items come here
	onDone := make(chan bool, 1)   // signals when done prefetching items
	onError := make(chan error, 1) // when there's an error, return right away

	collectItemCount := 3
	outputter := &Outputter{OnItem: onItem, OnError: onError}

	// on new register, there's a race condition where SocialApiId
	// isn't sometimes set; in that case don't prefetch socialdata
	// since it'll return empty
	if !isSocialIdEmpty(userInfo.SocialApiId) {
		collectItemCount = 4
		go fetchSocial(userInfo.SocialApiId, outputter)
	}

	user := NewLoggedInUser()
	user.Set("Group", kodingGroup)
	user.Set("Username", userInfo.Username)
	user.Set("SessionId", userInfo.ClientId)
	user.Set("Impersonating", userInfo.Impersonating)

	// the goroutines below (and maybe one above) will work in parallel
	// and send results
	go collectItems(user, onItem, onDone, collectItemCount)

	go sendAccount(userInfo.Account, outputter)
	go fetchMachines(userInfo.UserId, outputter)
	go fetchWorkspaces(userInfo.AccountId, outputter)

	// return in 750ms regardless and let client get what it wants
	timeout := time.NewTimer(time.Millisecond * 750)

	select {
	case <-onError:
		writeLoggedOutHomeToResp(w)
	case <-timeout.C:
		writeLoggedInHomeToResp(w, user)
	case <-onDone:
		writeLoggedInHomeToResp(w, user)
	}
}

func collectItems(resp *LoggedInUser, onItem <-chan Item, onDone chan<- bool, max int) {
	for i := 1; i <= max; i++ {
		item := <-onItem
		resp.Set(item.Name, item.Data)
	}

	onDone <- true
}

func isSocialIdEmpty(id string) bool {
	return id == ""
}
<commit_msg>go-webserver: minor fixes<commit_after>package main

import (
	"net/http"
	"time"
)

var (
	TimeoutTime = 750 * time.Millisecond
)

// HomeHandler renders both loggedin and loggedout page for user.
// When user is loggedin, we send some extra data with the payload
// so the client doesn't need to fetch them after the page loads.
func HomeHandler(w http.ResponseWriter, r *http.Request) {
	userInfo, err := fetchUserInfo(w, r)
	if err != nil {
		writeLoggedOutHomeToResp(w)
		return
	}

	onItem := make(chan Item, 0)   // individual prefetched items come here
	onDone := make(chan bool, 1)   // signals when done prefetching items
	onError := make(chan error, 1) // when there's an error, return right away

	collectItemCount := 3
	outputter := &Outputter{OnItem: onItem, OnError: onError}

	// on new register, there's a race condition where SocialApiId
	// isn't sometimes set; in that case don't prefetch socialdata
	// since it'll return empty
	if !isSocialIdEmpty(userInfo.SocialApiId) {
		collectItemCount = 4
		go fetchSocial(userInfo.SocialApiId, outputter)
	}

	user := NewLoggedInUser()
	user.Set("Group", kodingGroup)
	user.Set("Username", userInfo.Username)
	user.Set("SessionId", userInfo.ClientId)
	user.Set("Impersonating", userInfo.Impersonating)

	// the goroutines below (and maybe one above) work in parallel
	// and send items to here to be collected
	go collectItems(user, onItem, onDone, collectItemCount)

	// prefetch items
	go sendAccount(userInfo.Account, outputter) // this is fetched above
	go fetchMachines(userInfo.UserId, outputter)
	go fetchWorkspaces(userInfo.AccountId, outputter)

	// return if timeout reached and let client get what it wants
	timeout := time.NewTimer(time.Millisecond * TimeoutTime)

	select {
	case <-onError:
		writeLoggedOutHomeToResp(w)
	case <-timeout.C:
		writeLoggedInHomeToResp(w, user)
	case <-onDone:
		writeLoggedInHomeToResp(w, user)
	}
}

func collectItems(resp *LoggedInUser, onItem <-chan Item, onDone chan<- bool, max int) {
	for i := 1; i <= max; i++ {
		item := <-onItem
		resp.Set(item.Name, item.Data)
	}

	onDone <- true
}

func isSocialIdEmpty(id string) bool {
	return id == ""
}
<|endoftext|>