<commit_before>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2020, OpenNebula Project, OpenNebula Systems                */
/*                                                                            */
/* 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 goca

import (
	"fmt"
	"strconv"
	"encoding/json"

	srv_tmpl "github.com/OpenNebula/one/src/oca/go/src/goca/schemas/service_template"
	"github.com/OpenNebula/one/src/oca/go/src/goca/schemas/service"
	"github.com/OpenNebula/one/src/oca/go/src/goca/schemas/shared"
)

var endpointFTemplate string

func init() {
	endpointFTemplate = "service_template"
}

// STemplateController interacts with oneflow service. Uses REST Client.
type STemplateController entityController

// STemplatesController interacts with oneflow services. Uses REST Client.
type STemplatesController entitiesController

// STemplate Controller constructor
func (c *Controller) STemplate(id int) *STemplateController {
	return &STemplateController{c, id}
}

// STemplates Controller constructor
func (c *Controller) STemplates() *STemplatesController {
	return &STemplatesController{c}
}

// OpenNebula Actions

// Get the service template pool
func (stc *STemplatesController) Info() (*srv_tmpl.Pool, error) {
	response, err := stc.c.ClientFlow.HTTPMethod("GET", endpointFTemplate)
	if err != nil {
		return nil, err
	}

	stemplatepool := &srv_tmpl.Pool{}
	pool_str, err := json.Marshal(response.BodyMap()["DOCUMENT_POOL"])
	err = json.Unmarshal([]byte(pool_str), stemplatepool)
	if err != nil {
		return nil, err
	}

	return stemplatepool, err
}

// Get the service template identified by <id>
func (tc *STemplateController) Info() (*srv_tmpl.ServiceTemplate, error) {
	url := urlTemplate(tc.ID)
	response, err := tc.c.ClientFlow.HTTPMethod("GET", url)
	if err != nil {
		return nil, err
	}

	stemplate := &srv_tmpl.ServiceTemplate{}
	stemplate_str, err := json.Marshal(response.BodyMap()["DOCUMENT"])
	err = json.Unmarshal(stemplate_str, stemplate)
	if err != nil {
		return nil, err
	}

	return stemplate, nil
}

// Allocate a service template
// st will be filled with the new ServiceTemplate information
func (tc *STemplatesController) Create(st *srv_tmpl.ServiceTemplate) (bool, error) {
	body :=  make(map[string]interface{})

	// Get Template.Body as map
	tmpl_byte, err := json.Marshal(st.Template.Body)
	if err != nil {
		return false, err
	}
    json.Unmarshal(tmpl_byte, &body)

	// Get response
	response, err := tc.c.ClientFlow.HTTPMethod("POST", endpointFTemplate, body)
	if err != nil {
		return false, err
	}

	// Update current ServiceTemplate with new values
	stemplate_str, err := json.Marshal(response.BodyMap()["DOCUMENT"])
	if err != nil {
		return false, err
	}
	err = json.Unmarshal(stemplate_str, st)
	if err != nil {
		return false, err
	}

	return true, nil
}

// Delete the service resource identified by <id>
func (tc *STemplateController) Delete() (bool, error) {
	url := urlTemplate(tc.ID)

	return tc.c.boolResponse("DELETE", url, nil)
}

// Instantiate the service_template resource identified by <id>
func (tc *STemplateController) Instantiate(extra_tmpl string) (*service.Service, error) {
	url := urlTemplateAction(tc.ID)
	action := make(map[string]interface{})
	args   := make(map[string]interface{})

	// Create request
	json.Unmarshal([]byte(extra_tmpl), &args)
	action["action"] = map[string]interface{}{
		"perform": "instantiate",
		"params": map[string]interface{}{
			"merge_template": args,
		},
	}

	//Get response
	response, err := tc.c.ClientFlow.HTTPMethod("POST", url, action)
	if err != nil {
		return nil, err
	}

	//Build Service from response
	service := &service.Service{}
	service_str, err := json.Marshal(response.BodyMap()["DOCUMENT"])
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(service_str, service)
	if err != nil {
		return nil, err
	}

	return service, nil
}

// Update service template
func (tc *STemplateController) Update(st *srv_tmpl.ServiceTemplate, append bool) (bool, error) {
	tmpl_byte, err := json.Marshal(st.Template.Body)
	if err != nil {
		return false, err
	}

	action := make(map[string]interface{})
	action["action"] = map[string]interface{}{
		"perform": "update",
		"params": map[string]interface{}{
			"append": append,
			"template_json": string(tmpl_byte),
		},
	}

	return tc.action(action)
}

// Rename service template
func (tc *STemplateController) Rename(new_name string) (bool, error) {
	action := make(map[string]interface{})

	action["action"] = map[string]interface{}{
		"perform": "rename",
		"params": map[string]interface{}{
			"name": new_name,
		},
	}

	return tc.action(action)
}

// Clone a service template
func (tc *STemplateController) Clone(clone_name string, recursive bool) (*srv_tmpl.ServiceTemplate, error) {
	url := urlTemplateAction(tc.ID)
	action := make(map[string]interface{})

	action["action"] = map[string]interface{}{
		"perform": "clone",
		"params": map[string]interface{}{
			"name": clone_name,
			"recursive": recursive,
		},
	}

	//Get response
	response, err := tc.c.ClientFlow.HTTPMethod("POST", url, action)
	if err != nil {
		return nil, err
	}

	//Build Service from response
	stemplate := &srv_tmpl.ServiceTemplate{}
	stemplate_str, err := json.Marshal(response.BodyMap()["DOCUMENT"])
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(stemplate_str, stemplate)
	if err != nil {
		return nil, err
	}

	return stemplate, nil
}

// Permissions operations

// Chgrp template
func (tc *STemplateController) Chgrp(gid int) (bool, error) {
	action := make(map[string]interface{})

	action["action"] = map[string]interface{}{
		"perform": "chgrp",
		"params": map[string]interface{}{
			"group_id": gid,
		},
	}

	return tc.action(action)
}

// Chown template
func (tc *STemplateController) Chown(uid, gid int) (bool, error) {
	action := make(map[string]interface{})

	action["action"] = map[string]interface{}{
		"perform": "chown",
		"params": map[string]interface{}{
			"group_id": gid,
			"owner_id":  uid,
		},
	}

	return tc.action(action)
}

// Chmod template
func (tc *STemplateController) Chmod(perm shared.Permissions) (bool, error) {
	action := make(map[string]interface{})

	action["action"] = map[string]interface{}{
		"perform": "chmod",
		"params": map[string]interface{}{
			"octet": strconv.Itoa(perm.Octet()),
		},
	}

	return tc.action(action)
}

// Helpers
// Action handler for service_templates identified by <id>
func (tc *STemplateController) action(action map[string]interface{}) (bool, error) {
	url := urlTemplateAction(tc.ID)

	return tc.c.boolResponse("POST", url, action)
}

func urlTemplateAction(id int) string {
	return fmt.Sprintf("%s/action", urlTemplate(id))
}

func urlTemplate(id int) string {
	return fmt.Sprintf("%s/%s", endpointFTemplate, strconv.Itoa(id))
}

func documentJSON(response *Response) map[string]interface{} {
	responseJSON := response.BodyMap()

	return responseJSON["DOCUMENT"].(map[string]interface{})
}
<commit_msg>M #-: minor fix service_template GOCA (#4913)<commit_after>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2020, OpenNebula Project, OpenNebula Systems                */
/*                                                                            */
/* 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 goca

import (
	"fmt"
	"strconv"
	"encoding/json"

	srv_tmpl "github.com/OpenNebula/one/src/oca/go/src/goca/schemas/service_template"
	"github.com/OpenNebula/one/src/oca/go/src/goca/schemas/service"
	"github.com/OpenNebula/one/src/oca/go/src/goca/schemas/shared"
)

var endpointFTemplate string

func init() {
	endpointFTemplate = "service_template"
}

// STemplateController interacts with oneflow service. Uses REST Client.
type STemplateController entityController

// STemplatesController interacts with oneflow services. Uses REST Client.
type STemplatesController entitiesController

// STemplate Controller constructor
func (c *Controller) STemplate(id int) *STemplateController {
	return &STemplateController{c, id}
}

// STemplates Controller constructor
func (c *Controller) STemplates() *STemplatesController {
	return &STemplatesController{c}
}

// OpenNebula Actions

// Get the service template pool
func (stc *STemplatesController) Info() (*srv_tmpl.Pool, error) {
	response, err := stc.c.ClientFlow.HTTPMethod("GET", endpointFTemplate)
	if err != nil {
		return nil, err
	}

	stemplatepool := &srv_tmpl.Pool{}
	pool_str, err := json.Marshal(response.BodyMap()["DOCUMENT_POOL"])
	err = json.Unmarshal([]byte(pool_str), stemplatepool)
	if err != nil {
		return nil, err
	}

	return stemplatepool, err
}

// Get the service template identified by <id>
func (tc *STemplateController) Info() (*srv_tmpl.ServiceTemplate, error) {
	url := urlTemplate(tc.ID)
	response, err := tc.c.ClientFlow.HTTPMethod("GET", url)
	if err != nil {
		return nil, err
	}

	stemplate := &srv_tmpl.ServiceTemplate{}
	stemplate_str, err := json.Marshal(response.BodyMap()["DOCUMENT"])
	err = json.Unmarshal(stemplate_str, stemplate)
	if err != nil {
		return nil, err
	}

	return stemplate, nil
}

// Allocate a service template
// st will be filled with the new ServiceTemplate information
func (tc *STemplatesController) Create(st *srv_tmpl.ServiceTemplate) (bool, error) {
	body :=  make(map[string]interface{})

	// Get Template.Body as map
	tmpl_byte, err := json.Marshal(st.Template.Body)
	if err != nil {
		return false, err
	}
    json.Unmarshal(tmpl_byte, &body)

	// Get response
	response, err := tc.c.ClientFlow.HTTPMethod("POST", endpointFTemplate, body)
	if err != nil {
		return false, err
	}

	// Update current ServiceTemplate with new values
	stemplate_str, err := json.Marshal(response.BodyMap()["DOCUMENT"])
	if err != nil {
		return false, err
	}
	err = json.Unmarshal(stemplate_str, st)
	if err != nil {
		return false, err
	}

	return true, nil
}

// Delete the service resource identified by <id>
func (tc *STemplateController) Delete() (bool, error) {
	url := urlTemplate(tc.ID)

	return tc.c.boolResponse("DELETE", url, nil)
}

// Instantiate the service_template resource identified by <id>
func (tc *STemplateController) Instantiate(extra_tmpl string) (*service.Service, error) {
	url := urlTemplateAction(tc.ID)
	action := make(map[string]interface{})
	args   := make(map[string]interface{})
	params := make(map[string]interface{})

	if extra_tmpl != "" {
		err := json.Unmarshal([]byte(extra_tmpl), &args)
		if err != nil {
			return nil, err
		}

		params["merge_template"] = args
	}

	// Create request
	action["action"] = map[string]interface{}{
		"perform": "instantiate",
		"params": params,
	}

	//Get response
	response, err := tc.c.ClientFlow.HTTPMethod("POST", url, action)
	if err != nil {
		return nil, err
	}

	//Build Service from response
	service := &service.Service{}
	service_str, err := json.Marshal(response.BodyMap()["DOCUMENT"])
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(service_str, service)
	if err != nil {
		return nil, err
	}

	return service, nil
}

// Update service template
func (tc *STemplateController) Update(st *srv_tmpl.ServiceTemplate, append bool) (bool, error) {
	tmpl_byte, err := json.Marshal(st.Template.Body)
	if err != nil {
		return false, err
	}

	action := make(map[string]interface{})
	action["action"] = map[string]interface{}{
		"perform": "update",
		"params": map[string]interface{}{
			"append": append,
			"template_json": string(tmpl_byte),
		},
	}

	return tc.action(action)
}

// Rename service template
func (tc *STemplateController) Rename(new_name string) (bool, error) {
	action := make(map[string]interface{})

	action["action"] = map[string]interface{}{
		"perform": "rename",
		"params": map[string]interface{}{
			"name": new_name,
		},
	}

	return tc.action(action)
}

// Clone a service template
func (tc *STemplateController) Clone(clone_name string, recursive bool) (*srv_tmpl.ServiceTemplate, error) {
	url := urlTemplateAction(tc.ID)
	action := make(map[string]interface{})

	action["action"] = map[string]interface{}{
		"perform": "clone",
		"params": map[string]interface{}{
			"name": clone_name,
			"recursive": recursive,
		},
	}

	//Get response
	response, err := tc.c.ClientFlow.HTTPMethod("POST", url, action)
	if err != nil {
		return nil, err
	}

	//Build Service from response
	stemplate := &srv_tmpl.ServiceTemplate{}
	stemplate_str, err := json.Marshal(response.BodyMap()["DOCUMENT"])
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(stemplate_str, stemplate)
	if err != nil {
		return nil, err
	}

	return stemplate, nil
}

// Permissions operations

// Chgrp template
func (tc *STemplateController) Chgrp(gid int) (bool, error) {
	action := make(map[string]interface{})

	action["action"] = map[string]interface{}{
		"perform": "chgrp",
		"params": map[string]interface{}{
			"group_id": gid,
		},
	}

	return tc.action(action)
}

// Chown template
func (tc *STemplateController) Chown(uid, gid int) (bool, error) {
	action := make(map[string]interface{})

	action["action"] = map[string]interface{}{
		"perform": "chown",
		"params": map[string]interface{}{
			"group_id": gid,
			"owner_id":  uid,
		},
	}

	return tc.action(action)
}

// Chmod template
func (tc *STemplateController) Chmod(perm shared.Permissions) (bool, error) {
	action := make(map[string]interface{})

	action["action"] = map[string]interface{}{
		"perform": "chmod",
		"params": map[string]interface{}{
			"octet": strconv.Itoa(perm.Octet()),
		},
	}

	return tc.action(action)
}

// Helpers
// Action handler for service_templates identified by <id>
func (tc *STemplateController) action(action map[string]interface{}) (bool, error) {
	url := urlTemplateAction(tc.ID)

	return tc.c.boolResponse("POST", url, action)
}

func urlTemplateAction(id int) string {
	return fmt.Sprintf("%s/action", urlTemplate(id))
}

func urlTemplate(id int) string {
	return fmt.Sprintf("%s/%s", endpointFTemplate, strconv.Itoa(id))
}

func documentJSON(response *Response) map[string]interface{} {
	responseJSON := response.BodyMap()

	return responseJSON["DOCUMENT"].(map[string]interface{})
}
<|endoftext|>


<commit_before>package metrics

import (
	"bytes"
	"fmt"
	"math"
	"net/url"
	"strings"
	"sync"
	"time"
)

var spaceReplacer = strings.NewReplacer(" ", "_")

// InmemSink provides a MetricSink that does in-memory aggregation
// without sending metrics over a network. It can be embedded within
// an application to provide profiling information.
type InmemSink struct {
	// How long is each aggregation interval
	interval time.Duration

	// Retain controls how many metrics interval we keep
	retain time.Duration

	// maxIntervals is the maximum length of intervals.
	// It is retain / interval.
	maxIntervals int

	// intervals is a slice of the retained intervals
	intervals    []*IntervalMetrics
	intervalLock sync.RWMutex

	rateDenom float64
}

// IntervalMetrics stores the aggregated metrics
// for a specific interval
type IntervalMetrics struct {
	sync.RWMutex

	// The start time of the interval
	Interval time.Time

	// Gauges maps the key to the last set value
	Gauges map[string]GaugeValue

	// Points maps the string to the list of emitted values
	// from EmitKey
	Points map[string][]float32

	// Counters maps the string key to a sum of the counter
	// values
	Counters map[string]SampledValue

	// Samples maps the key to an AggregateSample,
	// which has the rolled up view of a sample
	Samples map[string]SampledValue
}

// NewIntervalMetrics creates a new IntervalMetrics for a given interval
func NewIntervalMetrics(intv time.Time) *IntervalMetrics {
	return &IntervalMetrics{
		Interval: intv,
		Gauges:   make(map[string]GaugeValue),
		Points:   make(map[string][]float32),
		Counters: make(map[string]SampledValue),
		Samples:  make(map[string]SampledValue),
	}
}

// AggregateSample is used to hold aggregate metrics
// about a sample
type AggregateSample struct {
	Count       int       // The count of emitted pairs
	Rate        float64   // The values rate per time unit (usually 1 second)
	Sum         float64   // The sum of values
	SumSq       float64   `json:"-"` // The sum of squared values
	Min         float64   // Minimum value
	Max         float64   // Maximum value
	LastUpdated time.Time `json:"-"` // When value was last updated
}

// Computes a Stddev of the values
func (a *AggregateSample) Stddev() float64 {
	num := (float64(a.Count) * a.SumSq) - math.Pow(a.Sum, 2)
	div := float64(a.Count * (a.Count - 1))
	if div == 0 {
		return 0
	}
	return math.Sqrt(num / div)
}

// Computes a mean of the values
func (a *AggregateSample) Mean() float64 {
	if a.Count == 0 {
		return 0
	}
	return a.Sum / float64(a.Count)
}

// Ingest is used to update a sample
func (a *AggregateSample) Ingest(v float64, rateDenom float64) {
	a.Count++
	a.Sum += v
	a.SumSq += (v * v)
	if v < a.Min || a.Count == 1 {
		a.Min = v
	}
	if v > a.Max || a.Count == 1 {
		a.Max = v
	}
	a.Rate = float64(a.Sum) / rateDenom
	a.LastUpdated = time.Now()
}

func (a *AggregateSample) String() string {
	if a.Count == 0 {
		return "Count: 0"
	} else if a.Stddev() == 0 {
		return fmt.Sprintf("Count: %d Sum: %0.3f LastUpdated: %s", a.Count, a.Sum, a.LastUpdated)
	} else {
		return fmt.Sprintf("Count: %d Min: %0.3f Mean: %0.3f Max: %0.3f Stddev: %0.3f Sum: %0.3f LastUpdated: %s",
			a.Count, a.Min, a.Mean(), a.Max, a.Stddev(), a.Sum, a.LastUpdated)
	}
}

// NewInmemSinkFromURL creates an InmemSink from a URL. It is used
// (and tested) from NewMetricSinkFromURL.
func NewInmemSinkFromURL(u *url.URL) (MetricSink, error) {
	params := u.Query()

	interval, err := time.ParseDuration(params.Get("interval"))
	if err != nil {
		return nil, fmt.Errorf("Bad 'interval' param: %s", err)
	}

	retain, err := time.ParseDuration(params.Get("retain"))
	if err != nil {
		return nil, fmt.Errorf("Bad 'retain' param: %s", err)
	}

	return NewInmemSink(interval, retain), nil
}

// NewInmemSink is used to construct a new in-memory sink.
// Uses an aggregation interval and maximum retention period.
func NewInmemSink(interval, retain time.Duration) *InmemSink {
	rateTimeUnit := time.Second
	i := &InmemSink{
		interval:     interval,
		retain:       retain,
		maxIntervals: int(retain / interval),
		rateDenom:    float64(interval.Nanoseconds()) / float64(rateTimeUnit.Nanoseconds()),
	}
	i.intervals = make([]*IntervalMetrics, 0, i.maxIntervals)
	return i
}

func (i *InmemSink) SetGauge(key []string, val float32) {
	i.SetGaugeWithLabels(key, val, nil)
}

func (i *InmemSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {
	k, name := i.flattenKeyLabels(key, labels)
	intv := i.getInterval()

	intv.Lock()
	defer intv.Unlock()
	intv.Gauges[k] = GaugeValue{Name: name, Value: val, Labels: labels}
}

func (i *InmemSink) EmitKey(key []string, val float32) {
	k := i.flattenKey(key)
	intv := i.getInterval()

	intv.Lock()
	defer intv.Unlock()
	vals := intv.Points[k]
	intv.Points[k] = append(vals, val)
}

func (i *InmemSink) IncrCounter(key []string, val float32) {
	i.IncrCounterWithLabels(key, val, nil)
}

func (i *InmemSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {
	k, name := i.flattenKeyLabels(key, labels)
	intv := i.getInterval()

	intv.Lock()
	defer intv.Unlock()

	agg, ok := intv.Counters[k]
	if !ok {
		agg = SampledValue{
			Name:            name,
			AggregateSample: &AggregateSample{},
			Labels:          labels,
		}
		intv.Counters[k] = agg
	}
	agg.Ingest(float64(val), i.rateDenom)
}

func (i *InmemSink) AddSample(key []string, val float32) {
	i.AddSampleWithLabels(key, val, nil)
}

func (i *InmemSink) AddSampleWithLabels(key []string, val float32, labels []Label) {
	k, name := i.flattenKeyLabels(key, labels)
	intv := i.getInterval()

	intv.Lock()
	defer intv.Unlock()

	agg, ok := intv.Samples[k]
	if !ok {
		agg = SampledValue{
			Name:            name,
			AggregateSample: &AggregateSample{},
			Labels:          labels,
		}
		intv.Samples[k] = agg
	}
	agg.Ingest(float64(val), i.rateDenom)
}

// Data is used to retrieve all the aggregated metrics
// Intervals may be in use, and a read lock should be acquired
func (i *InmemSink) Data() []*IntervalMetrics {
	// Get the current interval, forces creation
	i.getInterval()

	i.intervalLock.RLock()
	defer i.intervalLock.RUnlock()

	n := len(i.intervals)
	intervals := make([]*IntervalMetrics, n)

	copy(intervals[:n-1], i.intervals[:n-1])
	current := i.intervals[n-1]

	// make its own copy for current interval
	intervals[n-1] = &IntervalMetrics{}
	copyCurrent := intervals[n-1]
	current.RLock()
	*copyCurrent = *current
	// RWMutex is not safe to copy, so create a new instance on the copy
	copyCurrent.RWMutex = sync.RWMutex{}

	copyCurrent.Gauges = make(map[string]GaugeValue, len(current.Gauges))
	for k, v := range current.Gauges {
		copyCurrent.Gauges[k] = v
	}
	// saved values will be not change, just copy its link
	copyCurrent.Points = make(map[string][]float32, len(current.Points))
	for k, v := range current.Points {
		copyCurrent.Points[k] = v
	}
	copyCurrent.Counters = make(map[string]SampledValue, len(current.Counters))
	for k, v := range current.Counters {
		copyCurrent.Counters[k] = v.deepCopy()
	}
	copyCurrent.Samples = make(map[string]SampledValue, len(current.Samples))
	for k, v := range current.Samples {
		copyCurrent.Samples[k] = v.deepCopy()
	}
	current.RUnlock()

	return intervals
}

func (i *InmemSink) getExistingInterval(intv time.Time) *IntervalMetrics {
	i.intervalLock.RLock()
	defer i.intervalLock.RUnlock()

	n := len(i.intervals)
	if n > 0 && i.intervals[n-1].Interval == intv {
		return i.intervals[n-1]
	}
	return nil
}

func (i *InmemSink) createInterval(intv time.Time) *IntervalMetrics {
	i.intervalLock.Lock()
	defer i.intervalLock.Unlock()

	// Check for an existing interval
	n := len(i.intervals)
	if n > 0 && i.intervals[n-1].Interval == intv {
		return i.intervals[n-1]
	}

	// Add the current interval
	current := NewIntervalMetrics(intv)
	i.intervals = append(i.intervals, current)
	n++

	// Truncate the intervals if they are too long
	if n >= i.maxIntervals {
		copy(i.intervals[0:], i.intervals[n-i.maxIntervals:])
		i.intervals = i.intervals[:i.maxIntervals]
	}
	return current
}

// getInterval returns the current interval to write to
func (i *InmemSink) getInterval() *IntervalMetrics {
	intv := time.Now().Truncate(i.interval)
	if m := i.getExistingInterval(intv); m != nil {
		return m
	}
	return i.createInterval(intv)
}

// Flattens the key for formatting, removes spaces
func (i *InmemSink) flattenKey(parts []string) string {
	buf := &bytes.Buffer{}

	joined := strings.Join(parts, ".")

	spaceReplacer.WriteString(buf, joined)

	return buf.String()
}

// Flattens the key for formatting along with its labels, removes spaces
func (i *InmemSink) flattenKeyLabels(parts []string, labels []Label) (string, string) {
	key := i.flattenKey(parts)
	buf := bytes.NewBufferString(key)

	for _, label := range labels {
		spaceReplacer.WriteString(buf, fmt.Sprintf(";%s=%s", label.Name, label.Value))
	}

	return buf.String(), key
}
<commit_msg>inmem: Inline a few functions into getInterval<commit_after>package metrics

import (
	"bytes"
	"fmt"
	"math"
	"net/url"
	"strings"
	"sync"
	"time"
)

var spaceReplacer = strings.NewReplacer(" ", "_")

// InmemSink provides a MetricSink that does in-memory aggregation
// without sending metrics over a network. It can be embedded within
// an application to provide profiling information.
type InmemSink struct {
	// How long is each aggregation interval
	interval time.Duration

	// Retain controls how many metrics interval we keep
	retain time.Duration

	// maxIntervals is the maximum length of intervals.
	// It is retain / interval.
	maxIntervals int

	// intervals is a slice of the retained intervals
	intervals    []*IntervalMetrics
	intervalLock sync.RWMutex

	rateDenom float64
}

// IntervalMetrics stores the aggregated metrics
// for a specific interval
type IntervalMetrics struct {
	sync.RWMutex

	// The start time of the interval
	Interval time.Time

	// Gauges maps the key to the last set value
	Gauges map[string]GaugeValue

	// Points maps the string to the list of emitted values
	// from EmitKey
	Points map[string][]float32

	// Counters maps the string key to a sum of the counter
	// values
	Counters map[string]SampledValue

	// Samples maps the key to an AggregateSample,
	// which has the rolled up view of a sample
	Samples map[string]SampledValue

	// done is closed when this interval has ended, and a new IntervalMetrics
	// has been created to receive any future metrics.
	done chan struct{}
}

// NewIntervalMetrics creates a new IntervalMetrics for a given interval
func NewIntervalMetrics(intv time.Time) *IntervalMetrics {
	return &IntervalMetrics{
		Interval: intv,
		Gauges:   make(map[string]GaugeValue),
		Points:   make(map[string][]float32),
		Counters: make(map[string]SampledValue),
		Samples:  make(map[string]SampledValue),
	}
}

// AggregateSample is used to hold aggregate metrics
// about a sample
type AggregateSample struct {
	Count       int       // The count of emitted pairs
	Rate        float64   // The values rate per time unit (usually 1 second)
	Sum         float64   // The sum of values
	SumSq       float64   `json:"-"` // The sum of squared values
	Min         float64   // Minimum value
	Max         float64   // Maximum value
	LastUpdated time.Time `json:"-"` // When value was last updated
}

// Computes a Stddev of the values
func (a *AggregateSample) Stddev() float64 {
	num := (float64(a.Count) * a.SumSq) - math.Pow(a.Sum, 2)
	div := float64(a.Count * (a.Count - 1))
	if div == 0 {
		return 0
	}
	return math.Sqrt(num / div)
}

// Computes a mean of the values
func (a *AggregateSample) Mean() float64 {
	if a.Count == 0 {
		return 0
	}
	return a.Sum / float64(a.Count)
}

// Ingest is used to update a sample
func (a *AggregateSample) Ingest(v float64, rateDenom float64) {
	a.Count++
	a.Sum += v
	a.SumSq += (v * v)
	if v < a.Min || a.Count == 1 {
		a.Min = v
	}
	if v > a.Max || a.Count == 1 {
		a.Max = v
	}
	a.Rate = float64(a.Sum) / rateDenom
	a.LastUpdated = time.Now()
}

func (a *AggregateSample) String() string {
	if a.Count == 0 {
		return "Count: 0"
	} else if a.Stddev() == 0 {
		return fmt.Sprintf("Count: %d Sum: %0.3f LastUpdated: %s", a.Count, a.Sum, a.LastUpdated)
	} else {
		return fmt.Sprintf("Count: %d Min: %0.3f Mean: %0.3f Max: %0.3f Stddev: %0.3f Sum: %0.3f LastUpdated: %s",
			a.Count, a.Min, a.Mean(), a.Max, a.Stddev(), a.Sum, a.LastUpdated)
	}
}

// NewInmemSinkFromURL creates an InmemSink from a URL. It is used
// (and tested) from NewMetricSinkFromURL.
func NewInmemSinkFromURL(u *url.URL) (MetricSink, error) {
	params := u.Query()

	interval, err := time.ParseDuration(params.Get("interval"))
	if err != nil {
		return nil, fmt.Errorf("Bad 'interval' param: %s", err)
	}

	retain, err := time.ParseDuration(params.Get("retain"))
	if err != nil {
		return nil, fmt.Errorf("Bad 'retain' param: %s", err)
	}

	return NewInmemSink(interval, retain), nil
}

// NewInmemSink is used to construct a new in-memory sink.
// Uses an aggregation interval and maximum retention period.
func NewInmemSink(interval, retain time.Duration) *InmemSink {
	rateTimeUnit := time.Second
	i := &InmemSink{
		interval:     interval,
		retain:       retain,
		maxIntervals: int(retain / interval),
		rateDenom:    float64(interval.Nanoseconds()) / float64(rateTimeUnit.Nanoseconds()),
	}
	i.intervals = make([]*IntervalMetrics, 0, i.maxIntervals)
	return i
}

func (i *InmemSink) SetGauge(key []string, val float32) {
	i.SetGaugeWithLabels(key, val, nil)
}

func (i *InmemSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {
	k, name := i.flattenKeyLabels(key, labels)
	intv := i.getInterval()

	intv.Lock()
	defer intv.Unlock()
	intv.Gauges[k] = GaugeValue{Name: name, Value: val, Labels: labels}
}

func (i *InmemSink) EmitKey(key []string, val float32) {
	k := i.flattenKey(key)
	intv := i.getInterval()

	intv.Lock()
	defer intv.Unlock()
	vals := intv.Points[k]
	intv.Points[k] = append(vals, val)
}

func (i *InmemSink) IncrCounter(key []string, val float32) {
	i.IncrCounterWithLabels(key, val, nil)
}

func (i *InmemSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {
	k, name := i.flattenKeyLabels(key, labels)
	intv := i.getInterval()

	intv.Lock()
	defer intv.Unlock()

	agg, ok := intv.Counters[k]
	if !ok {
		agg = SampledValue{
			Name:            name,
			AggregateSample: &AggregateSample{},
			Labels:          labels,
		}
		intv.Counters[k] = agg
	}
	agg.Ingest(float64(val), i.rateDenom)
}

func (i *InmemSink) AddSample(key []string, val float32) {
	i.AddSampleWithLabels(key, val, nil)
}

func (i *InmemSink) AddSampleWithLabels(key []string, val float32, labels []Label) {
	k, name := i.flattenKeyLabels(key, labels)
	intv := i.getInterval()

	intv.Lock()
	defer intv.Unlock()

	agg, ok := intv.Samples[k]
	if !ok {
		agg = SampledValue{
			Name:            name,
			AggregateSample: &AggregateSample{},
			Labels:          labels,
		}
		intv.Samples[k] = agg
	}
	agg.Ingest(float64(val), i.rateDenom)
}

// Data is used to retrieve all the aggregated metrics
// Intervals may be in use, and a read lock should be acquired
func (i *InmemSink) Data() []*IntervalMetrics {
	// Get the current interval, forces creation
	i.getInterval()

	i.intervalLock.RLock()
	defer i.intervalLock.RUnlock()

	n := len(i.intervals)
	intervals := make([]*IntervalMetrics, n)

	copy(intervals[:n-1], i.intervals[:n-1])
	current := i.intervals[n-1]

	// make its own copy for current interval
	intervals[n-1] = &IntervalMetrics{}
	copyCurrent := intervals[n-1]
	current.RLock()
	*copyCurrent = *current
	// RWMutex is not safe to copy, so create a new instance on the copy
	copyCurrent.RWMutex = sync.RWMutex{}

	copyCurrent.Gauges = make(map[string]GaugeValue, len(current.Gauges))
	for k, v := range current.Gauges {
		copyCurrent.Gauges[k] = v
	}
	// saved values will be not change, just copy its link
	copyCurrent.Points = make(map[string][]float32, len(current.Points))
	for k, v := range current.Points {
		copyCurrent.Points[k] = v
	}
	copyCurrent.Counters = make(map[string]SampledValue, len(current.Counters))
	for k, v := range current.Counters {
		copyCurrent.Counters[k] = v.deepCopy()
	}
	copyCurrent.Samples = make(map[string]SampledValue, len(current.Samples))
	for k, v := range current.Samples {
		copyCurrent.Samples[k] = v.deepCopy()
	}
	current.RUnlock()

	return intervals
}

// getInterval returns the current interval. A new interval is created if no
// previous interval exists, or if the current time is beyond the window for the
// current interval.
func (i *InmemSink) getInterval() *IntervalMetrics {
	intv := time.Now().Truncate(i.interval)

	// Attempt to return the existing interval first, because it only requires
	// a read lock.
	i.intervalLock.RLock()
	n := len(i.intervals)
	if n > 0 && i.intervals[n-1].Interval == intv {
		defer i.intervalLock.RUnlock()
		return i.intervals[n-1]
	}
	i.intervalLock.RUnlock()

	i.intervalLock.Lock()
	defer i.intervalLock.Unlock()

	// Re-check for an existing interval now that the lock is re-acquired.
	n = len(i.intervals)
	if n > 0 && i.intervals[n-1].Interval == intv {
		return i.intervals[n-1]
	}

	current := NewIntervalMetrics(intv)
	i.intervals = append(i.intervals, current)
	n++

	// Prune old intervals if the count exceeds the max.
	if n >= i.maxIntervals {
		copy(i.intervals[0:], i.intervals[n-i.maxIntervals:])
		i.intervals = i.intervals[:i.maxIntervals]
	}
	return current
}

// Flattens the key for formatting, removes spaces
func (i *InmemSink) flattenKey(parts []string) string {
	buf := &bytes.Buffer{}

	joined := strings.Join(parts, ".")

	spaceReplacer.WriteString(buf, joined)

	return buf.String()
}

// Flattens the key for formatting along with its labels, removes spaces
func (i *InmemSink) flattenKeyLabels(parts []string, labels []Label) (string, string) {
	key := i.flattenKey(parts)
	buf := bytes.NewBufferString(key)

	for _, label := range labels {
		spaceReplacer.WriteString(buf, fmt.Sprintf(";%s=%s", label.Name, label.Value))
	}

	return buf.String(), key
}
<|endoftext|>


<commit_before>//: ----------------------------------------------------------------------------
//: Copyright (C) 2017 Verizon.  All Rights Reserved.
//: All Rights Reserved
//:
//: file:    ipfix.go
//: details: TODO
//: author:  Mehrdad Arshad Rad
//: date:    02/01/2017
//:
//: 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 main

import (
	"net"
	"strconv"
	"sync"
	"time"

	"git.edgecastcdn.net/vflow/ipfix"
	"git.edgecastcdn.net/vflow/mirror"
)

type IPFIX struct {
	port    int
	addr    string
	udpSize int
	workers int
	stop    bool
}

type IPFIXUDPMsg struct {
	raddr *net.UDPAddr
	body  []byte
}

var (
	ipfixUdpCh         = make(chan IPFIXUDPMsg, 1000)
	ipfixMCh           = make(chan IPFIXUDPMsg, 1000)
	ipfixMirrorEnabled bool

	// templates memory cache
	mCache ipfix.MemCache
)

func NewIPFIX() *IPFIX {
	return &IPFIX{
		port:    opts.IPFIXPort,
		udpSize: opts.IPFIXUDPSize,
		workers: opts.IPFIXWorkers,
	}
}

func (i *IPFIX) run() {
	var wg sync.WaitGroup

	hostPort := net.JoinHostPort(i.addr, strconv.Itoa(i.port))
	udpAddr, _ := net.ResolveUDPAddr("udp", hostPort)

	conn, err := net.ListenUDP("udp", udpAddr)
	if err != nil {

	}

	for n := 0; n < i.workers; n++ {
		go func() {
			wg.Add(1)
			defer wg.Done()
			ipfixWorker()

		}()
	}

	logger.Printf("ipfix is running (workers#: %d)", i.workers)

	mCache = ipfix.NewCache()

	go func() {
		mirrorIPFIX(ipfixMCh)
	}()

	for !i.stop {
		b := make([]byte, i.udpSize)
		conn.SetReadDeadline(time.Now().Add(1e9))
		n, raddr, err := conn.ReadFromUDP(b)
		if err != nil {
			continue
		}
		ipfixUdpCh <- IPFIXUDPMsg{raddr, b[:n]}
	}

	wg.Wait()
}

func (i *IPFIX) shutdown() {
	i.stop = true
	logger.Println("stopped ipfix service gracefully ...")
	time.Sleep(1 * time.Second)
	logger.Println("ipfix has been shutdown")
	close(ipfixUdpCh)
}

func ipfixWorker() {
	var (
		msg IPFIXUDPMsg
		ok  bool
	)

	for {
		if msg, ok = <-ipfixUdpCh; !ok {
			break
		}

		if verbose {
			logger.Printf("rcvd ipfix data from: %s, size: %d bytes",
				msg.raddr, len(msg.body))
		}

		if ipfixMirrorEnabled {
			ipfixMCh <- IPFIXUDPMsg{msg.raddr, append([]byte{}, msg.body...)}
		}

		d := ipfix.NewDecoder(msg.raddr.IP, msg.body)
		d.Decode(mCache)
	}
}

func mirrorIPFIXv4(dst net.IP, port int, ch chan IPFIXUDPMsg) error {
	var (
		packet = make([]byte, 1500)
		msg    IPFIXUDPMsg
		pLen   int
		err    error
	)

	conn, err := mirror.NewRawConn(dst)
	if err != nil {
		return err
	}

	udp := mirror.UDP{55117, port, 0, 0}
	udpHdr := udp.Marshal()

	ip := mirror.NewIPv4HeaderTpl(mirror.UDPProto)
	ipHdr := ip.Marshal()

	for {
		msg = <-ch
		pLen = len(msg.body)

		ip.SetAddrs(ipHdr, msg.raddr.IP, dst)
		ip.SetLen(ipHdr, pLen+mirror.UDPHLen)
		udp.SetLen(udpHdr, pLen)

		copy(packet[0:20], ipHdr)
		copy(packet[20:28], udpHdr)
		copy(packet[28:], msg.body)

		if err = conn.Send(packet[0 : 28+pLen]); err != nil {
			return err
		}
	}
	return nil
}

func mirrorIPFIX(ch chan IPFIXUDPMsg) {
	var (
		ch4 = make(chan IPFIXUDPMsg, 1000)
		msg IPFIXUDPMsg
	)

	if opts.IPFIXMirror == "" {
		return
	}

	host, port, err := net.SplitHostPort(opts.IPFIXMirror)
	if err != nil {
		logger.Fatalf("wrong ipfix mirror address%s", opts.IPFIXMirror)
	}

	portNo, _ := strconv.Atoi(port)
	dst := net.ParseIP(host)

	if dst.To4() != nil {
		go mirrorIPFIXv4(dst, portNo, ch4)
	}

	ipfixMirrorEnabled = true
	logger.Println("ipfix mirror service is running ...")

	for {
		msg = <-ch
		if msg.raddr.IP.To4() != nil {
			ch4 <- msg
		}
	}
}
<commit_msg>add logging<commit_after>//: ----------------------------------------------------------------------------
//: Copyright (C) 2017 Verizon.  All Rights Reserved.
//: All Rights Reserved
//:
//: file:    ipfix.go
//: details: TODO
//: author:  Mehrdad Arshad Rad
//: date:    02/01/2017
//:
//: 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 main

import (
	"net"
	"strconv"
	"sync"
	"time"

	"git.edgecastcdn.net/vflow/ipfix"
	"git.edgecastcdn.net/vflow/mirror"
)

type IPFIX struct {
	port    int
	addr    string
	udpSize int
	workers int
	stop    bool
}

type IPFIXUDPMsg struct {
	raddr *net.UDPAddr
	body  []byte
}

var (
	ipfixUdpCh         = make(chan IPFIXUDPMsg, 1000)
	ipfixMCh           = make(chan IPFIXUDPMsg, 1000)
	ipfixMirrorEnabled bool

	// templates memory cache
	mCache ipfix.MemCache
)

func NewIPFIX() *IPFIX {
	return &IPFIX{
		port:    opts.IPFIXPort,
		udpSize: opts.IPFIXUDPSize,
		workers: opts.IPFIXWorkers,
	}
}

func (i *IPFIX) run() {
	var wg sync.WaitGroup

	hostPort := net.JoinHostPort(i.addr, strconv.Itoa(i.port))
	udpAddr, _ := net.ResolveUDPAddr("udp", hostPort)

	conn, err := net.ListenUDP("udp", udpAddr)
	if err != nil {

	}

	for n := 0; n < i.workers; n++ {
		go func() {
			wg.Add(1)
			defer wg.Done()
			ipfixWorker()

		}()
	}

	logger.Printf("ipfix is running (workers#: %d)", i.workers)

	mCache = ipfix.NewCache()

	go func() {
		mirrorIPFIX(ipfixMCh)
	}()

	for !i.stop {
		b := make([]byte, i.udpSize)
		conn.SetReadDeadline(time.Now().Add(1e9))
		n, raddr, err := conn.ReadFromUDP(b)
		if err != nil {
			continue
		}
		ipfixUdpCh <- IPFIXUDPMsg{raddr, b[:n]}
	}

	wg.Wait()
}

func (i *IPFIX) shutdown() {
	i.stop = true
	logger.Println("stopped ipfix service gracefully ...")
	time.Sleep(1 * time.Second)
	logger.Println("ipfix has been shutdown")
	close(ipfixUdpCh)
}

func ipfixWorker() {
	var (
		msg IPFIXUDPMsg
		ok  bool
	)

	for {
		if msg, ok = <-ipfixUdpCh; !ok {
			break
		}

		if verbose {
			logger.Printf("rcvd ipfix data from: %s, size: %d bytes",
				msg.raddr, len(msg.body))
		}

		if ipfixMirrorEnabled {
			ipfixMCh <- IPFIXUDPMsg{msg.raddr, append([]byte{}, msg.body...)}
		}

		d := ipfix.NewDecoder(msg.raddr.IP, msg.body)
		if _, err := d.Decode(mCache); err != nil {
			logger.Println(err)
		}

	}
}

func mirrorIPFIXv4(dst net.IP, port int, ch chan IPFIXUDPMsg) error {
	var (
		packet = make([]byte, 1500)
		msg    IPFIXUDPMsg
		pLen   int
		err    error
	)

	conn, err := mirror.NewRawConn(dst)
	if err != nil {
		return err
	}

	udp := mirror.UDP{55117, port, 0, 0}
	udpHdr := udp.Marshal()

	ip := mirror.NewIPv4HeaderTpl(mirror.UDPProto)
	ipHdr := ip.Marshal()

	for {
		msg = <-ch
		pLen = len(msg.body)

		ip.SetAddrs(ipHdr, msg.raddr.IP, dst)
		ip.SetLen(ipHdr, pLen+mirror.UDPHLen)
		udp.SetLen(udpHdr, pLen)

		copy(packet[0:20], ipHdr)
		copy(packet[20:28], udpHdr)
		copy(packet[28:], msg.body)

		if err = conn.Send(packet[0 : 28+pLen]); err != nil {
			return err
		}
	}
	return nil
}

func mirrorIPFIX(ch chan IPFIXUDPMsg) {
	var (
		ch4 = make(chan IPFIXUDPMsg, 1000)
		msg IPFIXUDPMsg
	)

	if opts.IPFIXMirror == "" {
		return
	}

	host, port, err := net.SplitHostPort(opts.IPFIXMirror)
	if err != nil {
		logger.Fatalf("wrong ipfix mirror address%s", opts.IPFIXMirror)
	}

	portNo, _ := strconv.Atoi(port)
	dst := net.ParseIP(host)

	if dst.To4() != nil {
		go mirrorIPFIXv4(dst, portNo, ch4)
	}

	ipfixMirrorEnabled = true
	logger.Println("ipfix mirror service is running ...")

	for {
		msg = <-ch
		if msg.raddr.IP.To4() != nil {
			ch4 <- msg
		}
	}
}
<|endoftext|>


<commit_before>package junos

import (
	"fmt"
	"github.com/Juniper/go-netconf/netconf"
	"log"
)

type Session struct {
	Conn *netconf.Session
}

func NewSession(host, user, password string) *Session {
    sess := &Session{}
    s, err := netconf.DialSSH(host, netconf.SSHConfigPassword(user, password))
    if err != nil {
        log.Fatal(err)
    }
    defer s.Close()
    sess.Conn = s

	return sess
}

func (s *Session) Lock() {
	resp, err := s.Conn.Exec("<rpc><lock-configuration/></rpc>")

	if err != nil {
		fmt.Printf("Error: %+v\n", err)
	}

    fmt.Printf("%+v\n", resp)
}

func (s *Session) Unlock() {
	resp, err := s.Conn.Exec("<rpc><unlock-configuration/></rpc>")

	if err != nil {
		fmt.Printf("Error: %+v\n", err)
	}

    fmt.Printf("%+v\n", resp)
}<commit_msg>Changed RPC locking & unlocking<commit_after>package junos

import (
	"fmt"
	"github.com/Juniper/go-netconf/netconf"
	"log"
)

type Session struct {
	Conn *netconf.Session
}

func NewSession(host, user, password string) *Session {
    sess := &Session{}
    s, err := netconf.DialSSH(host, netconf.SSHConfigPassword(user, password))
    if err != nil {
        log.Fatal(err)
    }
    defer s.Close()
    sess.Conn = s

	return sess
}

func (s *Session) Lock() {
	resp, err := s.Conn.Exec("<rpc><lock><target><candidate/></target></lock></rpc>")

	if err != nil {
		fmt.Printf("Error: %+v\n", err)
	}

    fmt.Printf("%+v\n", resp)
}

func (s *Session) Unlock() {
	resp, err := s.Conn.Exec("<rpc><unlock><target><candidate/></target></unlock></rpc>")

	if err != nil {
		fmt.Printf("Error: %+v\n", err)
	}

    fmt.Printf("%+v\n", resp)
}<|endoftext|>


<commit_before>/*

Kasper companion library for stateful stream processing.

*/

package kv

// Entry is a key-value pair for KeyValueStore
type Entry struct {
	Key   string
	Value interface{}
}

// KeyValueStore is universal interface for a key-value store
// Keys are strings, and values are pointers to structs
type KeyValueStore interface {
	Get(key string) (interface{}, error)
	GetAll(keys []string) ([]*Entry, error)
	Put(key string, value interface{}) error
	PutAll(entries []*Entry) error
	Delete(key string) error
	Flush() error
}

func ToMap(entries []*Entry, err error) (map[string]interface{}, error) {
	if err != nil {
		return nil, err
	}
	res := make(map[string] interface{}, len(entries))
	for _, entry := range entries {
		if entry.Value != nil {
			res[entry.Key] = entry.Value
		}
	}
	return res, nil
}

func FromMap(m map[string]interface{}) []*Entry {
	res := make([]*Entry, len(m))
	i := 0
	for key, value := range m {
		if value == nil {
			continue
		}
		res[i] = &Entry{key, value}
		i++
	}
	return res[0:i]
}
<commit_msg>fix nil comparisons to interface{}<commit_after>/*

Kasper companion library for stateful stream processing.

*/

package kv

import (
	"reflect"
)

// Entry is a key-value pair for KeyValueStore
type Entry struct {
	Key   string
	Value interface{}
}

// KeyValueStore is universal interface for a key-value store
// Keys are strings, and values are pointers to structs
type KeyValueStore interface {
	Get(key string) (interface{}, error)
	GetAll(keys []string) ([]*Entry, error)
	Put(key string, value interface{}) error
	PutAll(entries []*Entry) error
	Delete(key string) error
	Flush() error
}

func ToMap(entries []*Entry, err error) (map[string]interface{}, error) {
	if err != nil {
		return nil, err
	}
	res := make(map[string]interface{}, len(entries))
	for _, entry := range entries {
		if entry.Value != reflect.Zero(reflect.TypeOf(entry.Value)).Interface() {
			res[entry.Key] = entry.Value
		}
	}
	return res, nil
}

func FromMap(m map[string]interface{}) []*Entry {
	res := make([]*Entry, len(m))
	i := 0
	for key, value := range m {
		if value == reflect.Zero(reflect.TypeOf(value)).Interface() {
			continue
		}
		res[i] = &Entry{key, value}
		i++
	}
	return res[0:i]
}
<|endoftext|>


<commit_before>package template

import (
	"bytes"
	"unicode"
	"utf8"
)

type token int

const (
	tokIllegal token = iota
	tokEof

	tokIdent  // for
	tokInt    // 12345
	tokFloat  // 123.45e2
	tokString // 'abc' or "abc"

	tokText // text not inside a tag

	tokBlockTagStart   // {%
	tokBlockTagEnd     // %}
	tokVarTagStart     // {{
	tokVarTagEnd       // }}

	tokDot      // .
	tokFilter   // |
	tokArgument // :
)

var tokStrings = map[token]string{
	tokIllegal:         "illegal",
	tokEof:             "eof",
	tokIdent:           "ident",
	tokInt:             "int",
	tokFloat:           "float",
	tokString:          "string",
	tokText:            "text",
	tokBlockTagStart:   "{%",
	tokBlockTagEnd:     "%}",
	tokVarTagStart:     "{{",
	tokVarTagEnd:       "}}",
	tokDot:             ".",
	tokFilter:          "|",
	tokArgument:        ":",
}

type lexer struct {
	src       []byte
	offset    int
	ch        int
	width     int
	insideTag bool
}

func (l *lexer) init() {
	l.ch, l.width = utf8.DecodeRune(l.src)
}

func (l *lexer) next() {
	l.offset += l.width
	if l.offset < len(l.src) {
		r, w := int(l.src[l.offset]), 1
		if r >= utf8.RuneSelf {
			r, w = utf8.DecodeRune(l.src[l.offset:])
		}
		l.width = w
		l.ch = r
	} else {
		l.width = 0
		l.ch = -1
	}
}

func (l *lexer) scan() (token, []byte) {
	if !l.insideTag && l.ch != '{' {
		lit := l.scanText()
		return tokText, lit
	}
	l.insideTag = true
	// Another divergence from Django is that we allow spaces between all tokens. So
	// {{ var . 1 }}
	// is a valid template and does what you would expect. Django doesn't allow this.
	l.consumeWhitespace()

	pos := l.offset
	tok := tokIllegal

	switch ch := l.ch; {
	case unicode.IsLetter(ch), l.ch == '_':
		tok = l.scanIdent()
	case unicode.IsDigit(ch):
		tok = l.scanNumber()
	case ch == '-', ch == '+':
		tok = l.scanNumber()
	case ch == '|':
		tok = tokFilter
		l.next()
	case ch == '.':
		tok = tokDot
		l.next()
	case ch == ':':
		tok = tokArgument
		l.next()
	default:
		l.next()
		switch ch {
		case -1:
			tok = tokEof
		case '{':
			switch l.ch {
			case '%':
				tok = tokBlockTagStart
			case '{':
				tok = tokVarTagStart
			case '#':
				// start of a comment; scan until the end
				l.next()
				for {
					for l.ch != '#' {
						l.next()
					}
					l.next()
					if l.ch == '}' {
						l.next()
						break
					}
				}
				// TODO: probably shouldn't recurse
				return l.scan()
			}
		case '%':
			if l.ch != '}' {
				goto illegal
			}
			l.insideTag = false
			tok = tokBlockTagEnd
		case '}':
			if l.ch != '}' {
				goto illegal
			}
			l.insideTag = false
			tok = tokVarTagEnd
		case '\'', '"':
			tok = l.scanString(byte(ch))
			return tok, l.src[pos+1 : l.offset-1]
		default:
		illegal:
			panic("illegal character")
		}
		l.next()
	}
	return tok, l.src[pos:l.offset]
}

func (l *lexer) consumeWhitespace() {
	for l.ch == ' ' || l.ch == '\t' || l.ch == '\n' || l.ch == '\r' {
		l.next()
	}
}

// There's a known incompatibility here for a template such as:
// {%{##}}}
// Django outputs {%}}. It has to see the opening and the closing symbols for it to be considered a tag.
// Godjan will return an error on this input. Disallowing this unlikely case makes
// lexing much easier and more efficient because we don't have to look ahead.
func (l *lexer) scanText() []byte {
	n := len(l.src) - 1
	off := l.offset
	pos := 0
consume:
	for {
		pos = bytes.IndexByte(l.src[off:], '{')
		if pos < 0 || off+pos >= n {
			break
		}
		off += pos
		switch l.src[off+1] {
		case '%', '{', '#':
			break consume
		}
		off++
	}
	var lit []byte
	if pos < 0 || pos == n {
		lit = l.src[l.offset:]
		l.offset = len(l.src)
		l.width = 0
		l.ch = -1
	} else {
		l.width = 1
		l.ch = '{'
		lit = l.src[l.offset:off]
		l.offset = off
		if l.src[l.offset] != byte(l.ch) {
			panic("Something's wrong")
		}
	}
	l.insideTag = true
	return lit
}

func (l *lexer) scanIdent() token {
	for unicode.IsLetter(l.ch) || unicode.IsDigit(l.ch) || l.ch == '_' {
		l.next()
	}
	return tokIdent
}

func (l *lexer) scanNumber() token {
	tok := tokInt
	seenDecimal := false
	seenExponent := false

	if l.ch == '-' || l.ch == '+' {
		l.next()
	}
	for l.ch >= '0' && l.ch <= '9' ||
		!seenDecimal && !seenExponent && l.ch == '.' ||
		!seenExponent && (l.ch == 'e' || l.ch == 'E') {
		if l.ch == '.' {
			seenDecimal = true
			tok = tokFloat
		}
		if l.ch == 'e' || l.ch == 'E' {
			seenExponent = true
			tok = tokFloat
		}
		l.next()
	}
	return tok
}

func (l *lexer) scanString(start byte) token {
	// ' or " already consumed
	pos := bytes.IndexByte(l.src[l.offset:], start)
	if pos < 0 {
		panic("String not terminated")
	}
	l.offset += pos
	l.width = 1
	l.next()
	return tokString
}
<commit_msg>gofmt<commit_after>package template

import (
	"bytes"
	"unicode"
	"utf8"
)

type token int

const (
	tokIllegal token = iota
	tokEof

	tokIdent  // for
	tokInt    // 12345
	tokFloat  // 123.45e2
	tokString // 'abc' or "abc"

	tokText // text not inside a tag

	tokBlockTagStart // {%
	tokBlockTagEnd   // %}
	tokVarTagStart   // {{
	tokVarTagEnd     // }}

	tokDot      // .
	tokFilter   // |
	tokArgument // :
)

var tokStrings = map[token]string{
	tokIllegal:       "illegal",
	tokEof:           "eof",
	tokIdent:         "ident",
	tokInt:           "int",
	tokFloat:         "float",
	tokString:        "string",
	tokText:          "text",
	tokBlockTagStart: "{%",
	tokBlockTagEnd:   "%}",
	tokVarTagStart:   "{{",
	tokVarTagEnd:     "}}",
	tokDot:           ".",
	tokFilter:        "|",
	tokArgument:      ":",
}

type lexer struct {
	src       []byte
	offset    int
	ch        int
	width     int
	insideTag bool
}

func (l *lexer) init() {
	l.ch, l.width = utf8.DecodeRune(l.src)
}

func (l *lexer) next() {
	l.offset += l.width
	if l.offset < len(l.src) {
		r, w := int(l.src[l.offset]), 1
		if r >= utf8.RuneSelf {
			r, w = utf8.DecodeRune(l.src[l.offset:])
		}
		l.width = w
		l.ch = r
	} else {
		l.width = 0
		l.ch = -1
	}
}

func (l *lexer) scan() (token, []byte) {
	if !l.insideTag && l.ch != '{' {
		lit := l.scanText()
		return tokText, lit
	}
	l.insideTag = true
	// Another divergence from Django is that we allow spaces between all tokens. So
	// {{ var . 1 }}
	// is a valid template and does what you would expect. Django doesn't allow this.
	l.consumeWhitespace()

	pos := l.offset
	tok := tokIllegal

	switch ch := l.ch; {
	case unicode.IsLetter(ch), l.ch == '_':
		tok = l.scanIdent()
	case unicode.IsDigit(ch):
		tok = l.scanNumber()
	case ch == '-', ch == '+':
		tok = l.scanNumber()
	case ch == '|':
		tok = tokFilter
		l.next()
	case ch == '.':
		tok = tokDot
		l.next()
	case ch == ':':
		tok = tokArgument
		l.next()
	default:
		l.next()
		switch ch {
		case -1:
			tok = tokEof
		case '{':
			switch l.ch {
			case '%':
				tok = tokBlockTagStart
			case '{':
				tok = tokVarTagStart
			case '#':
				// start of a comment; scan until the end
				l.next()
				for {
					for l.ch != '#' {
						l.next()
					}
					l.next()
					if l.ch == '}' {
						l.next()
						break
					}
				}
				// TODO: probably shouldn't recurse
				return l.scan()
			}
		case '%':
			if l.ch != '}' {
				goto illegal
			}
			l.insideTag = false
			tok = tokBlockTagEnd
		case '}':
			if l.ch != '}' {
				goto illegal
			}
			l.insideTag = false
			tok = tokVarTagEnd
		case '\'', '"':
			tok = l.scanString(byte(ch))
			return tok, l.src[pos+1 : l.offset-1]
		default:
		illegal:
			panic("illegal character")
		}
		l.next()
	}
	return tok, l.src[pos:l.offset]
}

func (l *lexer) consumeWhitespace() {
	for l.ch == ' ' || l.ch == '\t' || l.ch == '\n' || l.ch == '\r' {
		l.next()
	}
}

// There's a known incompatibility here for a template such as:
// {%{##}}}
// Django outputs {%}}. It has to see the opening and the closing symbols for it to be considered a tag.
// Godjan will return an error on this input. Disallowing this unlikely case makes
// lexing much easier and more efficient because we don't have to look ahead.
func (l *lexer) scanText() []byte {
	n := len(l.src) - 1
	off := l.offset
	pos := 0
consume:
	for {
		pos = bytes.IndexByte(l.src[off:], '{')
		if pos < 0 || off+pos >= n {
			break
		}
		off += pos
		switch l.src[off+1] {
		case '%', '{', '#':
			break consume
		}
		off++
	}
	var lit []byte
	if pos < 0 || pos == n {
		lit = l.src[l.offset:]
		l.offset = len(l.src)
		l.width = 0
		l.ch = -1
	} else {
		l.width = 1
		l.ch = '{'
		lit = l.src[l.offset:off]
		l.offset = off
		if l.src[l.offset] != byte(l.ch) {
			panic("Something's wrong")
		}
	}
	l.insideTag = true
	return lit
}

func (l *lexer) scanIdent() token {
	for unicode.IsLetter(l.ch) || unicode.IsDigit(l.ch) || l.ch == '_' {
		l.next()
	}
	return tokIdent
}

func (l *lexer) scanNumber() token {
	tok := tokInt
	seenDecimal := false
	seenExponent := false

	if l.ch == '-' || l.ch == '+' {
		l.next()
	}
	for l.ch >= '0' && l.ch <= '9' ||
		!seenDecimal && !seenExponent && l.ch == '.' ||
		!seenExponent && (l.ch == 'e' || l.ch == 'E') {
		if l.ch == '.' {
			seenDecimal = true
			tok = tokFloat
		}
		if l.ch == 'e' || l.ch == 'E' {
			seenExponent = true
			tok = tokFloat
		}
		l.next()
	}
	return tok
}

func (l *lexer) scanString(start byte) token {
	// ' or " already consumed
	pos := bytes.IndexByte(l.src[l.offset:], start)
	if pos < 0 {
		panic("String not terminated")
	}
	l.offset += pos
	l.width = 1
	l.next()
	return tokString
}
<|endoftext|>


<commit_before>package mango

import (
	"fmt"
	"http"
	"log"
	"net/textproto"
	"os"
)

type Request struct {
	*http.Request
}

type Status int
type Body string

type Headers http.Header

func (h Headers) Add(key, value string) {
	textproto.MIMEHeader(h).Add(key, value)
}

func (h Headers) Set(key, value string) {
	textproto.MIMEHeader(h).Set(key, value)
}

func (h Headers) Get(key string) string {
	return textproto.MIMEHeader(h).Get(key)
}

func (h Headers) Del(key string) {
	textproto.MIMEHeader(h).Del(key)
}

type Env map[string]interface{}

func (this Env) Logger() *log.Logger {
	if this["mango.logger"] == nil {
		this["mango.logger"] = log.New(os.Stdout, "", log.Ldate|log.Ltime)
	}

	return this["mango.logger"].(*log.Logger)
}

func (this Env) Request() *Request {
	return this["mango.request"].(*Request)
}

func (this Env) Session() map[string]interface{} {
	return this["mango.session"].(map[string]interface{})
}

// This is the core app the user has written
type App func(Env) (Status, Headers, Body)

// These are pieces of middleware,
// which 'wrap' around the core App
// (and each other)
type Middleware func(Env, App) (Status, Headers, Body)

// Bundle a given list of Middleware pieces into a App
func bundle(r ...Middleware) App {
	if len(r) <= 1 {
		// Terminate the innermost piece of Middleware
		// Basically stops it from recursing any further.
		return func(input Env) (Status, Headers, Body) {
			return r[0](input, func(Env) (Status, Headers, Body) {
				panic("Core Mango App should never call it's upstream function.")
			})
		}
	}
	return wrap(r[0], bundle(r[1:]...))
}

// Attach a piece of Middleware to the outside
// of a App. This wraps the inner App
// inside the outer Middleware.
func wrap(middleware Middleware, app App) App {
	return func(input Env) (Status, Headers, Body) {
		return middleware(input, app)
	}
}

// Convert a App into Middleware
// We convert the core app into a Middleware
// so we can pass it to Bundle as part of the
// stack. Because the App does not call its
// upstream method, the resulting Middleware
// will just ignore any upstream passed to it.
func middlewareify(app App) Middleware {
	return func(input Env, upstream App) (Status, Headers, Body) {
		return app(input)
	}
}

type Stack struct {
	Address    string
	middleware []Middleware
	app        App
}

func (this *Stack) Version() []int {
	return []int{0, 1}
}

func (this *Stack) Middleware(middleware ...Middleware) {
	this.middleware = middleware
}

func (this *Stack) Compile(app App) App {
	this.app = app
	return bundle(append(this.middleware, middlewareify(this.app))...)
}

func (this *Stack) HandlerFunc(app App) http.HandlerFunc {
	compiled_app := this.Compile(app)
	return func(w http.ResponseWriter, r *http.Request) {
		env := make(map[string]interface{})
		env["mango.request"] = &Request{r}
		env["mango.version"] = this.Version()

		status, headers, body := compiled_app(env)

		for key, values := range headers {
			for _, value := range values {
				w.Header().Add(key, value)
			}
		}
		w.WriteHeader(int(status))
		fmt.Fprintf(w, string(body))
	}
}

func (this *Stack) Run(app App) os.Error {
	if this.Address == "" {
		this.Address = "0.0.0.0:8000"
	}
	fmt.Println("Starting Mango Stack On:", this.Address)
	http.HandleFunc("/", this.HandlerFunc(app))
	return http.ListenAndServe(this.Address, nil)
}
<commit_msg>Bumped version to 0.2<commit_after>package mango

import (
	"fmt"
	"http"
	"log"
	"net/textproto"
	"os"
)

type Request struct {
	*http.Request
}

type Status int
type Body string

type Headers http.Header

func (h Headers) Add(key, value string) {
	textproto.MIMEHeader(h).Add(key, value)
}

func (h Headers) Set(key, value string) {
	textproto.MIMEHeader(h).Set(key, value)
}

func (h Headers) Get(key string) string {
	return textproto.MIMEHeader(h).Get(key)
}

func (h Headers) Del(key string) {
	textproto.MIMEHeader(h).Del(key)
}

type Env map[string]interface{}

func (this Env) Logger() *log.Logger {
	if this["mango.logger"] == nil {
		this["mango.logger"] = log.New(os.Stdout, "", log.Ldate|log.Ltime)
	}

	return this["mango.logger"].(*log.Logger)
}

func (this Env) Request() *Request {
	return this["mango.request"].(*Request)
}

func (this Env) Session() map[string]interface{} {
	return this["mango.session"].(map[string]interface{})
}

// This is the core app the user has written
type App func(Env) (Status, Headers, Body)

// These are pieces of middleware,
// which 'wrap' around the core App
// (and each other)
type Middleware func(Env, App) (Status, Headers, Body)

// Bundle a given list of Middleware pieces into a App
func bundle(r ...Middleware) App {
	if len(r) <= 1 {
		// Terminate the innermost piece of Middleware
		// Basically stops it from recursing any further.
		return func(input Env) (Status, Headers, Body) {
			return r[0](input, func(Env) (Status, Headers, Body) {
				panic("Core Mango App should never call it's upstream function.")
			})
		}
	}
	return wrap(r[0], bundle(r[1:]...))
}

// Attach a piece of Middleware to the outside
// of a App. This wraps the inner App
// inside the outer Middleware.
func wrap(middleware Middleware, app App) App {
	return func(input Env) (Status, Headers, Body) {
		return middleware(input, app)
	}
}

// Convert a App into Middleware
// We convert the core app into a Middleware
// so we can pass it to Bundle as part of the
// stack. Because the App does not call its
// upstream method, the resulting Middleware
// will just ignore any upstream passed to it.
func middlewareify(app App) Middleware {
	return func(input Env, upstream App) (Status, Headers, Body) {
		return app(input)
	}
}

type Stack struct {
	Address    string
	middleware []Middleware
	app        App
}

func (this *Stack) Version() []int {
	return []int{0, 2}
}

func (this *Stack) Middleware(middleware ...Middleware) {
	this.middleware = middleware
}

func (this *Stack) Compile(app App) App {
	this.app = app
	return bundle(append(this.middleware, middlewareify(this.app))...)
}

func (this *Stack) HandlerFunc(app App) http.HandlerFunc {
	compiled_app := this.Compile(app)
	return func(w http.ResponseWriter, r *http.Request) {
		env := make(map[string]interface{})
		env["mango.request"] = &Request{r}
		env["mango.version"] = this.Version()

		status, headers, body := compiled_app(env)

		for key, values := range headers {
			for _, value := range values {
				w.Header().Add(key, value)
			}
		}
		w.WriteHeader(int(status))
		fmt.Fprintf(w, string(body))
	}
}

func (this *Stack) Run(app App) os.Error {
	if this.Address == "" {
		this.Address = "0.0.0.0:8000"
	}
	fmt.Println("Starting Mango Stack On:", this.Address)
	http.HandleFunc("/", this.HandlerFunc(app))
	return http.ListenAndServe(this.Address, nil)
}
<|endoftext|>


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

// Based on src/pkg/reflect/deepequal.go from official
// golang's stdlib.

package mergo

import (
	"fmt"
	"reflect"
)

func hasExportedField(dst reflect.Value) (exported bool) {
	for i, n := 0, dst.NumField(); i < n; i++ {
		field := dst.Type().Field(i)
		if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
			exported = exported || hasExportedField(dst.Field(i))
		} else {
			exported = exported || len(field.PkgPath) == 0
		}
	}
	return
}

type Config struct {
	Overwrite                    bool
	AppendSlice                  bool
	TypeCheck                    bool
	Transformers                 Transformers
	overwriteWithEmptyValue      bool
	OverwriteSliceWithEmptyValue bool
}

type Transformers interface {
	Transformer(reflect.Type) func(dst, src reflect.Value) error
}

// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
	overwrite := config.Overwrite
	typeCheck := config.TypeCheck
	overwriteWithEmptySrc := config.overwriteWithEmptyValue
	overwriteSliceWithEmptySrc := config.OverwriteSliceWithEmptyValue
	config.overwriteWithEmptyValue = false

	if !src.IsValid() {
		return
	}
	if dst.CanAddr() {
		addr := dst.UnsafeAddr()
		h := 17 * addr
		seen := visited[h]
		typ := dst.Type()
		for p := seen; p != nil; p = p.next {
			if p.ptr == addr && p.typ == typ {
				return nil
			}
		}
		// Remember, remember...
		visited[h] = &visit{addr, typ, seen}
	}

	if config.Transformers != nil && !isEmptyValue(dst) {
		if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
			err = fn(dst, src)
			return
		}
	}

	switch dst.Kind() {
	case reflect.Struct:
		if hasExportedField(dst) {
			for i, n := 0, dst.NumField(); i < n; i++ {
				if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
					return
				}
			}
		} else {
			if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) {
				dst.Set(src)
			}
		}
	case reflect.Map:
		if dst.IsNil() && !src.IsNil() {
			dst.Set(reflect.MakeMap(dst.Type()))
		}
		for _, key := range src.MapKeys() {
			srcElement := src.MapIndex(key)
			if !srcElement.IsValid() {
				continue
			}
			dstElement := dst.MapIndex(key)
			switch srcElement.Kind() {
			case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
				if srcElement.IsNil() {
					continue
				}
				fallthrough
			default:
				if !srcElement.CanInterface() {
					continue
				}
				switch reflect.TypeOf(srcElement.Interface()).Kind() {
				case reflect.Struct:
					fallthrough
				case reflect.Ptr:
					fallthrough
				case reflect.Map:
					srcMapElm := srcElement
					dstMapElm := dstElement
					if srcMapElm.CanInterface() {
						srcMapElm = reflect.ValueOf(srcMapElm.Interface())
						if dstMapElm.IsValid() {
							dstMapElm = reflect.ValueOf(dstMapElm.Interface())
						}
					}
					if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil {
						return
					}
				case reflect.Slice:
					srcSlice := reflect.ValueOf(srcElement.Interface())

					var dstSlice reflect.Value
					if !dstElement.IsValid() || dstElement.IsNil() {
						dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
					} else {
						dstSlice = reflect.ValueOf(dstElement.Interface())
					}

					if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
						if typeCheck && srcSlice.Type() != dstSlice.Type() {
							return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
						}
						dstSlice = srcSlice
					} else if config.AppendSlice {
						if srcSlice.Type() != dstSlice.Type() {
							return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
						}
						dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
					}
					dst.SetMapIndex(key, dstSlice)
				}
			}
			if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) {
				continue
			}

			if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement)) {
				if dst.IsNil() {
					dst.Set(reflect.MakeMap(dst.Type()))
				}
				dst.SetMapIndex(key, srcElement)
			}
		}
	case reflect.Slice:
		if !dst.CanSet() {
			break
		}
		if (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
			dst.Set(src)
		} else if config.AppendSlice {
			if src.Type() != dst.Type() {
				return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
			}
			dst.Set(reflect.AppendSlice(dst, src))
		}
	case reflect.Ptr:
		fallthrough
	case reflect.Interface:
		if src.IsNil() {
			break
		}

		if dst.Kind() != reflect.Ptr && src.Type().AssignableTo(dst.Type()) {
			if dst.IsNil() || overwrite {
				if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
					dst.Set(src)
				}
			}
			break
		}

		if src.Kind() != reflect.Interface {
			if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
				if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
					dst.Set(src)
				}
			} else if src.Kind() == reflect.Ptr {
				if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
					return
				}
			} else if dst.Elem().Type() == src.Type() {
				if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
					return
				}
			} else {
				return ErrDifferentArgumentsTypes
			}
			break
		}
		if dst.IsNil() || overwrite {
			if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
				dst.Set(src)
			}
		} else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
			return
		}
	default:
		if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) {
			dst.Set(src)
		}
	}

	return
}

// Merge will fill any empty for value type attributes on the dst struct using corresponding
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
// and dst must be a pointer to struct.
// It won't merge unexported (private) fields and will do recursively any exported field.
func Merge(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, opts...)
}

// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
// non-empty src attribute values.
// Deprecated: use Merge(…) with WithOverride
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, append(opts, WithOverride)...)
}

// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
func WithTransformers(transformers Transformers) func(*Config) {
	return func(config *Config) {
		config.Transformers = transformers
	}
}

// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
func WithOverride(config *Config) {
	config.Overwrite = true
}

// WithOverride will make merge override empty dst slice with empty src slice.
func WithOverrideEmptySlice(config *Config) {
	config.OverwriteSliceWithEmptyValue = true
}

// WithAppendSlice will make merge append slices instead of overwriting it.
func WithAppendSlice(config *Config) {
	config.AppendSlice = true
}

// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
func WithTypeCheck(config *Config) {
	config.TypeCheck = true
}

func merge(dst, src interface{}, opts ...func(*Config)) error {
	var (
		vDst, vSrc reflect.Value
		err        error
	)

	config := &Config{}

	for _, opt := range opts {
		opt(config)
	}

	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
		return err
	}
	if vDst.Type() != vSrc.Type() {
		return ErrDifferentArgumentsTypes
	}
	return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}
<commit_msg>added WithOverrideEmptySlice config flag<commit_after>// Copyright 2013 Dario Castañé. All rights reserved.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Based on src/pkg/reflect/deepequal.go from official
// golang's stdlib.

package mergo

import (
	"fmt"
	"reflect"
)

func hasExportedField(dst reflect.Value) (exported bool) {
	for i, n := 0, dst.NumField(); i < n; i++ {
		field := dst.Type().Field(i)
		if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
			exported = exported || hasExportedField(dst.Field(i))
		} else {
			exported = exported || len(field.PkgPath) == 0
		}
	}
	return
}

type Config struct {
	Overwrite                    bool
	AppendSlice                  bool
	TypeCheck                    bool
	Transformers                 Transformers
	overwriteWithEmptyValue      bool
	OverwriteSliceWithEmptyValue bool
}

type Transformers interface {
	Transformer(reflect.Type) func(dst, src reflect.Value) error
}

// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
	overwrite := config.Overwrite
	typeCheck := config.TypeCheck
	overwriteWithEmptySrc := config.overwriteWithEmptyValue
	overwriteSliceWithEmptySrc := config.OverwriteSliceWithEmptyValue
	config.overwriteWithEmptyValue = false

	if !src.IsValid() {
		return
	}
	if dst.CanAddr() {
		addr := dst.UnsafeAddr()
		h := 17 * addr
		seen := visited[h]
		typ := dst.Type()
		for p := seen; p != nil; p = p.next {
			if p.ptr == addr && p.typ == typ {
				return nil
			}
		}
		// Remember, remember...
		visited[h] = &visit{addr, typ, seen}
	}

	if config.Transformers != nil && !isEmptyValue(dst) {
		if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
			err = fn(dst, src)
			return
		}
	}

	switch dst.Kind() {
	case reflect.Struct:
		if hasExportedField(dst) {
			for i, n := 0, dst.NumField(); i < n; i++ {
				if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
					return
				}
			}
		} else {
			if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) {
				dst.Set(src)
			}
		}
	case reflect.Map:
		if dst.IsNil() && !src.IsNil() {
			dst.Set(reflect.MakeMap(dst.Type()))
		}
		for _, key := range src.MapKeys() {
			srcElement := src.MapIndex(key)
			if !srcElement.IsValid() {
				continue
			}
			dstElement := dst.MapIndex(key)
			switch srcElement.Kind() {
			case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
				if srcElement.IsNil() {
					continue
				}
				fallthrough
			default:
				if !srcElement.CanInterface() {
					continue
				}
				switch reflect.TypeOf(srcElement.Interface()).Kind() {
				case reflect.Struct:
					fallthrough
				case reflect.Ptr:
					fallthrough
				case reflect.Map:
					srcMapElm := srcElement
					dstMapElm := dstElement
					if srcMapElm.CanInterface() {
						srcMapElm = reflect.ValueOf(srcMapElm.Interface())
						if dstMapElm.IsValid() {
							dstMapElm = reflect.ValueOf(dstMapElm.Interface())
						}
					}
					if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil {
						return
					}
				case reflect.Slice:
					srcSlice := reflect.ValueOf(srcElement.Interface())

					var dstSlice reflect.Value
					if !dstElement.IsValid() || dstElement.IsNil() {
						dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
					} else {
						dstSlice = reflect.ValueOf(dstElement.Interface())
					}

					if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
						if typeCheck && srcSlice.Type() != dstSlice.Type() {
							return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
						}
						dstSlice = srcSlice
					} else if config.AppendSlice {
						if srcSlice.Type() != dstSlice.Type() {
							return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
						}
						dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
					}
					dst.SetMapIndex(key, dstSlice)
				}
			}
			if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) {
				continue
			}

			if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement)) {
				if dst.IsNil() {
					dst.Set(reflect.MakeMap(dst.Type()))
				}
				dst.SetMapIndex(key, srcElement)
			}
		}
	case reflect.Slice:
		if !dst.CanSet() {
			break
		}
		if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
			dst.Set(src)
		} else if config.AppendSlice {
			if src.Type() != dst.Type() {
				return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
			}
			dst.Set(reflect.AppendSlice(dst, src))
		}
	case reflect.Ptr:
		fallthrough
	case reflect.Interface:
		if src.IsNil() {
			break
		}

		if dst.Kind() != reflect.Ptr && src.Type().AssignableTo(dst.Type()) {
			if dst.IsNil() || overwrite {
				if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
					dst.Set(src)
				}
			}
			break
		}

		if src.Kind() != reflect.Interface {
			if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
				if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
					dst.Set(src)
				}
			} else if src.Kind() == reflect.Ptr {
				if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
					return
				}
			} else if dst.Elem().Type() == src.Type() {
				if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
					return
				}
			} else {
				return ErrDifferentArgumentsTypes
			}
			break
		}
		if dst.IsNil() || overwrite {
			if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
				dst.Set(src)
			}
		} else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
			return
		}
	default:
		if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) {
			dst.Set(src)
		}
	}

	return
}

// Merge will fill any empty for value type attributes on the dst struct using corresponding
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
// and dst must be a pointer to struct.
// It won't merge unexported (private) fields and will do recursively any exported field.
func Merge(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, opts...)
}

// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
// non-empty src attribute values.
// Deprecated: use Merge(…) with WithOverride
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, append(opts, WithOverride)...)
}

// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
func WithTransformers(transformers Transformers) func(*Config) {
	return func(config *Config) {
		config.Transformers = transformers
	}
}

// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
func WithOverride(config *Config) {
	config.Overwrite = true
}

// WithOverride will make merge override empty dst slice with empty src slice.
func WithOverrideEmptySlice(config *Config) {
	config.OverwriteSliceWithEmptyValue = true
}

// WithAppendSlice will make merge append slices instead of overwriting it.
func WithAppendSlice(config *Config) {
	config.AppendSlice = true
}

// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
func WithTypeCheck(config *Config) {
	config.TypeCheck = true
}

func merge(dst, src interface{}, opts ...func(*Config)) error {
	var (
		vDst, vSrc reflect.Value
		err        error
	)

	config := &Config{}

	for _, opt := range opts {
		opt(config)
	}

	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
		return err
	}
	if vDst.Type() != vSrc.Type() {
		return ErrDifferentArgumentsTypes
	}
	return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}
<|endoftext|>


<commit_before>// Copyright Istio 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 cmd

import (
	"context"
	"fmt"
	"io"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"

	"github.com/spf13/cobra"
	admit_v1 "k8s.io/api/admissionregistration/v1"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	api_pkg_labels "k8s.io/apimachinery/pkg/labels"

	"istio.io/api/annotation"
	"istio.io/api/label"
	"istio.io/istio/istioctl/pkg/clioptions"
	"istio.io/istio/pkg/config/analysis/analyzers/injection"
	analyzer_util "istio.io/istio/pkg/config/analysis/analyzers/util"
	"istio.io/istio/pkg/config/resource"
	"istio.io/istio/pkg/kube"
)

type revisionCount struct {
	// pods in a revision
	pods int
	// pods that are disabled from injection
	disabled int
	// pods that are enabled for injection, but whose revision doesn't match their namespace's revision
	needsRestart int
}

func injectorCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:     "injector",
		Short:   "List sidecar injector and sidecar versions",
		Long:    `List sidecar injector and sidecar versions`,
		Example: `  istioctl experimental injector list`,
		Args: func(cmd *cobra.Command, args []string) error {
			if len(args) != 0 {
				return fmt.Errorf("unknown subcommand %q", args[0])
			}
			return nil
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			cmd.HelpFunc()(cmd, args)
			return nil
		},
	}

	cmd.AddCommand(injectorListCommand())
	return cmd
}

func injectorListCommand() *cobra.Command {
	var opts clioptions.ControlPlaneOptions
	cmd := &cobra.Command{
		Use:     "list",
		Short:   "List sidecar injector and sidecar versions",
		Long:    `List sidecar injector and sidecar versions`,
		Example: `  istioctl experimental injector list`,
		RunE: func(cmd *cobra.Command, args []string) error {
			client, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision)
			if err != nil {
				return fmt.Errorf("failed to create k8s client: %v", err)
			}
			ctx := context.Background()

			nslist, err := getNamespaces(ctx, client)
			if err != nil {
				return err
			}
			sort.Slice(nslist, func(i, j int) bool {
				return nslist[i].Name < nslist[j].Name
			})

			hooks, err := getWebhooks(ctx, client)
			if err != nil {
				return err
			}
			pods, err := getPods(ctx, client)
			if err != nil {
				return err
			}
			err = printNS(cmd.OutOrStdout(), nslist, hooks, pods)
			if err != nil {
				return err
			}
			cmd.Println()
			injectedImages, err := getInjectedImages(ctx, client)
			if err != nil {
				return err
			}

			sort.Slice(hooks, func(i, j int) bool {
				return hooks[i].Name < hooks[j].Name
			})
			return printHooks(cmd.OutOrStdout(), nslist, hooks, injectedImages)
		},
	}

	return cmd
}

func getNamespaces(ctx context.Context, client kube.ExtendedClient) ([]v1.Namespace, error) {
	nslist, err := client.Kube().CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
	if err != nil {
		return []v1.Namespace{}, err
	}
	return nslist.Items, nil
}

func printNS(writer io.Writer, namespaces []v1.Namespace, hooks []admit_v1.MutatingWebhookConfiguration,
	allPods map[resource.Namespace][]v1.Pod,
) error {
	outputCount := 0

	w := new(tabwriter.Writer).Init(writer, 0, 8, 1, ' ', 0)
	for _, namespace := range namespaces {
		if hideFromOutput(resource.Namespace(namespace.Name)) {
			continue
		}

		revision := getInjectedRevision(&namespace, hooks)
		podCount := podCountByRevision(allPods[resource.Namespace(namespace.Name)], revision)
		if len(podCount) == 0 {
			// This namespace has no pods, but we wish to display it if new pods will be auto-injected
			if revision != "" {
				podCount[revision] = revisionCount{}
			}
		}
		for injectedRevision, count := range podCount {
			if outputCount == 0 {
				fmt.Fprintln(w, "NAMESPACE\tISTIO-REVISION\tPOD-REVISIONS")
			}
			outputCount++

			fmt.Fprintf(w, "%s\t%s\t%s\n", namespace.Name, revision, renderCounts(injectedRevision, count))
		}
	}
	if outputCount == 0 {
		fmt.Fprintf(writer, "No Istio injected namespaces present.\n")
	}

	return w.Flush()
}

func getWebhooks(ctx context.Context, client kube.ExtendedClient) ([]admit_v1.MutatingWebhookConfiguration, error) {
	hooks, err := client.Kube().AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{})
	if err != nil {
		return []admit_v1.MutatingWebhookConfiguration{}, err
	}
	return hooks.Items, nil
}

func printHooks(writer io.Writer, namespaces []v1.Namespace, hooks []admit_v1.MutatingWebhookConfiguration, injectedImages map[string]string) error {
	if len(hooks) == 0 {
		fmt.Fprintf(writer, "No Istio injection hooks present.\n")
		return nil
	}

	w := new(tabwriter.Writer).Init(writer, 0, 8, 1, ' ', 0)
	fmt.Fprintln(w, "NAMESPACES\tINJECTOR-HOOK\tISTIO-REVISION\tSIDECAR-IMAGE")
	for _, hook := range hooks {
		revision := hook.ObjectMeta.GetLabels()[label.IoIstioRev.Name]
		namespaces := getMatchingNamespaces(&hook, namespaces)
		if len(namespaces) == 0 {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "DOES NOT AUTOINJECT", hook.Name, revision, injectedImages[revision])
			continue
		}
		for _, namespace := range namespaces {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", namespace.Name, hook.Name, revision, injectedImages[revision])
		}
	}
	return w.Flush()
}

func getInjector(namespace *v1.Namespace, hooks []admit_v1.MutatingWebhookConfiguration) *admit_v1.MutatingWebhookConfiguration {
	// find matching hook
	for _, hook := range hooks {
		for _, webhook := range hook.Webhooks {
			nsSelector, err := metav1.LabelSelectorAsSelector(webhook.NamespaceSelector)
			if err != nil {
				continue
			}
			if nsSelector.Matches(api_pkg_labels.Set(namespace.ObjectMeta.Labels)) {
				return &hook
			}
		}
	}
	return nil
}

func getInjectedRevision(namespace *v1.Namespace, hooks []admit_v1.MutatingWebhookConfiguration) string {
	injector := getInjector(namespace, hooks)
	if injector != nil {
		return injector.ObjectMeta.GetLabels()[label.IoIstioRev.Name]
	}
	newRev := namespace.ObjectMeta.GetLabels()[label.IoIstioRev.Name]
	oldLabel, ok := namespace.ObjectMeta.GetLabels()[analyzer_util.InjectionLabelName]
	// If there is no istio-injection=disabled and no istio.io/rev, the namespace isn't injected
	if newRev == "" && (ok && oldLabel == "disabled" || !ok) {
		return ""
	}
	if newRev != "" {
		return fmt.Sprintf("MISSING/%s", newRev)
	}
	return fmt.Sprintf("MISSING/%s", analyzer_util.InjectionLabelName)
}

func getMatchingNamespaces(hook *admit_v1.MutatingWebhookConfiguration, namespaces []v1.Namespace) []v1.Namespace {
	retval := make([]v1.Namespace, 0)
	for _, webhook := range hook.Webhooks {
		nsSelector, err := metav1.LabelSelectorAsSelector(webhook.NamespaceSelector)
		if err != nil {
			return retval
		}

		for _, namespace := range namespaces {
			if nsSelector.Matches(api_pkg_labels.Set(namespace.Labels)) {
				retval = append(retval, namespace)
			}
		}
	}
	return retval
}

func getPods(ctx context.Context, client kube.ExtendedClient) (map[resource.Namespace][]v1.Pod, error) {
	retval := map[resource.Namespace][]v1.Pod{}
	// All pods in all namespaces
	pods, err := client.Kube().CoreV1().Pods("").List(ctx, metav1.ListOptions{})
	if err != nil {
		return retval, err
	}
	for _, pod := range pods.Items {
		podList, ok := retval[resource.Namespace(pod.GetNamespace())]
		if !ok {
			retval[resource.Namespace(pod.GetNamespace())] = []v1.Pod{pod}
		} else {
			retval[resource.Namespace(pod.GetNamespace())] = append(podList, pod)
		}
	}
	return retval, nil
}

// getInjectedImages() returns a map of revision->dockerimage
func getInjectedImages(ctx context.Context, client kube.ExtendedClient) (map[string]string, error) {
	retval := map[string]string{}

	// All configs in all namespaces that are Istio revisioned
	configMaps, err := client.Kube().CoreV1().ConfigMaps("").List(ctx, metav1.ListOptions{LabelSelector: label.IoIstioRev.Name})
	if err != nil {
		return retval, err
	}

	for _, configMap := range configMaps.Items {
		image := injection.GetIstioProxyImage(&configMap)
		if image != "" {
			retval[configMap.ObjectMeta.GetLabels()[label.IoIstioRev.Name]] = image
		}
	}

	return retval, nil
}

// podCountByRevision() returns a map of revision->pods, with "<non-Istio>" as the dummy "revision" for uninjected pods
func podCountByRevision(pods []v1.Pod, expectedRevision string) map[string]revisionCount {
	retval := map[string]revisionCount{}
	for _, pod := range pods {
		revision := pod.ObjectMeta.GetLabels()[label.IoIstioRev.Name]
		revisionLabel := revision
		if revision == "" {
			revisionLabel = "<non-Istio>"
		}
		counts := retval[revisionLabel]
		counts.pods++
		if injectionDisabled(&pod) {
			counts.disabled++
		} else if revision != expectedRevision {
			counts.needsRestart++
		}
		retval[revisionLabel] = counts
	}
	return retval
}

func hideFromOutput(ns resource.Namespace) bool {
	return (analyzer_util.IsSystemNamespace(ns) || ns == resource.Namespace(istioNamespace))
}

func injectionDisabled(pod *v1.Pod) bool {
	inject := pod.ObjectMeta.GetAnnotations()[annotation.SidecarInject.Name]
	return strings.EqualFold(inject, "false")
}

func renderCounts(injectedRevision string, counts revisionCount) string {
	if counts.pods == 0 {
		return "<no pods>"
	}

	podText := strconv.Itoa(counts.pods)
	if counts.disabled > 0 {
		podText += fmt.Sprintf(" (injection disabled: %d)", counts.disabled)
	}
	if counts.needsRestart > 0 {
		podText += fmt.Sprintf(" NEEDS RESTART: %d", counts.needsRestart)
	}
	return fmt.Sprintf("%s: %s", injectedRevision, podText)
}
<commit_msg>fix injection check in x injector list (#39523)<commit_after>// Copyright Istio 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 cmd

import (
	"context"
	"fmt"
	"io"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"

	"github.com/spf13/cobra"
	admit_v1 "k8s.io/api/admissionregistration/v1"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	api_pkg_labels "k8s.io/apimachinery/pkg/labels"

	"istio.io/api/annotation"
	"istio.io/api/label"
	"istio.io/istio/istioctl/pkg/clioptions"
	"istio.io/istio/pkg/config/analysis/analyzers/injection"
	analyzer_util "istio.io/istio/pkg/config/analysis/analyzers/util"
	"istio.io/istio/pkg/config/resource"
	"istio.io/istio/pkg/kube"
)

type revisionCount struct {
	// pods in a revision
	pods int
	// pods that are disabled from injection
	disabled int
	// pods that are enabled for injection, but whose revision doesn't match their namespace's revision
	needsRestart int
}

func injectorCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:     "injector",
		Short:   "List sidecar injector and sidecar versions",
		Long:    `List sidecar injector and sidecar versions`,
		Example: `  istioctl experimental injector list`,
		Args: func(cmd *cobra.Command, args []string) error {
			if len(args) != 0 {
				return fmt.Errorf("unknown subcommand %q", args[0])
			}
			return nil
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			cmd.HelpFunc()(cmd, args)
			return nil
		},
	}

	cmd.AddCommand(injectorListCommand())
	return cmd
}

func injectorListCommand() *cobra.Command {
	var opts clioptions.ControlPlaneOptions
	cmd := &cobra.Command{
		Use:     "list",
		Short:   "List sidecar injector and sidecar versions",
		Long:    `List sidecar injector and sidecar versions`,
		Example: `  istioctl experimental injector list`,
		RunE: func(cmd *cobra.Command, args []string) error {
			client, err := kubeClientWithRevision(kubeconfig, configContext, opts.Revision)
			if err != nil {
				return fmt.Errorf("failed to create k8s client: %v", err)
			}
			ctx := context.Background()

			nslist, err := getNamespaces(ctx, client)
			if err != nil {
				return err
			}
			sort.Slice(nslist, func(i, j int) bool {
				return nslist[i].Name < nslist[j].Name
			})

			hooks, err := getWebhooks(ctx, client)
			if err != nil {
				return err
			}
			pods, err := getPods(ctx, client)
			if err != nil {
				return err
			}
			err = printNS(cmd.OutOrStdout(), nslist, hooks, pods)
			if err != nil {
				return err
			}
			cmd.Println()
			injectedImages, err := getInjectedImages(ctx, client)
			if err != nil {
				return err
			}

			sort.Slice(hooks, func(i, j int) bool {
				return hooks[i].Name < hooks[j].Name
			})
			return printHooks(cmd.OutOrStdout(), nslist, hooks, injectedImages)
		},
	}

	return cmd
}

func getNamespaces(ctx context.Context, client kube.ExtendedClient) ([]v1.Namespace, error) {
	nslist, err := client.Kube().CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
	if err != nil {
		return []v1.Namespace{}, err
	}
	return nslist.Items, nil
}

func printNS(writer io.Writer, namespaces []v1.Namespace, hooks []admit_v1.MutatingWebhookConfiguration,
	allPods map[resource.Namespace][]v1.Pod,
) error {
	outputCount := 0

	w := new(tabwriter.Writer).Init(writer, 0, 8, 1, ' ', 0)
	for _, namespace := range namespaces {
		if hideFromOutput(resource.Namespace(namespace.Name)) {
			continue
		}

		revision := getInjectedRevision(&namespace, hooks)
		podCount := podCountByRevision(allPods[resource.Namespace(namespace.Name)], revision)
		if len(podCount) == 0 {
			// This namespace has no pods, but we wish to display it if new pods will be auto-injected
			if revision != "" {
				podCount[revision] = revisionCount{}
			}
		}
		for injectedRevision, count := range podCount {
			if outputCount == 0 {
				fmt.Fprintln(w, "NAMESPACE\tISTIO-REVISION\tPOD-REVISIONS")
			}
			outputCount++

			fmt.Fprintf(w, "%s\t%s\t%s\n", namespace.Name, revision, renderCounts(injectedRevision, count))
		}
	}
	if outputCount == 0 {
		fmt.Fprintf(writer, "No Istio injected namespaces present.\n")
	}

	return w.Flush()
}

func getWebhooks(ctx context.Context, client kube.ExtendedClient) ([]admit_v1.MutatingWebhookConfiguration, error) {
	hooks, err := client.Kube().AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{})
	if err != nil {
		return []admit_v1.MutatingWebhookConfiguration{}, err
	}
	return hooks.Items, nil
}

func printHooks(writer io.Writer, namespaces []v1.Namespace, hooks []admit_v1.MutatingWebhookConfiguration, injectedImages map[string]string) error {
	if len(hooks) == 0 {
		fmt.Fprintf(writer, "No Istio injection hooks present.\n")
		return nil
	}

	w := new(tabwriter.Writer).Init(writer, 0, 8, 1, ' ', 0)
	fmt.Fprintln(w, "NAMESPACES\tINJECTOR-HOOK\tISTIO-REVISION\tSIDECAR-IMAGE")
	for _, hook := range hooks {
		revision := hook.ObjectMeta.GetLabels()[label.IoIstioRev.Name]
		namespaces := getMatchingNamespaces(&hook, namespaces)
		if len(namespaces) == 0 {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "DOES NOT AUTOINJECT", hook.Name, revision, injectedImages[revision])
			continue
		}
		for _, namespace := range namespaces {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", namespace.Name, hook.Name, revision, injectedImages[revision])
		}
	}
	return w.Flush()
}

func getInjector(namespace *v1.Namespace, hooks []admit_v1.MutatingWebhookConfiguration) *admit_v1.MutatingWebhookConfiguration {
	// find matching hook
	for _, hook := range hooks {
		for _, webhook := range hook.Webhooks {
			nsSelector, err := metav1.LabelSelectorAsSelector(webhook.NamespaceSelector)
			if err != nil {
				continue
			}
			if nsSelector.Matches(api_pkg_labels.Set(namespace.ObjectMeta.Labels)) {
				return &hook
			}
		}
	}
	return nil
}

func getInjectedRevision(namespace *v1.Namespace, hooks []admit_v1.MutatingWebhookConfiguration) string {
	injector := getInjector(namespace, hooks)
	if injector != nil {
		return injector.ObjectMeta.GetLabels()[label.IoIstioRev.Name]
	}
	newRev := namespace.ObjectMeta.GetLabels()[label.IoIstioRev.Name]
	oldLabel, ok := namespace.ObjectMeta.GetLabels()[analyzer_util.InjectionLabelName]
	// If there is no istio-injection=disabled and no istio.io/rev, the namespace isn't injected
	if newRev == "" && (ok && oldLabel == "disabled" || !ok) {
		return ""
	}
	if newRev != "" {
		return fmt.Sprintf("MISSING/%s", newRev)
	}
	return fmt.Sprintf("MISSING/%s", analyzer_util.InjectionLabelName)
}

func getMatchingNamespaces(hook *admit_v1.MutatingWebhookConfiguration, namespaces []v1.Namespace) []v1.Namespace {
	retval := make([]v1.Namespace, 0)
	for _, webhook := range hook.Webhooks {
		nsSelector, err := metav1.LabelSelectorAsSelector(webhook.NamespaceSelector)
		if err != nil {
			return retval
		}

		for _, namespace := range namespaces {
			if nsSelector.Matches(api_pkg_labels.Set(namespace.Labels)) {
				retval = append(retval, namespace)
			}
		}
	}
	return retval
}

func getPods(ctx context.Context, client kube.ExtendedClient) (map[resource.Namespace][]v1.Pod, error) {
	retval := map[resource.Namespace][]v1.Pod{}
	// All pods in all namespaces
	pods, err := client.Kube().CoreV1().Pods("").List(ctx, metav1.ListOptions{})
	if err != nil {
		return retval, err
	}
	for _, pod := range pods.Items {
		podList, ok := retval[resource.Namespace(pod.GetNamespace())]
		if !ok {
			retval[resource.Namespace(pod.GetNamespace())] = []v1.Pod{pod}
		} else {
			retval[resource.Namespace(pod.GetNamespace())] = append(podList, pod)
		}
	}
	return retval, nil
}

// getInjectedImages() returns a map of revision->dockerimage
func getInjectedImages(ctx context.Context, client kube.ExtendedClient) (map[string]string, error) {
	retval := map[string]string{}

	// All configs in all namespaces that are Istio revisioned
	configMaps, err := client.Kube().CoreV1().ConfigMaps("").List(ctx, metav1.ListOptions{LabelSelector: label.IoIstioRev.Name})
	if err != nil {
		return retval, err
	}

	for _, configMap := range configMaps.Items {
		image := injection.GetIstioProxyImage(&configMap)
		if image != "" {
			retval[configMap.ObjectMeta.GetLabels()[label.IoIstioRev.Name]] = image
		}
	}

	return retval, nil
}

// podCountByRevision() returns a map of revision->pods, with "<non-Istio>" as the dummy "revision" for uninjected pods
func podCountByRevision(pods []v1.Pod, expectedRevision string) map[string]revisionCount {
	retval := map[string]revisionCount{}
	for _, pod := range pods {
		revision := pod.ObjectMeta.GetLabels()[label.IoIstioRev.Name]
		revisionLabel := revision
		if revision == "" {
			revisionLabel = "<non-Istio>"
		}
		counts := retval[revisionLabel]
		counts.pods++
		if injectionDisabled(&pod) {
			counts.disabled++
		} else if revision != expectedRevision {
			counts.needsRestart++
		}
		retval[revisionLabel] = counts
	}
	return retval
}

func hideFromOutput(ns resource.Namespace) bool {
	return (analyzer_util.IsSystemNamespace(ns) || ns == resource.Namespace(istioNamespace))
}

func injectionDisabled(pod *v1.Pod) bool {
	inject := pod.ObjectMeta.GetAnnotations()[annotation.SidecarInject.Name]
	if lbl, labelPresent := pod.ObjectMeta.GetLabels()[annotation.SidecarInject.Name]; labelPresent {
		inject = lbl
	}
	return strings.EqualFold(inject, "false")
}

func renderCounts(injectedRevision string, counts revisionCount) string {
	if counts.pods == 0 {
		return "<no pods>"
	}

	podText := strconv.Itoa(counts.pods)
	if counts.disabled > 0 {
		podText += fmt.Sprintf(" (injection disabled: %d)", counts.disabled)
	}
	if counts.needsRestart > 0 {
		podText += fmt.Sprintf(" NEEDS RESTART: %d", counts.needsRestart)
	}
	return fmt.Sprintf("%s: %s", injectedRevision, podText)
}
<|endoftext|>