<commit_before>//
// SPDX-License-Identifier: BSD-3-Clause
//

package swordfish

import (
	"encoding/json"
	"strings"
	"testing"

	"github.com/stmcginnis/gofish/common"
)

var spareResourceSetBody = `{
		"@odata.context": "/redfish/v1/$metadata#SpareResourceSet.SpareResourceSet",
		"@odata.type": "#SpareResourceSet.v1_0_0.SpareResourceSet",
		"@odata.id": "/redfish/v1/SpareResourceSet",
		"Id": "SpareResourceSet-1",
		"Name": "SpareResourceSetOne",
		"Description": "SpareResourceSet One",
		"Links": {
			"OnHandSpares": [{
				"@odata.id": "/redfish/v1/SpareType/1"
			}],
			"OnHandSpares@odata.count": 1,
			"ReplacementSpareSets": [],
			"ReplacementSpareSets@odata.count": 0
		},
		"OnHandLocation": {
			"AltitudeMeters": 150,
			"Contacts": [{
					"ContactName": "Fred",
					"EmailAddress": "fred@example.com",
					"PhoneNumber": "+44 303 123 7300"
				},
				{
					"ContactName": "George",
					"EmailAddress": "george@example.com",
					"PhoneNumber": "+44 20 7405 7324"
				}
			],
			"Info": "Down the street",
			"InfoFormat": "Landmarks",
			"Latitude": 51.5074,
			"Longitude": 0.1278,
			"PartLocation": {
				"LocationOrdinalValue": 1,
				"LocationType": "Slot",
				"Orientation": "TopToBottom",
				"Reference": "Top",
				"ServiceLabel": "Label1"
			},
			"Placement": {
				"AdditionalInfo": "Turn left at the fountain.",
				"Rack": "Rack-2",
				"RackOffset": 3,
				"RackOffsetUnits": "OpenU",
				"Row": "Row-4"
			},
			"PostalAddress": {
				"Building": "The big one on the right",
				"City": "London",
				"Country": 826,
				"Floor": "Top floor",
				"HouseNumber": 89,
				"Name": "Big Data Center",
				"PlaceType": "office",
				"Street": "Clerkenwell Rd"
			}
		},
		"OnLine": true,
		"ResourceType": "Box",
		"TimeToProvision": "P0DT12H30M5S",
		"TimeToReplenish": "P5DT0H0M0S"
	}`

// TestSpareResourceSet tests the parsing of SpareResourceSet objects.
func TestSpareResourceSet(t *testing.T) {
	var result SpareResourceSet
	err := json.NewDecoder(strings.NewReader(spareResourceSetBody)).Decode(&result)

	if err != nil {
		t.Errorf("Error decoding JSON: %s", err)
	}

	if result.ID != "SpareResourceSet-1" {
		t.Errorf("Received invalid ID: %s", result.ID)
	}

	if result.Name != "SpareResourceSetOne" {
		t.Errorf("Received invalid name: %s", result.Name)
	}

	if result.OnHandLocation.AltitudeMeters != 150 {
		t.Errorf("OnHandLocation Altitude incorrect: %d", result.OnHandLocation.AltitudeMeters)
	}

	if !result.OnLine {
		t.Error("OnLine should be true")
	}

	if result.ResourceType != "Box" {
		t.Errorf("Invalid resource type: %s", result.ResourceType)
	}
}

// TestSpareResourceSetUpdate tests the Update call.
func TestSpareResourceSetUpdate(t *testing.T) { // nolint:dupl
	var result SpareResourceSet
	err := json.NewDecoder(strings.NewReader(spareResourceSetBody)).Decode(&result)

	if err != nil {
		t.Errorf("Error decoding JSON: %s", err)
	}

	testClient := &common.TestClient{}
	result.SetClient(testClient)

	result.OnLine = true
	result.ResourceType = "Hat"
	result.TimeToProvision = "P0DT06H30M5S"
	result.TimeToReplenish = "P5DT0H12M0S"
	err = result.Update()

	if err != nil {
		t.Errorf("Error making Update call: %s", err)
	}

	calls := testClient.CapturedCalls()

	if strings.Contains(calls[0].Payload, "OnLine") {
		t.Errorf("Unexpected OnLine update payload: %s", calls[0].Payload)
	}

	if !strings.Contains(calls[0].Payload, "ResourceType:Hat") {
		t.Errorf("Unexpected ResourceType update payload: %s", calls[0].Payload)
	}

	if !strings.Contains(calls[0].Payload, "TimeToProvision:P0DT06H30M5S") {
		t.Errorf("Unexpected TimeToProvision update payload: %s", calls[0].Payload)
	}

	if !strings.Contains(calls[0].Payload, "TimeToReplenish:P5DT0H12M0S") {
		t.Errorf("Unexpected TimeToReplenish update payload: %s", calls[0].Payload)
	}
}
<commit_msg>make PostalAddress.Country a string<commit_after>//
// SPDX-License-Identifier: BSD-3-Clause
//

package swordfish

import (
	"encoding/json"
	"strings"
	"testing"

	"github.com/stmcginnis/gofish/common"
)

var spareResourceSetBody = `{
		"@odata.context": "/redfish/v1/$metadata#SpareResourceSet.SpareResourceSet",
		"@odata.type": "#SpareResourceSet.v1_0_0.SpareResourceSet",
		"@odata.id": "/redfish/v1/SpareResourceSet",
		"Id": "SpareResourceSet-1",
		"Name": "SpareResourceSetOne",
		"Description": "SpareResourceSet One",
		"Links": {
			"OnHandSpares": [{
				"@odata.id": "/redfish/v1/SpareType/1"
			}],
			"OnHandSpares@odata.count": 1,
			"ReplacementSpareSets": [],
			"ReplacementSpareSets@odata.count": 0
		},
		"OnHandLocation": {
			"AltitudeMeters": 150,
			"Contacts": [{
					"ContactName": "Fred",
					"EmailAddress": "fred@example.com",
					"PhoneNumber": "+44 303 123 7300"
				},
				{
					"ContactName": "George",
					"EmailAddress": "george@example.com",
					"PhoneNumber": "+44 20 7405 7324"
				}
			],
			"Info": "Down the street",
			"InfoFormat": "Landmarks",
			"Latitude": 51.5074,
			"Longitude": 0.1278,
			"PartLocation": {
				"LocationOrdinalValue": 1,
				"LocationType": "Slot",
				"Orientation": "TopToBottom",
				"Reference": "Top",
				"ServiceLabel": "Label1"
			},
			"Placement": {
				"AdditionalInfo": "Turn left at the fountain.",
				"Rack": "Rack-2",
				"RackOffset": 3,
				"RackOffsetUnits": "OpenU",
				"Row": "Row-4"
			},
			"PostalAddress": {
				"Building": "The big one on the right",
				"City": "London",
				"Country": "UK",
				"Floor": "Top floor",
				"HouseNumber": 89,
				"Name": "Big Data Center",
				"PlaceType": "office",
				"Street": "Clerkenwell Rd"
			}
		},
		"OnLine": true,
		"ResourceType": "Box",
		"TimeToProvision": "P0DT12H30M5S",
		"TimeToReplenish": "P5DT0H0M0S"
	}`

// TestSpareResourceSet tests the parsing of SpareResourceSet objects.
func TestSpareResourceSet(t *testing.T) {
	var result SpareResourceSet
	err := json.NewDecoder(strings.NewReader(spareResourceSetBody)).Decode(&result)

	if err != nil {
		t.Errorf("Error decoding JSON: %s", err)
	}

	if result.ID != "SpareResourceSet-1" {
		t.Errorf("Received invalid ID: %s", result.ID)
	}

	if result.Name != "SpareResourceSetOne" {
		t.Errorf("Received invalid name: %s", result.Name)
	}

	if result.OnHandLocation.AltitudeMeters != 150 {
		t.Errorf("OnHandLocation Altitude incorrect: %d", result.OnHandLocation.AltitudeMeters)
	}

	if !result.OnLine {
		t.Error("OnLine should be true")
	}

	if result.ResourceType != "Box" {
		t.Errorf("Invalid resource type: %s", result.ResourceType)
	}
}

// TestSpareResourceSetUpdate tests the Update call.
func TestSpareResourceSetUpdate(t *testing.T) { // nolint:dupl
	var result SpareResourceSet
	err := json.NewDecoder(strings.NewReader(spareResourceSetBody)).Decode(&result)

	if err != nil {
		t.Errorf("Error decoding JSON: %s", err)
	}

	testClient := &common.TestClient{}
	result.SetClient(testClient)

	result.OnLine = true
	result.ResourceType = "Hat"
	result.TimeToProvision = "P0DT06H30M5S"
	result.TimeToReplenish = "P5DT0H12M0S"
	err = result.Update()

	if err != nil {
		t.Errorf("Error making Update call: %s", err)
	}

	calls := testClient.CapturedCalls()

	if strings.Contains(calls[0].Payload, "OnLine") {
		t.Errorf("Unexpected OnLine update payload: %s", calls[0].Payload)
	}

	if !strings.Contains(calls[0].Payload, "ResourceType:Hat") {
		t.Errorf("Unexpected ResourceType update payload: %s", calls[0].Payload)
	}

	if !strings.Contains(calls[0].Payload, "TimeToProvision:P0DT06H30M5S") {
		t.Errorf("Unexpected TimeToProvision update payload: %s", calls[0].Payload)
	}

	if !strings.Contains(calls[0].Payload, "TimeToReplenish:P5DT0H12M0S") {
		t.Errorf("Unexpected TimeToReplenish update payload: %s", calls[0].Payload)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Eleme Inc. All rights reserved.

package detector

import (
	"bufio"
	"fmt"
	"net"
	"path/filepath"
	"time"

	"github.com/eleme/banshee/config"
	"github.com/eleme/banshee/detector/cursor"
	"github.com/eleme/banshee/filter"
	"github.com/eleme/banshee/models"
	"github.com/eleme/banshee/storage"
	"github.com/eleme/banshee/storage/indexdb"
	"github.com/eleme/banshee/storage/metricdb"
	"github.com/eleme/banshee/storage/statedb"
	"github.com/eleme/banshee/util/log"
)

// MaxMetricNameLen results that metrics with long name will be refused.
const MaxMetricNameLen = 256

// Detector is a tcp server to detect anomalies.
type Detector struct {
	// Config
	cfg *config.Config
	// Storage
	db *storage.DB
	// Filter
	filter *filter.Filter
	// Cursor
	cursor *cursor.Cursor
	// Output
	outs []chan *models.Metric
}

// New creates a detector.
func New(cfg *config.Config, db *storage.DB, filter *filter.Filter) *Detector {
	d := new(Detector)
	d.cfg = cfg
	d.db = db
	d.filter = filter
	d.cursor = cursor.New(cfg.Detector.Factor, cfg.LeastC())
	return d
}

// Out outputs detected metrics to given channel.
func (d *Detector) Out(ch chan *models.Metric) {
	d.outs = append(d.outs, ch)
}

// output detected metrics to outs.
func (d *Detector) output(m *models.Metric) {
	for _, ch := range d.outs {
		select {
		case ch <- m:
		default:
			log.Error("output channel is full, skipping..")
		}
	}
}

// Start detector.
func (d *Detector) Start() {
	addr := fmt.Sprintf("0.0.0.0:%d", d.cfg.Detector.Port)
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatal("failed to bind tcp://%s: %v", addr, err)
	}
	log.Info("detector is listening on tcp://%s..", addr)
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Fatal("accept conn: %v", err)
		}
		go d.handle(conn)
	}
}

// Validate a metric.
func (d *Detector) Validate(m *models.Metric) bool {
	if len(m.Name) > MaxMetricNameLen {
		// Name too long.
		log.Error("metric name too long: %s", m.Name[:MaxMetricNameLen])
		return false
	}
	if m.Stamp < metricdb.Horizon() {
		// Stamp too small
		log.Error("metric stamp too small: %s, %d", m.Name, m.Stamp)
		return false
	}
	return true
}

// Handle a connection, it will filter the mertics by rules and detect whether
// the metrics are anomalies.
func (d *Detector) handle(conn net.Conn) {
	// New conn
	addr := conn.RemoteAddr()
	defer func() {
		conn.Close()
		log.Info("conn %s disconnected", addr)
	}()
	log.Info("conn %s established", addr)
	// Scan line by line.
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		if err := scanner.Err(); err != nil {
			log.Info("read conn: %v, closing it..", err)
			break
		}
		startAt := time.Now()
		// Parse
		line := scanner.Text()
		m, err := parseMetric(line)
		if err != nil {
			if len(line) > 10 {
				line = line[:10]
			}
			log.Error("parse '%s': %v, skipping..", line, err)
			continue
		}
		// Validation
		if !d.Validate(m) {
			continue
		}
		matched, rules := d.match(m)
		if matched {
			// Detect with states.
			err = d.detect(m)
			if err != nil {
				log.Error("failed to detect: %v, skipping..", err)
				continue
			}
			elapsed := time.Since(startAt)
			log.Info("%dμs detected %s %.4f", elapsed.Nanoseconds()/1000, m.Name, m.Score)
			// Output to alerter if test ok with matched rules.
			for _, rule := range rules {
				if rule.Test(m, d.cfg) {
					d.output(m)
				}
			}
			// Store
			if err := d.store(m); err != nil {
				log.Error("store metric %s: %v, skiping..", m.Name, err)
			}
		}
	}
}

// Test whether a metric matches the rules.
func (d *Detector) match(m *models.Metric) (bool, []*models.Rule) {
	// Check rules.
	rules := d.filter.MatchedRules(m)
	if len(rules) == 0 {
		log.Debug("%s hit no rules", m.Name)
		return false, rules
	}
	// Check blacklist.
	for _, pattern := range d.cfg.Detector.BlackList {
		ok, err := filepath.Match(pattern, m.Name)
		if err == nil && ok {
			log.Debug("%s hit black pattern %s", m.Name, pattern)
			return false, rules
		}
	}
	return true, rules
}

// Detect incoming metric with 3-sigma rule and fill the metric.Score.
func (d *Detector) detect(m *models.Metric) error {
	// Get pervious state.
	s, err := d.db.State.Get(m)
	if err != nil && err != statedb.ErrNotFound {
		return err
	}
	if err == statedb.ErrNotFound {
		s = nil
	}
	// Fill blank with zeros.
	for _, pattern := range d.cfg.Detector.FillBlankZeros {
		if ok, _ := filepath.Match(pattern, m.Name); ok {
			// Need to fill zeros.
			if s, err = d.fillBlankZeros(m, s); err != nil {
				return err
			}
			break
		}
	}
	// Move state next.
	s = d.cursor.Next(s, m)
	// Put the next state to db.
	return d.db.State.Put(m, s)
}

// store detected metrics.
func (d *Detector) store(m *models.Metric) error {
	// Metric.
	if err := d.db.Metric.Put(m); err != nil {
		return err
	}
	// Index.
	idx := &models.Index{}
	idx.WriteMetric(m)
	if err := d.db.Index.Put(idx); err != nil {
		return err
	}
	return nil
}

// fillBlankZeros moves the state with zero on blanks.
func (d *Detector) fillBlankZeros(m *models.Metric, s *models.State) (*models.State, error) {
	// Get index.
	idx, err := d.db.Index.Get(m.Name)
	if err != nil && err != indexdb.ErrNotFound {
		return nil, err
	}
	if err == indexdb.ErrNotFound {
		// Not found, the metric is the first time seen and s must be nil.
		// The metric will be trusted since its state is nil.
		return s, nil
	}
	// Fill history blanks with zeros.
	numGrid := d.cfg.Period[0]
	gridLen := d.cfg.Period[1]
	period := numGrid * gridLen
	periodNo := m.Stamp / period
	gridNo := (m.Stamp % period) / gridLen
	// Find the start grid stamp
	gridStart := gridNo*gridLen + periodNo*period
	for gridStart-period >= idx.Stamp {
		gridStart -= period
	}
	for ; gridStart < m.Stamp; gridStart += period {
		for stamp := gridStart; stamp < gridStart+gridLen && stamp < m.Stamp; stamp += d.cfg.Interval {
			if stamp > idx.Stamp {
				// Move state with zero.
				s = d.cursor.Next(s, &models.Metric{Stamp: stamp})
			}
		}
	}
	return s, nil
}
<commit_msg>Log warning message if detection is timed out<commit_after>// Copyright 2015 Eleme Inc. All rights reserved.

package detector

import (
	"bufio"
	"fmt"
	"net"
	"path/filepath"
	"time"

	"github.com/eleme/banshee/config"
	"github.com/eleme/banshee/detector/cursor"
	"github.com/eleme/banshee/filter"
	"github.com/eleme/banshee/models"
	"github.com/eleme/banshee/storage"
	"github.com/eleme/banshee/storage/indexdb"
	"github.com/eleme/banshee/storage/metricdb"
	"github.com/eleme/banshee/storage/statedb"
	"github.com/eleme/banshee/util/log"
)

// MaxMetricNameLen results that metrics with long name will be refused.
const MaxMetricNameLen = 256

// Detection timed out in nanoseconds.
const detectionTimedOut = 1000 * 1000

// Detector is a tcp server to detect anomalies.
type Detector struct {
	// Config
	cfg *config.Config
	// Storage
	db *storage.DB
	// Filter
	filter *filter.Filter
	// Cursor
	cursor *cursor.Cursor
	// Output
	outs []chan *models.Metric
}

// New creates a detector.
func New(cfg *config.Config, db *storage.DB, filter *filter.Filter) *Detector {
	d := new(Detector)
	d.cfg = cfg
	d.db = db
	d.filter = filter
	d.cursor = cursor.New(cfg.Detector.Factor, cfg.LeastC())
	return d
}

// Out outputs detected metrics to given channel.
func (d *Detector) Out(ch chan *models.Metric) {
	d.outs = append(d.outs, ch)
}

// output detected metrics to outs.
func (d *Detector) output(m *models.Metric) {
	for _, ch := range d.outs {
		select {
		case ch <- m:
		default:
			log.Error("output channel is full, skipping..")
		}
	}
}

// Start detector.
func (d *Detector) Start() {
	addr := fmt.Sprintf("0.0.0.0:%d", d.cfg.Detector.Port)
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatal("failed to bind tcp://%s: %v", addr, err)
	}
	log.Info("detector is listening on tcp://%s..", addr)
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Fatal("accept conn: %v", err)
		}
		go d.handle(conn)
	}
}

// Validate a metric.
func (d *Detector) Validate(m *models.Metric) bool {
	if len(m.Name) > MaxMetricNameLen {
		// Name too long.
		log.Error("metric name too long: %s", m.Name[:MaxMetricNameLen])
		return false
	}
	if m.Stamp < metricdb.Horizon() {
		// Stamp too small
		log.Error("metric stamp too small: %s, %d", m.Name, m.Stamp)
		return false
	}
	return true
}

// Handle a connection, it will filter the mertics by rules and detect whether
// the metrics are anomalies.
func (d *Detector) handle(conn net.Conn) {
	// New conn
	addr := conn.RemoteAddr()
	defer func() {
		conn.Close()
		log.Info("conn %s disconnected", addr)
	}()
	log.Info("conn %s established", addr)
	// Scan line by line.
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		if err := scanner.Err(); err != nil {
			log.Info("read conn: %v, closing it..", err)
			break
		}
		startAt := time.Now()
		// Parse
		line := scanner.Text()
		m, err := parseMetric(line)
		if err != nil {
			if len(line) > 10 {
				line = line[:10]
			}
			log.Error("parse '%s': %v, skipping..", line, err)
			continue
		}
		// Validation
		if !d.Validate(m) {
			continue
		}
		matched, rules := d.match(m)
		if matched {
			// Detect with states.
			err = d.detect(m)
			if err != nil {
				log.Error("failed to detect: %v, skipping..", err)
				continue
			}
			elapsed := time.Since(startAt)
			// Log if processing is slow.
			if elapsed.Nanoseconds() > detectionTimedOut {
				log.Warn("%dμs detected %s %.4f", elapsed.Nanoseconds()/1000, m.Name, m.Score)
			}
			// Output to alerter if test ok with matched rules.
			for _, rule := range rules {
				if rule.Test(m, d.cfg) {
					d.output(m)
				}
			}
			// Store
			if err := d.store(m); err != nil {
				log.Error("store metric %s: %v, skiping..", m.Name, err)
			}
		}
	}
}

// Test whether a metric matches the rules.
func (d *Detector) match(m *models.Metric) (bool, []*models.Rule) {
	// Check rules.
	rules := d.filter.MatchedRules(m)
	if len(rules) == 0 {
		log.Debug("%s hit no rules", m.Name)
		return false, rules
	}
	// Check blacklist.
	for _, pattern := range d.cfg.Detector.BlackList {
		ok, err := filepath.Match(pattern, m.Name)
		if err == nil && ok {
			log.Debug("%s hit black pattern %s", m.Name, pattern)
			return false, rules
		}
	}
	return true, rules
}

// Detect incoming metric with 3-sigma rule and fill the metric.Score.
func (d *Detector) detect(m *models.Metric) error {
	// Get pervious state.
	s, err := d.db.State.Get(m)
	if err != nil && err != statedb.ErrNotFound {
		return err
	}
	if err == statedb.ErrNotFound {
		s = nil
	}
	// Fill blank with zeros.
	for _, pattern := range d.cfg.Detector.FillBlankZeros {
		if ok, _ := filepath.Match(pattern, m.Name); ok {
			// Need to fill zeros.
			if s, err = d.fillBlankZeros(m, s); err != nil {
				return err
			}
			break
		}
	}
	// Move state next.
	s = d.cursor.Next(s, m)
	// Put the next state to db.
	return d.db.State.Put(m, s)
}

// store detected metrics.
func (d *Detector) store(m *models.Metric) error {
	// Metric.
	if err := d.db.Metric.Put(m); err != nil {
		return err
	}
	// Index.
	idx := &models.Index{}
	idx.WriteMetric(m)
	if err := d.db.Index.Put(idx); err != nil {
		return err
	}
	return nil
}

// fillBlankZeros moves the state with zero on blanks.
func (d *Detector) fillBlankZeros(m *models.Metric, s *models.State) (*models.State, error) {
	// Get index.
	idx, err := d.db.Index.Get(m.Name)
	if err != nil && err != indexdb.ErrNotFound {
		return nil, err
	}
	if err == indexdb.ErrNotFound {
		// Not found, the metric is the first time seen and s must be nil.
		// The metric will be trusted since its state is nil.
		return s, nil
	}
	// Fill history blanks with zeros.
	numGrid := d.cfg.Period[0]
	gridLen := d.cfg.Period[1]
	period := numGrid * gridLen
	periodNo := m.Stamp / period
	gridNo := (m.Stamp % period) / gridLen
	// Find the start grid stamp
	gridStart := gridNo*gridLen + periodNo*period
	for gridStart-period >= idx.Stamp {
		gridStart -= period
	}
	for ; gridStart < m.Stamp; gridStart += period {
		for stamp := gridStart; stamp < gridStart+gridLen && stamp < m.Stamp; stamp += d.cfg.Interval {
			if stamp > idx.Stamp {
				// Move state with zero.
				s = d.cursor.Next(s, &models.Metric{Stamp: stamp})
			}
		}
	}
	return s, nil
}
<|endoftext|>


<commit_before>package funk

import (
	"reflect"
	"strings"
)

// Filter iterates over elements of collection, returning an array of
// all elements predicate returns truthy for.
func Filter(arr interface{}, predicate interface{}) interface{} {
	if !IsIteratee(arr) {
		panic("First parameter must be an iteratee")
	}

	if !IsFunction(predicate, 1, 1) {
		panic("Second argument must be function")
	}

	funcValue := reflect.ValueOf(predicate)

	funcType := funcValue.Type()

	if funcType.Out(0).Kind() != reflect.Bool {
		panic("Return argument should be a boolean")
	}

	arrValue := reflect.ValueOf(arr)

	arrType := arrValue.Type()

	// Get slice type corresponding to array type
	resultSliceType := reflect.SliceOf(arrType.Elem())

	// MakeSlice takes a slice kind type, and makes a slice.
	resultSlice := reflect.MakeSlice(resultSliceType, 0, 0)

	for i := 0; i < arrValue.Len(); i++ {
		elem := arrValue.Index(i)

		result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool)

		if result {
			resultSlice = reflect.Append(resultSlice, elem)
		}
	}

	return resultSlice.Interface()
}

// Find iterates over elements of collection, returning the first
// element predicate returns truthy for.
func Find(arr interface{}, predicate interface{}) interface{} {
	if !IsIteratee(arr) {
		panic("First parameter must be an iteratee")
	}

	if !IsFunction(predicate, 1, 1) {
		panic("Second argument must be function")
	}

	funcValue := reflect.ValueOf(predicate)

	funcType := funcValue.Type()

	if funcType.Out(0).Kind() != reflect.Bool {
		panic("Return argument should be a boolean")
	}

	arrValue := reflect.ValueOf(arr)

	for i := 0; i < arrValue.Len(); i++ {
		elem := arrValue.Index(i)

		result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool)

		if result {
			return elem.Interface()
		}
	}

	return nil
}

// IndexOf gets the index at which the first occurrence of value is found in array or return -1
// if the value cannot be found
func IndexOf(in interface{}, elem interface{}) int {
	inValue := reflect.ValueOf(in)

	elemValue := reflect.ValueOf(elem)

	inType := inValue.Type()

	if inType.Kind() == reflect.String {
		return strings.Index(inValue.String(), elemValue.String())
	}

	if inType.Kind() == reflect.Slice {
		for i := 0; i < inValue.Len(); i++ {
			if equal(inValue.Index(i).Interface(), elem) {
				return i
			}
		}
	}

	return -1
}

// LastIndexOf gets the index at which the last occurrence of value is found in array or return -1
// if the value cannot be found
func LastIndexOf(in interface{}, elem interface{}) int {
	inValue := reflect.ValueOf(in)

	elemValue := reflect.ValueOf(elem)

	inType := inValue.Type()

	if inType.Kind() == reflect.String {
		return strings.LastIndex(inValue.String(), elemValue.String())
	}

	if inType.Kind() == reflect.Slice {
		length := inValue.Len()

		for i := length - 1; i >= 0; i-- {
			if equal(inValue.Index(i).Interface(), elem) {
				return i
			}
		}
	}

	return -1
}

// Contains returns true if an element is present in a iteratee.
func Contains(in interface{}, elem interface{}) bool {
	inValue := reflect.ValueOf(in)

	elemValue := reflect.ValueOf(elem)

	inType := inValue.Type()

	if inType.Kind() == reflect.String {
		return strings.Contains(inValue.String(), elemValue.String())
	}

	if inType.Kind() == reflect.Map {
		keys := inValue.MapKeys()
		for i := 0; i < len(keys); i++ {
			if equal(keys[i].Interface(), elem) {
				return true
			}
		}
	}

	if inType.Kind() == reflect.Slice {
		for i := 0; i < inValue.Len(); i++ {
			if equal(inValue.Index(i).Interface(), elem) {
				return true
			}
		}
	}

	return false
}
<commit_msg>In 'Contains', save one comparison against reflect.Slice for Map that does not contain the key<commit_after>package funk

import (
	"reflect"
	"strings"
)

// Filter iterates over elements of collection, returning an array of
// all elements predicate returns truthy for.
func Filter(arr interface{}, predicate interface{}) interface{} {
	if !IsIteratee(arr) {
		panic("First parameter must be an iteratee")
	}

	if !IsFunction(predicate, 1, 1) {
		panic("Second argument must be function")
	}

	funcValue := reflect.ValueOf(predicate)

	funcType := funcValue.Type()

	if funcType.Out(0).Kind() != reflect.Bool {
		panic("Return argument should be a boolean")
	}

	arrValue := reflect.ValueOf(arr)

	arrType := arrValue.Type()

	// Get slice type corresponding to array type
	resultSliceType := reflect.SliceOf(arrType.Elem())

	// MakeSlice takes a slice kind type, and makes a slice.
	resultSlice := reflect.MakeSlice(resultSliceType, 0, 0)

	for i := 0; i < arrValue.Len(); i++ {
		elem := arrValue.Index(i)

		result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool)

		if result {
			resultSlice = reflect.Append(resultSlice, elem)
		}
	}

	return resultSlice.Interface()
}

// Find iterates over elements of collection, returning the first
// element predicate returns truthy for.
func Find(arr interface{}, predicate interface{}) interface{} {
	if !IsIteratee(arr) {
		panic("First parameter must be an iteratee")
	}

	if !IsFunction(predicate, 1, 1) {
		panic("Second argument must be function")
	}

	funcValue := reflect.ValueOf(predicate)

	funcType := funcValue.Type()

	if funcType.Out(0).Kind() != reflect.Bool {
		panic("Return argument should be a boolean")
	}

	arrValue := reflect.ValueOf(arr)

	for i := 0; i < arrValue.Len(); i++ {
		elem := arrValue.Index(i)

		result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool)

		if result {
			return elem.Interface()
		}
	}

	return nil
}

// IndexOf gets the index at which the first occurrence of value is found in array or return -1
// if the value cannot be found
func IndexOf(in interface{}, elem interface{}) int {
	inValue := reflect.ValueOf(in)

	elemValue := reflect.ValueOf(elem)

	inType := inValue.Type()

	if inType.Kind() == reflect.String {
		return strings.Index(inValue.String(), elemValue.String())
	}

	if inType.Kind() == reflect.Slice {
		for i := 0; i < inValue.Len(); i++ {
			if equal(inValue.Index(i).Interface(), elem) {
				return i
			}
		}
	}

	return -1
}

// LastIndexOf gets the index at which the last occurrence of value is found in array or return -1
// if the value cannot be found
func LastIndexOf(in interface{}, elem interface{}) int {
	inValue := reflect.ValueOf(in)

	elemValue := reflect.ValueOf(elem)

	inType := inValue.Type()

	if inType.Kind() == reflect.String {
		return strings.LastIndex(inValue.String(), elemValue.String())
	}

	if inType.Kind() == reflect.Slice {
		length := inValue.Len()

		for i := length - 1; i >= 0; i-- {
			if equal(inValue.Index(i).Interface(), elem) {
				return i
			}
		}
	}

	return -1
}

// Contains returns true if an element is present in a iteratee.
func Contains(in interface{}, elem interface{}) bool {
	inValue := reflect.ValueOf(in)
	elemValue := reflect.ValueOf(elem)
	inType := inValue.Type()

	switch inType.Kind() {
	case reflect.String:
		return strings.Contains(inValue.String(), elemValue.String())
	case reflect.Map:
		for _, key := range inValue.MapKeys() {
			if equal(key.Interface(), elem) {
				return true
			}
		}
	case reflect.Slice:
		for i := 0; i < inValue.Len(); i++ {
			if equal(inValue.Index(i).Interface(), elem) {
				return true
			}
		}
	}

	return false
}
<|endoftext|>


<commit_before>package admission

import (
	"fmt"
	"io/ioutil"
	"os"
	"time"

	authorizationclient "github.com/openshift/client-go/authorization/clientset/versioned"
	buildclient "github.com/openshift/client-go/build/clientset/versioned"
	userclient "github.com/openshift/client-go/user/clientset/versioned"
	userinformer "github.com/openshift/client-go/user/informers/externalversions"
	oadmission "github.com/openshift/origin/pkg/cmd/server/admission"
	configapi "github.com/openshift/origin/pkg/cmd/server/apis/config"
	kubernetes "github.com/openshift/origin/pkg/cmd/server/kubernetes/master"
	imageapi "github.com/openshift/origin/pkg/image/apis/image"
	imageinformer "github.com/openshift/origin/pkg/image/generated/informers/internalversion"
	imageclient "github.com/openshift/origin/pkg/image/generated/internalclientset"
	projectcache "github.com/openshift/origin/pkg/project/cache"
	"github.com/openshift/origin/pkg/quota/controller/clusterquotamapping"
	quotainformer "github.com/openshift/origin/pkg/quota/generated/informers/internalversion"
	quotaclient "github.com/openshift/origin/pkg/quota/generated/internalclientset"
	"github.com/openshift/origin/pkg/quota/image"
	securityinformer "github.com/openshift/origin/pkg/security/generated/informers/internalversion"
	"github.com/openshift/origin/pkg/service"
	templateclient "github.com/openshift/origin/pkg/template/generated/internalclientset"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apiserver/pkg/admission"
	"k8s.io/apiserver/pkg/admission/initializer"
	webhookconfig "k8s.io/apiserver/pkg/admission/plugin/webhook/config"
	webhookinitializer "k8s.io/apiserver/pkg/admission/plugin/webhook/initializer"
	"k8s.io/apiserver/pkg/authorization/authorizer"
	genericapiserver "k8s.io/apiserver/pkg/server"
	"k8s.io/client-go/discovery"
	cacheddiscovery "k8s.io/client-go/discovery/cached"
	kexternalinformers "k8s.io/client-go/informers"
	kubeclientgoinformers "k8s.io/client-go/informers"
	kubeclientgoclient "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	aggregatorapiserver "k8s.io/kube-aggregator/pkg/apiserver"
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	kclientsetinternal "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	kinternalinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
	kadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
	"k8s.io/kubernetes/pkg/quota/generic"
	"k8s.io/kubernetes/pkg/quota/install"
)

type InformerAccess interface {
	GetInternalKubeInformers() kinternalinformers.SharedInformerFactory
	GetExternalKubeInformers() kexternalinformers.SharedInformerFactory
	GetClientGoKubeInformers() kubeclientgoinformers.SharedInformerFactory
	GetImageInformers() imageinformer.SharedInformerFactory
	GetQuotaInformers() quotainformer.SharedInformerFactory
	GetSecurityInformers() securityinformer.SharedInformerFactory
	GetUserInformers() userinformer.SharedInformerFactory
}

func NewPluginInitializer(
	options configapi.MasterConfig,
	privilegedLoopbackConfig *rest.Config,
	informers InformerAccess,
	authorizer authorizer.Authorizer,
	projectCache *projectcache.ProjectCache,
	clusterQuotaMappingController *clusterquotamapping.ClusterQuotaMappingController,
) (admission.PluginInitializer, genericapiserver.PostStartHookFunc, error) {
	kubeInternalClient, err := kclientsetinternal.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	kubeClientGoClientSet, err := kubeclientgoclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	authorizationClient, err := authorizationclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	buildClient, err := buildclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	imageClient, err := imageclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	quotaClient, err := quotaclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	templateClient, err := templateclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	userClient, err := userclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}

	// TODO make a union registry
	quotaRegistry := generic.NewRegistry(install.NewQuotaConfigurationForAdmission().Evaluators())
	imageEvaluators := image.NewReplenishmentEvaluatorsForAdmission(
		informers.GetImageInformers().Image().InternalVersion().ImageStreams(),
		imageClient.Image(),
	)
	for i := range imageEvaluators {
		quotaRegistry.Add(imageEvaluators[i])
	}

	defaultRegistry := env("OPENSHIFT_DEFAULT_REGISTRY", "${DOCKER_REGISTRY_SERVICE_HOST}:${DOCKER_REGISTRY_SERVICE_PORT}")
	svcCache := service.NewServiceResolverCache(kubeInternalClient.Core().Services(metav1.NamespaceDefault).Get)
	defaultRegistryFunc, err := svcCache.Defer(defaultRegistry)
	if err != nil {
		return nil, nil, fmt.Errorf("OPENSHIFT_DEFAULT_REGISTRY variable is invalid %q: %v", defaultRegistry, err)
	}

	// Use a discovery client capable of being refreshed.
	discoveryClient := cacheddiscovery.NewMemCacheClient(kubeInternalClient.Discovery())
	restMapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, meta.InterfacesForUnstructured)

	// punch through layers to build this in order to get a string for a cloud provider file
	// TODO refactor us into a forward building flow with a side channel like this
	kubeOptions, err := kubernetes.BuildKubeAPIserverOptions(options)
	if err != nil {
		return nil, nil, err
	}

	var cloudConfig []byte
	if kubeOptions.CloudProvider.CloudConfigFile != "" {
		var err error
		cloudConfig, err = ioutil.ReadFile(kubeOptions.CloudProvider.CloudConfigFile)
		if err != nil {
			return nil, nil, fmt.Errorf("Error reading from cloud configuration file %s: %v", kubeOptions.CloudProvider.CloudConfigFile, err)
		}
	}
	// note: we are passing a combined quota registry here...
	genericInitializer := initializer.New(
		kubeClientGoClientSet,
		informers.GetClientGoKubeInformers(),
		authorizer,
		legacyscheme.Scheme,
	)
	kubePluginInitializer := kadmission.NewPluginInitializer(
		kubeInternalClient,
		informers.GetInternalKubeInformers(),
		cloudConfig,
		restMapper,
		generic.NewConfiguration(quotaRegistry.List(), map[schema.GroupResource]struct{}{}))

	webhookInitializer := webhookinitializer.NewPluginInitializer(
		func(delegate webhookconfig.AuthenticationInfoResolver) webhookconfig.AuthenticationInfoResolver {
			return webhookconfig.AuthenticationInfoResolverFunc(func(server string) (*rest.Config, error) {
				if server == "kubernetes.default.svc" {
					return rest.CopyConfig(privilegedLoopbackConfig), nil
				}
				return delegate.ClientConfigFor(server)
			})
		},
		aggregatorapiserver.NewClusterIPServiceResolver(informers.GetClientGoKubeInformers().Core().V1().Services().Lister()),
	)

	openshiftPluginInitializer := &oadmission.PluginInitializer{
		OpenshiftInternalAuthorizationClient: authorizationClient,
		OpenshiftInternalBuildClient:         buildClient,
		OpenshiftInternalImageClient:         imageClient,
		OpenshiftInternalQuotaClient:         quotaClient,
		OpenshiftInternalTemplateClient:      templateClient,
		OpenshiftInternalUserClient:          userClient,
		ProjectCache:                         projectCache,
		OriginQuotaRegistry:                  quotaRegistry,
		Authorizer:                           authorizer,
		JenkinsPipelineConfig:                options.JenkinsPipelineConfig,
		RESTClientConfig:                     *privilegedLoopbackConfig,
		Informers:                            informers.GetInternalKubeInformers(),
		ClusterResourceQuotaInformer:         informers.GetQuotaInformers().Quota().InternalVersion().ClusterResourceQuotas(),
		ClusterQuotaMapper:                   clusterQuotaMappingController.GetClusterQuotaMapper(),
		RegistryHostnameRetriever:            imageapi.DefaultRegistryHostnameRetriever(defaultRegistryFunc, options.ImagePolicyConfig.ExternalRegistryHostname, options.ImagePolicyConfig.InternalRegistryHostname),
		SecurityInformers:                    informers.GetSecurityInformers(),
		UserInformers:                        informers.GetUserInformers(),
	}

	return admission.PluginInitializers{genericInitializer, webhookInitializer, kubePluginInitializer, openshiftPluginInitializer},
		func(context genericapiserver.PostStartHookContext) error {
			restMapper.Reset()
			go func() {
				wait.Until(func() {
					restMapper.Reset()
				}, 10*time.Second, context.StopCh)
			}()
			return nil
		},
		nil
}

// env returns an environment variable, or the defaultValue if it is not set.
func env(key string, defaultValue string) string {
	val := os.Getenv(key)
	if len(val) == 0 {
		return defaultValue
	}
	return val
}
<commit_msg>interesting: webhook auth resolver change<commit_after>package admission

import (
	"fmt"
	"io/ioutil"
	"os"
	"time"

	authorizationclient "github.com/openshift/client-go/authorization/clientset/versioned"
	buildclient "github.com/openshift/client-go/build/clientset/versioned"
	userclient "github.com/openshift/client-go/user/clientset/versioned"
	userinformer "github.com/openshift/client-go/user/informers/externalversions"
	oadmission "github.com/openshift/origin/pkg/cmd/server/admission"
	configapi "github.com/openshift/origin/pkg/cmd/server/apis/config"
	kubernetes "github.com/openshift/origin/pkg/cmd/server/kubernetes/master"
	imageapi "github.com/openshift/origin/pkg/image/apis/image"
	imageinformer "github.com/openshift/origin/pkg/image/generated/informers/internalversion"
	imageclient "github.com/openshift/origin/pkg/image/generated/internalclientset"
	projectcache "github.com/openshift/origin/pkg/project/cache"
	"github.com/openshift/origin/pkg/quota/controller/clusterquotamapping"
	quotainformer "github.com/openshift/origin/pkg/quota/generated/informers/internalversion"
	quotaclient "github.com/openshift/origin/pkg/quota/generated/internalclientset"
	"github.com/openshift/origin/pkg/quota/image"
	securityinformer "github.com/openshift/origin/pkg/security/generated/informers/internalversion"
	"github.com/openshift/origin/pkg/service"
	templateclient "github.com/openshift/origin/pkg/template/generated/internalclientset"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apiserver/pkg/admission"
	"k8s.io/apiserver/pkg/admission/initializer"
	webhookconfig "k8s.io/apiserver/pkg/admission/plugin/webhook/config"
	webhookinitializer "k8s.io/apiserver/pkg/admission/plugin/webhook/initializer"
	"k8s.io/apiserver/pkg/authorization/authorizer"
	genericapiserver "k8s.io/apiserver/pkg/server"
	"k8s.io/client-go/discovery"
	cacheddiscovery "k8s.io/client-go/discovery/cached"
	kexternalinformers "k8s.io/client-go/informers"
	kubeclientgoinformers "k8s.io/client-go/informers"
	kubeclientgoclient "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	aggregatorapiserver "k8s.io/kube-aggregator/pkg/apiserver"
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	kclientsetinternal "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	kinternalinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
	kadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
	"k8s.io/kubernetes/pkg/quota/generic"
	"k8s.io/kubernetes/pkg/quota/install"
)

type InformerAccess interface {
	GetInternalKubeInformers() kinternalinformers.SharedInformerFactory
	GetExternalKubeInformers() kexternalinformers.SharedInformerFactory
	GetClientGoKubeInformers() kubeclientgoinformers.SharedInformerFactory
	GetImageInformers() imageinformer.SharedInformerFactory
	GetQuotaInformers() quotainformer.SharedInformerFactory
	GetSecurityInformers() securityinformer.SharedInformerFactory
	GetUserInformers() userinformer.SharedInformerFactory
}

func NewPluginInitializer(
	options configapi.MasterConfig,
	privilegedLoopbackConfig *rest.Config,
	informers InformerAccess,
	authorizer authorizer.Authorizer,
	projectCache *projectcache.ProjectCache,
	clusterQuotaMappingController *clusterquotamapping.ClusterQuotaMappingController,
) (admission.PluginInitializer, genericapiserver.PostStartHookFunc, error) {
	kubeInternalClient, err := kclientsetinternal.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	kubeClientGoClientSet, err := kubeclientgoclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	authorizationClient, err := authorizationclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	buildClient, err := buildclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	imageClient, err := imageclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	quotaClient, err := quotaclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	templateClient, err := templateclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}
	userClient, err := userclient.NewForConfig(privilegedLoopbackConfig)
	if err != nil {
		return nil, nil, err
	}

	// TODO make a union registry
	quotaRegistry := generic.NewRegistry(install.NewQuotaConfigurationForAdmission().Evaluators())
	imageEvaluators := image.NewReplenishmentEvaluatorsForAdmission(
		informers.GetImageInformers().Image().InternalVersion().ImageStreams(),
		imageClient.Image(),
	)
	for i := range imageEvaluators {
		quotaRegistry.Add(imageEvaluators[i])
	}

	defaultRegistry := env("OPENSHIFT_DEFAULT_REGISTRY", "${DOCKER_REGISTRY_SERVICE_HOST}:${DOCKER_REGISTRY_SERVICE_PORT}")
	svcCache := service.NewServiceResolverCache(kubeInternalClient.Core().Services(metav1.NamespaceDefault).Get)
	defaultRegistryFunc, err := svcCache.Defer(defaultRegistry)
	if err != nil {
		return nil, nil, fmt.Errorf("OPENSHIFT_DEFAULT_REGISTRY variable is invalid %q: %v", defaultRegistry, err)
	}

	// Use a discovery client capable of being refreshed.
	discoveryClient := cacheddiscovery.NewMemCacheClient(kubeInternalClient.Discovery())
	restMapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, meta.InterfacesForUnstructured)

	// punch through layers to build this in order to get a string for a cloud provider file
	// TODO refactor us into a forward building flow with a side channel like this
	kubeOptions, err := kubernetes.BuildKubeAPIserverOptions(options)
	if err != nil {
		return nil, nil, err
	}

	var cloudConfig []byte
	if kubeOptions.CloudProvider.CloudConfigFile != "" {
		var err error
		cloudConfig, err = ioutil.ReadFile(kubeOptions.CloudProvider.CloudConfigFile)
		if err != nil {
			return nil, nil, fmt.Errorf("Error reading from cloud configuration file %s: %v", kubeOptions.CloudProvider.CloudConfigFile, err)
		}
	}
	// note: we are passing a combined quota registry here...
	genericInitializer := initializer.New(
		kubeClientGoClientSet,
		informers.GetClientGoKubeInformers(),
		authorizer,
		legacyscheme.Scheme,
	)
	kubePluginInitializer := kadmission.NewPluginInitializer(
		kubeInternalClient,
		informers.GetInternalKubeInformers(),
		cloudConfig,
		restMapper,
		generic.NewConfiguration(quotaRegistry.List(), map[schema.GroupResource]struct{}{}))

	webhookAuthResolverWrapper := func(delegate webhookconfig.AuthenticationInfoResolver) webhookconfig.AuthenticationInfoResolver {
		return &webhookconfig.AuthenticationInfoResolverDelegator{
			ClientConfigForFunc: func(server string) (*rest.Config, error) {
				if server == "kubernetes.default.svc" {
					return rest.CopyConfig(privilegedLoopbackConfig), nil
				}
				return delegate.ClientConfigFor(server)
			},
			ClientConfigForServiceFunc: func(serviceName, serviceNamespace string) (*rest.Config, error) {
				if serviceName == "kubernetes" && serviceNamespace == "default" {
					return rest.CopyConfig(privilegedLoopbackConfig), nil
				}
				return delegate.ClientConfigForService(serviceName, serviceNamespace)
			},
		}
	}

	webhookInitializer := webhookinitializer.NewPluginInitializer(
		webhookAuthResolverWrapper,
		aggregatorapiserver.NewClusterIPServiceResolver(informers.GetClientGoKubeInformers().Core().V1().Services().Lister()),
	)

	openshiftPluginInitializer := &oadmission.PluginInitializer{
		OpenshiftInternalAuthorizationClient: authorizationClient,
		OpenshiftInternalBuildClient:         buildClient,
		OpenshiftInternalImageClient:         imageClient,
		OpenshiftInternalQuotaClient:         quotaClient,
		OpenshiftInternalTemplateClient:      templateClient,
		OpenshiftInternalUserClient:          userClient,
		ProjectCache:                         projectCache,
		OriginQuotaRegistry:                  quotaRegistry,
		Authorizer:                           authorizer,
		JenkinsPipelineConfig:                options.JenkinsPipelineConfig,
		RESTClientConfig:                     *privilegedLoopbackConfig,
		Informers:                            informers.GetInternalKubeInformers(),
		ClusterResourceQuotaInformer:         informers.GetQuotaInformers().Quota().InternalVersion().ClusterResourceQuotas(),
		ClusterQuotaMapper:                   clusterQuotaMappingController.GetClusterQuotaMapper(),
		RegistryHostnameRetriever:            imageapi.DefaultRegistryHostnameRetriever(defaultRegistryFunc, options.ImagePolicyConfig.ExternalRegistryHostname, options.ImagePolicyConfig.InternalRegistryHostname),
		SecurityInformers:                    informers.GetSecurityInformers(),
		UserInformers:                        informers.GetUserInformers(),
	}

	return admission.PluginInitializers{genericInitializer, webhookInitializer, kubePluginInitializer, openshiftPluginInitializer},
		func(context genericapiserver.PostStartHookContext) error {
			restMapper.Reset()
			go func() {
				wait.Until(func() {
					restMapper.Reset()
				}, 10*time.Second, context.StopCh)
			}()
			return nil
		},
		nil
}

// env returns an environment variable, or the defaultValue if it is not set.
func env(key string, defaultValue string) string {
	val := os.Getenv(key)
	if len(val) == 0 {
		return defaultValue
	}
	return val
}
<|endoftext|>


<commit_before>package events

import (
	"fmt"

	log "github.com/mozilla-services/reaper/reaperlog"
)

// ReaperEventConfig is the configuration for a ReaperEvent
type ReaperEventConfig struct {
	*EventReporterConfig

	Mode string
}

// ReaperEvent implements EventReporter, terminates resources
type ReaperEvent struct {
	Config *ReaperEventConfig
}

// setDryRun is a method of EventReporter
func (e *ReaperEvent) setDryRun(b bool) {
	e.Config.DryRun = b
}

// NewReaperEvent returns a new instance of ReaperEvent
func NewReaperEvent(c *ReaperEventConfig) *ReaperEvent {
	c.Name = "ReaperEvent"
	return &ReaperEvent{c}
}

// newReapableEvent is a method of EventReporter
func (e *ReaperEvent) newReapableEvent(r Reapable, tags []string) error {
	if e.Config.shouldTriggerFor(r) {
		var err error
		switch e.Config.Mode {
		case "Stop":
			_, err = r.Stop()
			if log.Extras() {
				log.Info("ReaperEvent: Stopping ", r.ReapableDescriptionShort())
			}
			NewEvent("Reaper: Stopping instance", r.ReapableDescriptionShort(), nil, []string{})
			NewCountStatistic("reaper.reapables.stopped", []string{})
		case "Terminate":
			_, err = r.Terminate()
			if log.Extras() {
				log.Info("ReaperEvent: Terminating ", r.ReapableDescriptionShort())
			}
			NewEvent("Reaper: Terminating instance", r.ReapableDescriptionShort(), nil, []string{})
			NewCountStatistic("reaper.reapables.terminated", []string{})
		default:
			log.Error(fmt.Sprintf("Invalid %s Mode %s", e.Config.Name, e.Config.Mode))
		}
		if err != nil {
			return err
		}
	}
	return nil
}

// newBatchReapableEvent is a method of EventReporter
func (e *ReaperEvent) newBatchReapableEvent(rs []Reapable, tags []string) error {
	for _, r := range rs {
		err := e.newReapableEvent(r, tags)
		if err != nil {
			return err
		}
	}
	return nil
}

// GetConfig is a method of EventReporter
func (e *ReaperEvent) GetConfig() EventReporterConfig {
	return *e.Config.EventReporterConfig
}

// newCountStatistic is a method of EventReporter
func (e *ReaperEvent) newCountStatistic(string, []string) error {
	return nil
}

// newStatistic is a method of EventReporter
func (e *ReaperEvent) newStatistic(string, float64, []string) error {
	return nil
}

// newEvent is a method of EventReporter
func (e *ReaperEvent) newEvent(string, string, map[string]string, []string) error {
	return nil
}
<commit_msg>override reaperevent shouldtriggerfor<commit_after>package events

import (
	"fmt"

	log "github.com/mozilla-services/reaper/reaperlog"
)

// ReaperEventConfig is the configuration for a ReaperEvent
type ReaperEventConfig struct {
	*EventReporterConfig

	Mode string
}

// this is a copy of the method from events.go EXCEPT
// that it triggers whether or not the state was updated this run
func (e *ReaperEventConfig) shouldTriggerFor(r Reapable) bool {
	triggering := false
	// if the reapable's state is set to trigger this EventReporter
	for _, trigger := range e.parseTriggers() {
		// if the reapable's state should trigger this event
		if trigger == r.ReaperState().State {
			triggering = true
		}
	}

	if e.DryRun {
		if log.Extras() {
			log.Info("DryRun: Not triggering %s for %s", e.Name, r.ReapableDescriptionTiny())
		}
		return false
	}
	return triggering
}

// ReaperEvent implements EventReporter, terminates resources
type ReaperEvent struct {
	Config *ReaperEventConfig
}

// setDryRun is a method of EventReporter
func (e *ReaperEvent) setDryRun(b bool) {
	e.Config.DryRun = b
}

// NewReaperEvent returns a new instance of ReaperEvent
func NewReaperEvent(c *ReaperEventConfig) *ReaperEvent {
	c.Name = "ReaperEvent"
	return &ReaperEvent{c}
}

// newReapableEvent is a method of EventReporter
func (e *ReaperEvent) newReapableEvent(r Reapable, tags []string) error {
	if e.Config.shouldTriggerFor(r) {
		var err error
		switch e.Config.Mode {
		case "Stop":
			_, err = r.Stop()
			if log.Extras() {
				log.Info("ReaperEvent: Stopping ", r.ReapableDescriptionShort())
			}
			NewEvent("Reaper: Stopping instance", r.ReapableDescriptionShort(), nil, []string{})
			NewCountStatistic("reaper.reapables.stopped", []string{})
		case "Terminate":
			_, err = r.Terminate()
			if log.Extras() {
				log.Info("ReaperEvent: Terminating ", r.ReapableDescriptionShort())
			}
			NewEvent("Reaper: Terminating instance", r.ReapableDescriptionShort(), nil, []string{})
			NewCountStatistic("reaper.reapables.terminated", []string{})
		default:
			log.Error(fmt.Sprintf("Invalid %s Mode %s", e.Config.Name, e.Config.Mode))
		}
		if err != nil {
			return err
		}
	}
	return nil
}

// newBatchReapableEvent is a method of EventReporter
func (e *ReaperEvent) newBatchReapableEvent(rs []Reapable, tags []string) error {
	for _, r := range rs {
		err := e.newReapableEvent(r, tags)
		if err != nil {
			return err
		}
	}
	return nil
}

// GetConfig is a method of EventReporter
func (e *ReaperEvent) GetConfig() EventReporterConfig {
	return *e.Config.EventReporterConfig
}

// newCountStatistic is a method of EventReporter
func (e *ReaperEvent) newCountStatistic(string, []string) error {
	return nil
}

// newStatistic is a method of EventReporter
func (e *ReaperEvent) newStatistic(string, float64, []string) error {
	return nil
}

// newEvent is a method of EventReporter
func (e *ReaperEvent) newEvent(string, string, map[string]string, []string) error {
	return nil
}
<|endoftext|>


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

package runutil

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"v.io/x/lib/envvar"
)

const (
	prefix = ">>"
)

type Opts struct {
	Color   bool
	Dir     string
	DryRun  bool
	Env     map[string]string
	Stdin   io.Reader
	Stdout  io.Writer
	Stderr  io.Writer
	Verbose bool
}

type executor struct {
	indent int
	opts   Opts
}

func newExecutor(env map[string]string, stdin io.Reader, stdout, stderr io.Writer, color, dryRun, verbose bool) *executor {
	if color {
		term := os.Getenv("TERM")
		switch term {
		case "dumb", "":
			color = false
		}
	}
	return &executor{
		indent: 0,
		opts: Opts{
			Color:   color,
			DryRun:  dryRun,
			Env:     env,
			Stdin:   stdin,
			Stdout:  stdout,
			Stderr:  stderr,
			Verbose: verbose,
		},
	}
}

// Opts returns the instance's options.
func (e *executor) Opts() Opts {
	return e.opts
}

// execute executes the binary pointed to by the given path using the given
// arguments and options. If the wait flag is set, the function waits for the
// completion of the binary and the timeout value can optionally specify for
// how long should the function wait before timing out.
func (e *executor) execute(wait bool, timeout time.Duration, opts Opts, path string, args ...string) (*exec.Cmd, error) {
	e.increaseIndent()
	defer e.decreaseIndent()

	// Check if <path> identifies a binary in the PATH environment
	// variable of the opts.Env.
	binary, err := LookPath(path, opts.Env)
	if err == nil {
		// If so, make sure to execute this binary. This step
		// enables us to "shadow" binaries included in the
		// PATH environment variable of the host OS (which
		// would be otherwise used to lookup <path>).
		//
		// This mechanism is used instead of modifying the
		// PATH environment variable of the host OS as the
		// latter is not thread-safe.
		path = binary
	}
	command := exec.Command(path, args...)
	command.Dir = opts.Dir
	command.Stdin = opts.Stdin
	command.Stdout = opts.Stdout
	command.Stderr = opts.Stderr
	if len(opts.Env) != 0 {
		vars := envvar.VarsFromOS()
		for key, value := range opts.Env {
			vars.Set(key, value)
		}
		command.Env = vars.ToSlice()
	}
	if opts.Verbose || opts.DryRun {
		args := []string{}
		for _, arg := range command.Args {
			// Quote any arguments that contain '"', ''', '|', or ' '.
			if strings.IndexAny(arg, "\"' |") != -1 {
				args = append(args, strconv.Quote(arg))
			} else {
				args = append(args, arg)
			}
		}
		e.printf(e.opts.Stdout, strings.Replace(strings.Join(args, " "), "%", "%%", -1))
	}
	if opts.DryRun {
		e.printf(e.opts.Stdout, "OK")
		return nil, nil
	}

	if wait {
		if timeout == 0 {
			if err = command.Run(); err != nil {
				if opts.Verbose {
					e.printf(e.opts.Stdout, "FAILED")
				}
			} else {
				if opts.Verbose {
					e.printf(e.opts.Stdout, "OK")
				}
			}
		} else {
			err = e.timedCommand(timeout, opts, command)
		}
	} else {
		err = command.Start()
		if err != nil {
			if opts.Verbose {
				e.printf(e.opts.Stdout, "FAILED")
			}
		} else {
			if opts.Verbose {
				e.printf(e.opts.Stdout, "OK")
			}
		}
	}
	return command, err
}

// timedCommand executes the given command, terminating it forcefully
// if it is still running after the given timeout elapses.
func (e *executor) timedCommand(timeout time.Duration, opts Opts, command *exec.Cmd) error {
	// Make the process of this command a new process group leader
	// to facilitate clean up of processes that time out.
	command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	// Kill this process group explicitly when receiving SIGTERM
	// or SIGINT signals.
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-sigchan
		e.terminateProcessGroup(command)
	}()
	if err := command.Start(); err != nil {
		if opts.Verbose {
			e.printf(e.opts.Stdout, "FAILED")
		}
		return err
	}
	done := make(chan error, 1)
	go func() {
		done <- command.Wait()
	}()
	select {
	case <-time.After(timeout):
		// The command has timed out.
		e.terminateProcessGroup(command)
		// Allow goroutine to exit.
		<-done
		if opts.Verbose {
			e.printf(e.opts.Stdout, "TIMED OUT")
		}
		return CommandTimedOutErr
	case err := <-done:
		if err != nil {
			if opts.Verbose {
				e.printf(e.opts.Stdout, "FAILED")
			}
		} else {
			if opts.Verbose {
				e.printf(e.opts.Stdout, "OK")
			}
		}
		return err
	}
}

// terminateProcessGroup sends SIGTERM followed by SIGKILL to the
// process group (the negative value of the process's pid).
func (e *executor) terminateProcessGroup(command *exec.Cmd) {
	pid := -command.Process.Pid
	if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
		fmt.Fprintf(e.opts.Stderr, "Kill(%v, %v) failed: %v\n", pid, syscall.SIGTERM, err)
	}
	fmt.Fprintf(e.opts.Stderr, "Waiting for command to exit: %q\n", command.Path)
	// Give the process some time to shut down cleanly.
	for i := 0; i < 10; i++ {
		select {
		case <-time.After(time.Second):
			if err := syscall.Kill(pid, 0); err != nil {
				return
			}
		}
	}
	// If it still exists, send SIGKILL to it.
	if err := syscall.Kill(pid, 0); err == nil {
		if err := syscall.Kill(-command.Process.Pid, syscall.SIGKILL); err != nil {
			fmt.Fprintf(e.opts.Stderr, "Kill(%v, %v) failed: %v\n", pid, syscall.SIGKILL, err)
		}
	}
}

func (e *executor) decreaseIndent() {
	e.indent--
}

func (e *executor) increaseIndent() {
	e.indent++
}

func (e *executor) printf(stdout io.Writer, format string, args ...interface{}) {
	timestamp := time.Now().Format("15:04:05.00")
	args = append([]interface{}{timestamp, strings.Repeat(prefix, e.indent)}, args...)
	fmt.Fprintf(stdout, "[%s] %v "+format+"\n", args...)
}
<commit_msg>jiri/runutil: print the full command not just the binary upon force termination<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runutil

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"v.io/x/lib/envvar"
)

const (
	prefix = ">>"
)

type Opts struct {
	Color   bool
	Dir     string
	DryRun  bool
	Env     map[string]string
	Stdin   io.Reader
	Stdout  io.Writer
	Stderr  io.Writer
	Verbose bool
}

type executor struct {
	indent int
	opts   Opts
}

func newExecutor(env map[string]string, stdin io.Reader, stdout, stderr io.Writer, color, dryRun, verbose bool) *executor {
	if color {
		term := os.Getenv("TERM")
		switch term {
		case "dumb", "":
			color = false
		}
	}
	return &executor{
		indent: 0,
		opts: Opts{
			Color:   color,
			DryRun:  dryRun,
			Env:     env,
			Stdin:   stdin,
			Stdout:  stdout,
			Stderr:  stderr,
			Verbose: verbose,
		},
	}
}

// Opts returns the instance's options.
func (e *executor) Opts() Opts {
	return e.opts
}

// execute executes the binary pointed to by the given path using the given
// arguments and options. If the wait flag is set, the function waits for the
// completion of the binary and the timeout value can optionally specify for
// how long should the function wait before timing out.
func (e *executor) execute(wait bool, timeout time.Duration, opts Opts, path string, args ...string) (*exec.Cmd, error) {
	e.increaseIndent()
	defer e.decreaseIndent()

	// Check if <path> identifies a binary in the PATH environment
	// variable of the opts.Env.
	binary, err := LookPath(path, opts.Env)
	if err == nil {
		// If so, make sure to execute this binary. This step
		// enables us to "shadow" binaries included in the
		// PATH environment variable of the host OS (which
		// would be otherwise used to lookup <path>).
		//
		// This mechanism is used instead of modifying the
		// PATH environment variable of the host OS as the
		// latter is not thread-safe.
		path = binary
	}
	command := exec.Command(path, args...)
	command.Dir = opts.Dir
	command.Stdin = opts.Stdin
	command.Stdout = opts.Stdout
	command.Stderr = opts.Stderr
	if len(opts.Env) != 0 {
		vars := envvar.VarsFromOS()
		for key, value := range opts.Env {
			vars.Set(key, value)
		}
		command.Env = vars.ToSlice()
	}
	if opts.Verbose || opts.DryRun {
		args := []string{}
		for _, arg := range command.Args {
			// Quote any arguments that contain '"', ''', '|', or ' '.
			if strings.IndexAny(arg, "\"' |") != -1 {
				args = append(args, strconv.Quote(arg))
			} else {
				args = append(args, arg)
			}
		}
		e.printf(e.opts.Stdout, strings.Replace(strings.Join(args, " "), "%", "%%", -1))
	}
	if opts.DryRun {
		e.printf(e.opts.Stdout, "OK")
		return nil, nil
	}

	if wait {
		if timeout == 0 {
			if err = command.Run(); err != nil {
				if opts.Verbose {
					e.printf(e.opts.Stdout, "FAILED")
				}
			} else {
				if opts.Verbose {
					e.printf(e.opts.Stdout, "OK")
				}
			}
		} else {
			err = e.timedCommand(timeout, opts, command)
		}
	} else {
		err = command.Start()
		if err != nil {
			if opts.Verbose {
				e.printf(e.opts.Stdout, "FAILED")
			}
		} else {
			if opts.Verbose {
				e.printf(e.opts.Stdout, "OK")
			}
		}
	}
	return command, err
}

// timedCommand executes the given command, terminating it forcefully
// if it is still running after the given timeout elapses.
func (e *executor) timedCommand(timeout time.Duration, opts Opts, command *exec.Cmd) error {
	// Make the process of this command a new process group leader
	// to facilitate clean up of processes that time out.
	command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	// Kill this process group explicitly when receiving SIGTERM
	// or SIGINT signals.
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-sigchan
		e.terminateProcessGroup(command)
	}()
	if err := command.Start(); err != nil {
		if opts.Verbose {
			e.printf(e.opts.Stdout, "FAILED")
		}
		return err
	}
	done := make(chan error, 1)
	go func() {
		done <- command.Wait()
	}()
	select {
	case <-time.After(timeout):
		// The command has timed out.
		e.terminateProcessGroup(command)
		// Allow goroutine to exit.
		<-done
		if opts.Verbose {
			e.printf(e.opts.Stdout, "TIMED OUT")
		}
		return CommandTimedOutErr
	case err := <-done:
		if err != nil {
			if opts.Verbose {
				e.printf(e.opts.Stdout, "FAILED")
			}
		} else {
			if opts.Verbose {
				e.printf(e.opts.Stdout, "OK")
			}
		}
		return err
	}
}

// terminateProcessGroup sends SIGTERM followed by SIGKILL to the
// process group (the negative value of the process's pid).
func (e *executor) terminateProcessGroup(command *exec.Cmd) {
	pid := -command.Process.Pid
	if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
		fmt.Fprintf(e.opts.Stderr, "Kill(%v, %v) failed: %v\n", pid, syscall.SIGTERM, err)
	}
	fmt.Fprintf(e.opts.Stderr, "Waiting for command to exit: %q\n", command.Args)
	// Give the process some time to shut down cleanly.
	for i := 0; i < 10; i++ {
		select {
		case <-time.After(time.Second):
			if err := syscall.Kill(pid, 0); err != nil {
				return
			}
		}
	}
	// If it still exists, send SIGKILL to it.
	if err := syscall.Kill(pid, 0); err == nil {
		if err := syscall.Kill(-command.Process.Pid, syscall.SIGKILL); err != nil {
			fmt.Fprintf(e.opts.Stderr, "Kill(%v, %v) failed: %v\n", pid, syscall.SIGKILL, err)
		}
	}
}

func (e *executor) decreaseIndent() {
	e.indent--
}

func (e *executor) increaseIndent() {
	e.indent++
}

func (e *executor) printf(stdout io.Writer, format string, args ...interface{}) {
	timestamp := time.Now().Format("15:04:05.00")
	args = append([]interface{}{timestamp, strings.Repeat(prefix, e.indent)}, args...)
	fmt.Fprintf(stdout, "[%s] %v "+format+"\n", args...)
}
<|endoftext|>


<commit_before>package nsq

import (
	"errors"
	"io"
	"log"
	"sync"
	"time"
)

// ProducerConfig carries the different variables to tune a newly started
// producer.
type ProducerConfig struct {
	Address         string
	MaxConcurrency  int
	DialTimeout     time.Duration
	ReadTimeout     time.Duration
	WriteTimeout    time.Duration
	MaxRetryTimeout time.Duration
	MinRetryTimeout time.Duration
}

// Producer provide an abstraction around using direct connections to nsqd
// nodes to send messages.
type Producer struct {
	// Communication channels of the producer.
	reqs chan ProducerRequest
	done chan struct{}
	once sync.Once
	join sync.WaitGroup

	// Immutable state of the producers.
	address         string
	dialTimeout     time.Duration
	readTimeout     time.Duration
	writeTimeout    time.Duration
	maxRetryTimeout time.Duration
	minRetryTimeout time.Duration
}

// ProducerRequest are used to represent operations that are submitted to
// producers.
type ProducerRequest struct {
	Topic    string
	Message  []byte
	Response chan<- error
}

// StartProducer starts and returns a new producer p, configured with the
// variables from the config parameter, or returning an non-nil error if
// some of the configuration variables were invalid.
func StartProducer(config ProducerConfig) (p *Producer, err error) {
	if len(config.Address) == 0 {
		config.Address = "localhost:4151"
	}

	if config.MaxConcurrency == 0 {
		config.MaxConcurrency = DefaultMaxConcurrency
	}

	if config.DialTimeout == 0 {
		config.DialTimeout = DefaultDialTimeout
	}

	if config.ReadTimeout == 0 {
		config.ReadTimeout = DefaultReadTimeout
	}

	if config.WriteTimeout == 0 {
		config.WriteTimeout = DefaultWriteTimeout
	}

	if config.MaxRetryTimeout == 0 {
		config.MaxRetryTimeout = DefaultMaxRetryTimeout
	}

	if config.MinRetryTimeout == 0 {
		config.MinRetryTimeout = DefaultMinRetryTimeout
	}

	p = &Producer{
		reqs:            make(chan ProducerRequest, config.MaxConcurrency),
		done:            make(chan struct{}),
		address:         config.Address,
		dialTimeout:     config.DialTimeout,
		readTimeout:     config.ReadTimeout,
		writeTimeout:    config.WriteTimeout,
		maxRetryTimeout: config.MaxRetryTimeout,
		minRetryTimeout: config.MinRetryTimeout,
	}
	p.join.Add(config.MaxConcurrency)

	for i := 0; i != config.MaxConcurrency; i++ {
		go p.run()
	}

	return
}

// Stop gracefully shutsdown the producer, cancelling all inflight requests and
// waiting for all backend connections to be closed.
//
// It is safe to call the method multiple times and from multiple goroutines,
// they will all block until the producer has been completely shutdown.
func (p *Producer) Stop() {
	p.once.Do(p.stop)
	err := errors.New("publishing to a producer that was already stopped")

	for req := range p.reqs {
		req.complete(err)
	}

	p.join.Wait()
}

// Publish enqueues a message to be sent by p, returning an error if p was
// already closed or if an error occurred while publishing the message.
//
// Note that no retry is done internally, the producer will fail after the
// first unsuccessful attempt to publish the message. It is the responsibility
// of the caller to retry if necessary.
func (p *Producer) Publish(topic string, message []byte) (err error) {
	defer func() {
		if recover() != nil {
			err = errors.New("publishing to a producer that was already stopped")
		}
	}()

	res := make(chan error, 1)

	p.reqs <- ProducerRequest{
		Topic:    topic,
		Message:  message,
		Response: res,
	}

	err = <-res
	return
}

// Requests returns a write-only channel that can be used to submit requests to p.
//
// This method is useful when the publish operation needs to be associated with
// other operations on channels in a select statement for example, or to publish
// in a non-blocking fashion.
func (p *Producer) Requests() chan<- ProducerRequest {
	return p.reqs
}

func (p *Producer) stop() {
	close(p.done)
	close(p.reqs)
}

func (p *Producer) run() {
	var conn *Conn
	var pipe chan ProducerRequest
	var ping chan struct{}
	var retry time.Duration

	defer p.join.Done()
	defer func() {
		if conn != nil {
			conn.Close()
		}
	}()

	for {
		var err error

		select {
		case <-p.done:
			return

		case <-ping:
			if err = p.ping(conn); err != nil {
				conn.Close()
				conn = nil
				ping = nil
				pipe = nil
				continue
			}

		case req, ok := <-p.reqs:
			if !ok {
				return
			}

			if conn == nil {
				if conn, err = DialTimeout(p.address, p.dialTimeout); err != nil {
					req.complete(err)

					log.Printf("failed to connect to %s, retrying after %s: %s", p.address, retry, err)
					retry = p.sleep(retry)
					continue
				}
				retry = 0
				pipe = make(chan ProducerRequest)
				ping = make(chan struct{})
				go p.flush(conn, pipe, ping)
			}

			if err = p.publish(conn, req.Topic, req.Message); err != nil {
				conn.Close()
				conn = nil
				ping = nil
				pipe = nil
				req.complete(err)
				continue
			}

			pipe <- req
		}
	}
}

func (p *Producer) flush(conn *Conn, pipe <-chan ProducerRequest, ping chan<- struct{}) {
	defer conn.Close()

	for {
		var frame Frame
		var err error

		select {
		default:
		case <-p.done:
			return
		}

		if frame, err = conn.ReadFrame(); err != nil {
			if err != io.EOF && err != io.ErrUnexpectedEOF {
				log.Print(err)
			}
			return
		}

		switch f := frame.(type) {
		case Response:
			switch f {
			case OK:
				req := <-pipe
				req.complete(nil)
				continue

			case Heartbeat:
				ping <- struct{}{}
				continue

			case CloseWait:
				continue
			}

		case Error:
			req := <-pipe
			req.complete(f)
			log.Printf("closing connection after receiving an error from %s: %s", conn.RemoteAddr(), f)
			return

		case Message:
			log.Printf("closing connection after receiving an unexpected message from %s: %s", conn.RemoteAddr(), f.FrameType())
			return

		default:
			log.Printf("closing connection after receiving an unsupported frame from %s: %s", conn.RemoteAddr(), f.FrameType())
			return
		}
	}
}

func (p *Producer) publish(conn *Conn, topic string, message []byte) error {
	return p.write(conn, Pub{Topic: topic, Message: message})
}

func (p *Producer) ping(conn *Conn) error {
	return p.write(conn, Nop{})
}

func (p *Producer) write(conn *Conn, cmd Command) (err error) {
	if err = conn.SetDeadline(time.Now().Add(p.writeTimeout)); err == nil {
		err = conn.WriteCommand(cmd)
	}
	return
}

func (p *Producer) sleep(d time.Duration) time.Duration {
	if d < p.minRetryTimeout {
		d = p.minRetryTimeout
	}

	t := time.NewTimer(d)
	defer t.Stop()

	select {
	case <-t.C:
	case <-p.done:
	}

	if d *= 2; d > p.maxRetryTimeout {
		d = p.maxRetryTimeout
	}

	return d
}

func (r ProducerRequest) complete(err error) {
	if r.Response != nil {
		r.Response <- err
	}
}
<commit_msg>fix comment<commit_after>package nsq

import (
	"errors"
	"io"
	"log"
	"sync"
	"time"
)

// ProducerConfig carries the different variables to tune a newly started
// producer.
type ProducerConfig struct {
	Address         string
	MaxConcurrency  int
	DialTimeout     time.Duration
	ReadTimeout     time.Duration
	WriteTimeout    time.Duration
	MaxRetryTimeout time.Duration
	MinRetryTimeout time.Duration
}

// Producer provide an abstraction around using direct connections to nsqd
// nodes to send messages.
type Producer struct {
	// Communication channels of the producer.
	reqs chan ProducerRequest
	done chan struct{}
	once sync.Once
	join sync.WaitGroup

	// Immutable state of the producers.
	address         string
	dialTimeout     time.Duration
	readTimeout     time.Duration
	writeTimeout    time.Duration
	maxRetryTimeout time.Duration
	minRetryTimeout time.Duration
}

// ProducerRequest are used to represent operations that are submitted to
// producers.
type ProducerRequest struct {
	Topic    string
	Message  []byte
	Response chan<- error
}

// StartProducer starts and returns a new producer p, configured with the
// variables from the config parameter, or returning an non-nil error if
// some of the configuration variables were invalid.
func StartProducer(config ProducerConfig) (p *Producer, err error) {
	if len(config.Address) == 0 {
		config.Address = "localhost:4151"
	}

	if config.MaxConcurrency == 0 {
		config.MaxConcurrency = DefaultMaxConcurrency
	}

	if config.DialTimeout == 0 {
		config.DialTimeout = DefaultDialTimeout
	}

	if config.ReadTimeout == 0 {
		config.ReadTimeout = DefaultReadTimeout
	}

	if config.WriteTimeout == 0 {
		config.WriteTimeout = DefaultWriteTimeout
	}

	if config.MaxRetryTimeout == 0 {
		config.MaxRetryTimeout = DefaultMaxRetryTimeout
	}

	if config.MinRetryTimeout == 0 {
		config.MinRetryTimeout = DefaultMinRetryTimeout
	}

	p = &Producer{
		reqs:            make(chan ProducerRequest, config.MaxConcurrency),
		done:            make(chan struct{}),
		address:         config.Address,
		dialTimeout:     config.DialTimeout,
		readTimeout:     config.ReadTimeout,
		writeTimeout:    config.WriteTimeout,
		maxRetryTimeout: config.MaxRetryTimeout,
		minRetryTimeout: config.MinRetryTimeout,
	}
	p.join.Add(config.MaxConcurrency)

	for i := 0; i != config.MaxConcurrency; i++ {
		go p.run()
	}

	return
}

// Stop gracefully shutsdown the producer, cancelling all inflight requests and
// waiting for all backend connections to be closed.
//
// It is safe to call the method multiple times and from multiple goroutines,
// they will all block until the producer has been completely shutdown.
func (p *Producer) Stop() {
	p.once.Do(p.stop)
	err := errors.New("publishing to a producer that was already stopped")

	for req := range p.reqs {
		req.complete(err)
	}

	p.join.Wait()
}

// Publish sends a message using the producer p, returning an error if it was
// already closed or if an error occurred while publishing the message.
//
// Note that no retry is done internally, the producer will fail after the
// first unsuccessful attempt to publish the message. It is the responsibility
// of the caller to retry if necessary.
func (p *Producer) Publish(topic string, message []byte) (err error) {
	defer func() {
		if recover() != nil {
			err = errors.New("publishing to a producer that was already stopped")
		}
	}()

	res := make(chan error, 1)

	p.reqs <- ProducerRequest{
		Topic:    topic,
		Message:  message,
		Response: res,
	}

	err = <-res
	return
}

// Requests returns a write-only channel that can be used to submit requests to p.
//
// This method is useful when the publish operation needs to be associated with
// other operations on channels in a select statement for example, or to publish
// in a non-blocking fashion.
func (p *Producer) Requests() chan<- ProducerRequest {
	return p.reqs
}

func (p *Producer) stop() {
	close(p.done)
	close(p.reqs)
}

func (p *Producer) run() {
	var conn *Conn
	var pipe chan ProducerRequest
	var ping chan struct{}
	var retry time.Duration

	defer p.join.Done()
	defer func() {
		if conn != nil {
			conn.Close()
		}
	}()

	for {
		var err error

		select {
		case <-p.done:
			return

		case <-ping:
			if err = p.ping(conn); err != nil {
				conn.Close()
				conn = nil
				ping = nil
				pipe = nil
				continue
			}

		case req, ok := <-p.reqs:
			if !ok {
				return
			}

			if conn == nil {
				if conn, err = DialTimeout(p.address, p.dialTimeout); err != nil {
					req.complete(err)

					log.Printf("failed to connect to %s, retrying after %s: %s", p.address, retry, err)
					retry = p.sleep(retry)
					continue
				}
				retry = 0
				pipe = make(chan ProducerRequest)
				ping = make(chan struct{})
				go p.flush(conn, pipe, ping)
			}

			if err = p.publish(conn, req.Topic, req.Message); err != nil {
				conn.Close()
				conn = nil
				ping = nil
				pipe = nil
				req.complete(err)
				continue
			}

			pipe <- req
		}
	}
}

func (p *Producer) flush(conn *Conn, pipe <-chan ProducerRequest, ping chan<- struct{}) {
	defer conn.Close()

	for {
		var frame Frame
		var err error

		select {
		default:
		case <-p.done:
			return
		}

		if frame, err = conn.ReadFrame(); err != nil {
			if err != io.EOF && err != io.ErrUnexpectedEOF {
				log.Print(err)
			}
			return
		}

		switch f := frame.(type) {
		case Response:
			switch f {
			case OK:
				req := <-pipe
				req.complete(nil)
				continue

			case Heartbeat:
				ping <- struct{}{}
				continue

			case CloseWait:
				continue
			}

		case Error:
			req := <-pipe
			req.complete(f)
			log.Printf("closing connection after receiving an error from %s: %s", conn.RemoteAddr(), f)
			return

		case Message:
			log.Printf("closing connection after receiving an unexpected message from %s: %s", conn.RemoteAddr(), f.FrameType())
			return

		default:
			log.Printf("closing connection after receiving an unsupported frame from %s: %s", conn.RemoteAddr(), f.FrameType())
			return
		}
	}
}

func (p *Producer) publish(conn *Conn, topic string, message []byte) error {
	return p.write(conn, Pub{Topic: topic, Message: message})
}

func (p *Producer) ping(conn *Conn) error {
	return p.write(conn, Nop{})
}

func (p *Producer) write(conn *Conn, cmd Command) (err error) {
	if err = conn.SetDeadline(time.Now().Add(p.writeTimeout)); err == nil {
		err = conn.WriteCommand(cmd)
	}
	return
}

func (p *Producer) sleep(d time.Duration) time.Duration {
	if d < p.minRetryTimeout {
		d = p.minRetryTimeout
	}

	t := time.NewTimer(d)
	defer t.Stop()

	select {
	case <-t.C:
	case <-p.done:
	}

	if d *= 2; d > p.maxRetryTimeout {
		d = p.maxRetryTimeout
	}

	return d
}

func (r ProducerRequest) complete(err error) {
	if r.Response != nil {
		r.Response <- err
	}
}
<|endoftext|>


<commit_before>package main

import(
    "github.com/spf13/cobra"
    "fmt"
)

func main() {

    var cmdScan = &cobra.Command{
        Use:   "scan",
        Short: "Scan current location for APs",
        Long:  `Turns on WiFi and scans for AP around.`,
        Run: func(cmd *cobra.Command, args []string) {
                fmt.Println("Scanning")
        },
    }

    var cmdProfile = &cobra.Command{
        Use:   "profile [name of the profile]",
        Short: "Manage profile",
        Long:  `Create, update, delete or differently manage the given profile.`,
        Run: func(cmd *cobra.Command, args []string) {
                fmt.Println("Managing")
        },
    }
    var rootCmd = &cobra.Command{Use: "proflock"}
    rootCmd.AddCommand(cmdScan, cmdProfile)
    rootCmd.Execute()
}

<commit_msg>improved scan command<commit_after>package main

import(
    "github.com/spf13/cobra"
    "fmt"
    "./iwscanner"
)

func main() {

    var cmdScan = &cobra.Command{
        Use:   "scan",
        Short: "Scan current location for APs",
        Long:  `Turns on WiFi and scans for AP around.`,
        Run: func(cmd *cobra.Command, args []string) {
                fmt.Println("Turning wifi on")

                if e := iwscanner.TurnWifi("wlan0", "on"); e != nil {
                        panic(e)
                }

                fmt.Println("Scanning")
                aps, err := iwscanner.GetAPs("wlan0")
                if err != nil {
                        panic(err);
                } else {
                        fmt.Println(aps)
                }
        },
    }

    var cmdProfile = &cobra.Command{
        Use:   "profile [name of the profile]",
        Short: "Manage profile",
        Long:  `Create, update, delete or differently manage the given profile.`,
        Run: func(cmd *cobra.Command, args []string) {
                fmt.Println("Managing")
        },
    }
    var rootCmd = &cobra.Command{Use: "proflock"}
    rootCmd.AddCommand(cmdScan, cmdProfile)
    rootCmd.Execute()
}

<|endoftext|>


<commit_before>package mpb

import (
	"container/heap"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"sync"
	"time"

	"github.com/vbauerster/mpb/cwriter"
)

const (
	// default RefreshRate
	prr = 120 * time.Millisecond
	// default width
	pwidth = 80
	// default format
	pformat = "[=>-]"
)

// Progress represents the container that renders Progress bars
type Progress struct {
	wg           *sync.WaitGroup
	uwg          *sync.WaitGroup
	operateState chan func(*pState)
	done         chan struct{}
}

type (
	pState struct {
		bHeap           *priorityQueue
		shutdownPending []*Bar
		heapUpdated     bool
		zeroWait        bool
		idCounter       int
		width           int
		format          string
		rr              time.Duration
		cw              *cwriter.Writer
		ticker          *time.Ticker
		pMatrix         map[int][]chan int
		aMatrix         map[int][]chan int

		// following are provided by user
		uwg              *sync.WaitGroup
		cancel           <-chan struct{}
		shutdownNotifier chan struct{}
		waitBars         map[*Bar]*Bar
		debugOut         io.Writer
	}
	widthSyncer struct {
		// Public for easy testing
		Accumulator []chan int
		Distributor []chan int
	}
)

// New creates new Progress instance, which orchestrates bars rendering process.
// Accepts mpb.ProgressOption funcs for customization.
func New(options ...ProgressOption) *Progress {
	pq := make(priorityQueue, 0)
	heap.Init(&pq)
	s := &pState{
		bHeap:    &pq,
		width:    pwidth,
		format:   pformat,
		cw:       cwriter.New(os.Stdout),
		rr:       prr,
		ticker:   time.NewTicker(prr),
		waitBars: make(map[*Bar]*Bar),
		debugOut: ioutil.Discard,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	p := &Progress{
		uwg:          s.uwg,
		wg:           new(sync.WaitGroup),
		operateState: make(chan func(*pState)),
		done:         make(chan struct{}),
	}
	go p.serve(s)
	return p
}

// AddBar creates a new progress bar and adds to the container.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
	p.wg.Add(1)
	result := make(chan *Bar)
	select {
	case p.operateState <- func(s *pState) {
		options = append(options, barWidth(s.width), barFormat(s.format))
		b := newBar(p.wg, s.idCounter, total, s.cancel, options...)
		if b.runningBar != nil {
			s.waitBars[b.runningBar] = b
		} else {
			heap.Push(s.bHeap, b)
			s.heapUpdated = true
		}
		s.idCounter++
		result <- b
	}:
		return <-result
	case <-p.done:
		p.wg.Done()
		return nil
	}
}

// Abort is only effective while bar progress is running,
// it means remove bar now without waiting for its completion.
// If bar is already completed, there is nothing to abort.
// If you need to remove bar after completion, use BarRemoveOnComplete BarOption.
func (p *Progress) Abort(b *Bar, remove bool) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		if remove {
			s.heapUpdated = heap.Remove(s.bHeap, b.index) != nil
		}
		s.shutdownPending = append(s.shutdownPending, b)
	}:
	case <-p.done:
	}
}

// UpdateBarPriority provides a way to change bar's order position.
// Zero is highest priority, i.e. bar will be on top.
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
	select {
	case p.operateState <- func(s *pState) { s.bHeap.update(b, priority) }:
	case <-p.done:
	}
}

// BarCount returns bars count
func (p *Progress) BarCount() int {
	result := make(chan int, 1)
	select {
	case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
		return <-result
	case <-p.done:
		return 0
	}
}

// Wait first waits for user provided *sync.WaitGroup, if any,
// then waits far all bars to complete and finally shutdowns master goroutine.
// After this method has been called, there is no way to reuse *Progress instance.
func (p *Progress) Wait() {
	if p.uwg != nil {
		p.uwg.Wait()
	}

	p.wg.Wait()

	select {
	case p.operateState <- func(s *pState) { s.zeroWait = true }:
		<-p.done
	case <-p.done:
	}
}

func (s *pState) updateSyncMatrix() {
	s.pMatrix = make(map[int][]chan int)
	s.aMatrix = make(map[int][]chan int)
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := (*s.bHeap)[i]
		table := bar.wSyncTable()
		pRow, aRow := table[0], table[1]

		for i, ch := range pRow {
			s.pMatrix[i] = append(s.pMatrix[i], ch)
		}

		for i, ch := range aRow {
			s.aMatrix[i] = append(s.aMatrix[i], ch)
		}
	}
}

func (s *pState) render(tw int) {
	if s.heapUpdated {
		s.updateSyncMatrix()
		s.heapUpdated = false
	}
	syncWidth(s.pMatrix)
	syncWidth(s.aMatrix)

	for i := 0; i < s.bHeap.Len(); i++ {
		bar := (*s.bHeap)[i]
		go bar.render(s.debugOut, tw)
	}

	if err := s.flush(); err != nil {
		fmt.Fprintf(s.debugOut, "%s %s %v\n", "[mpb]", time.Now(), err)
	}
}

func (s *pState) flush() (err error) {
	for s.bHeap.Len() > 0 {
		bar := heap.Pop(s.bHeap).(*Bar)
		reader := <-bar.frameReaderCh
		if _, e := s.cw.ReadFrom(reader); e != nil {
			err = e
		}
		defer func() {
			if frame, ok := reader.(*frameReader); ok && frame.toShutdown {
				// shutdown at next flush, in other words decrement underlying WaitGroup
				// only after the bar with completed state has been flushed.
				// this ensures no bar ends up with less than 100% rendered.
				s.shutdownPending = append(s.shutdownPending, bar)
				if replacementBar, ok := s.waitBars[bar]; ok {
					heap.Push(s.bHeap, replacementBar)
					s.heapUpdated = true
					delete(s.waitBars, bar)
				}
				if frame.removeOnComplete {
					s.heapUpdated = true
					return
				}
			}
			heap.Push(s.bHeap, bar)
		}()
	}

	if e := s.cw.Flush(); err == nil {
		err = e
	}

	for i := len(s.shutdownPending) - 1; i >= 0; i-- {
		close(s.shutdownPending[i].shutdown)
		s.shutdownPending = s.shutdownPending[:i]
	}
	return
}

func syncWidth(matrix map[int][]chan int) {
	for _, column := range matrix {
		column := column
		go func() {
			var maxWidth int
			for _, ch := range column {
				w := <-ch
				if w > maxWidth {
					maxWidth = w
				}
			}
			for _, ch := range column {
				ch <- maxWidth
			}
		}()
	}
}
<commit_msg>Remove unused struct type<commit_after>package mpb

import (
	"container/heap"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"sync"
	"time"

	"github.com/vbauerster/mpb/cwriter"
)

const (
	// default RefreshRate
	prr = 120 * time.Millisecond
	// default width
	pwidth = 80
	// default format
	pformat = "[=>-]"
)

// Progress represents the container that renders Progress bars
type Progress struct {
	wg           *sync.WaitGroup
	uwg          *sync.WaitGroup
	operateState chan func(*pState)
	done         chan struct{}
}

type pState struct {
	bHeap           *priorityQueue
	shutdownPending []*Bar
	heapUpdated     bool
	zeroWait        bool
	idCounter       int
	width           int
	format          string
	rr              time.Duration
	cw              *cwriter.Writer
	ticker          *time.Ticker
	pMatrix         map[int][]chan int
	aMatrix         map[int][]chan int

	// following are provided by user
	uwg              *sync.WaitGroup
	cancel           <-chan struct{}
	shutdownNotifier chan struct{}
	waitBars         map[*Bar]*Bar
	debugOut         io.Writer
}

// New creates new Progress instance, which orchestrates bars rendering process.
// Accepts mpb.ProgressOption funcs for customization.
func New(options ...ProgressOption) *Progress {
	pq := make(priorityQueue, 0)
	heap.Init(&pq)
	s := &pState{
		bHeap:    &pq,
		width:    pwidth,
		format:   pformat,
		cw:       cwriter.New(os.Stdout),
		rr:       prr,
		ticker:   time.NewTicker(prr),
		waitBars: make(map[*Bar]*Bar),
		debugOut: ioutil.Discard,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	p := &Progress{
		uwg:          s.uwg,
		wg:           new(sync.WaitGroup),
		operateState: make(chan func(*pState)),
		done:         make(chan struct{}),
	}
	go p.serve(s)
	return p
}

// AddBar creates a new progress bar and adds to the container.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
	p.wg.Add(1)
	result := make(chan *Bar)
	select {
	case p.operateState <- func(s *pState) {
		options = append(options, barWidth(s.width), barFormat(s.format))
		b := newBar(p.wg, s.idCounter, total, s.cancel, options...)
		if b.runningBar != nil {
			s.waitBars[b.runningBar] = b
		} else {
			heap.Push(s.bHeap, b)
			s.heapUpdated = true
		}
		s.idCounter++
		result <- b
	}:
		return <-result
	case <-p.done:
		p.wg.Done()
		return nil
	}
}

// Abort is only effective while bar progress is running,
// it means remove bar now without waiting for its completion.
// If bar is already completed, there is nothing to abort.
// If you need to remove bar after completion, use BarRemoveOnComplete BarOption.
func (p *Progress) Abort(b *Bar, remove bool) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		if remove {
			s.heapUpdated = heap.Remove(s.bHeap, b.index) != nil
		}
		s.shutdownPending = append(s.shutdownPending, b)
	}:
	case <-p.done:
	}
}

// UpdateBarPriority provides a way to change bar's order position.
// Zero is highest priority, i.e. bar will be on top.
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
	select {
	case p.operateState <- func(s *pState) { s.bHeap.update(b, priority) }:
	case <-p.done:
	}
}

// BarCount returns bars count
func (p *Progress) BarCount() int {
	result := make(chan int, 1)
	select {
	case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
		return <-result
	case <-p.done:
		return 0
	}
}

// Wait first waits for user provided *sync.WaitGroup, if any,
// then waits far all bars to complete and finally shutdowns master goroutine.
// After this method has been called, there is no way to reuse *Progress instance.
func (p *Progress) Wait() {
	if p.uwg != nil {
		p.uwg.Wait()
	}

	p.wg.Wait()

	select {
	case p.operateState <- func(s *pState) { s.zeroWait = true }:
		<-p.done
	case <-p.done:
	}
}

func (s *pState) updateSyncMatrix() {
	s.pMatrix = make(map[int][]chan int)
	s.aMatrix = make(map[int][]chan int)
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := (*s.bHeap)[i]
		table := bar.wSyncTable()
		pRow, aRow := table[0], table[1]

		for i, ch := range pRow {
			s.pMatrix[i] = append(s.pMatrix[i], ch)
		}

		for i, ch := range aRow {
			s.aMatrix[i] = append(s.aMatrix[i], ch)
		}
	}
}

func (s *pState) render(tw int) {
	if s.heapUpdated {
		s.updateSyncMatrix()
		s.heapUpdated = false
	}
	syncWidth(s.pMatrix)
	syncWidth(s.aMatrix)

	for i := 0; i < s.bHeap.Len(); i++ {
		bar := (*s.bHeap)[i]
		go bar.render(s.debugOut, tw)
	}

	if err := s.flush(); err != nil {
		fmt.Fprintf(s.debugOut, "%s %s %v\n", "[mpb]", time.Now(), err)
	}
}

func (s *pState) flush() (err error) {
	for s.bHeap.Len() > 0 {
		bar := heap.Pop(s.bHeap).(*Bar)
		reader := <-bar.frameReaderCh
		if _, e := s.cw.ReadFrom(reader); e != nil {
			err = e
		}
		defer func() {
			if frame, ok := reader.(*frameReader); ok && frame.toShutdown {
				// shutdown at next flush, in other words decrement underlying WaitGroup
				// only after the bar with completed state has been flushed.
				// this ensures no bar ends up with less than 100% rendered.
				s.shutdownPending = append(s.shutdownPending, bar)
				if replacementBar, ok := s.waitBars[bar]; ok {
					heap.Push(s.bHeap, replacementBar)
					s.heapUpdated = true
					delete(s.waitBars, bar)
				}
				if frame.removeOnComplete {
					s.heapUpdated = true
					return
				}
			}
			heap.Push(s.bHeap, bar)
		}()
	}

	if e := s.cw.Flush(); err == nil {
		err = e
	}

	for i := len(s.shutdownPending) - 1; i >= 0; i-- {
		close(s.shutdownPending[i].shutdown)
		s.shutdownPending = s.shutdownPending[:i]
	}
	return
}

func syncWidth(matrix map[int][]chan int) {
	for _, column := range matrix {
		column := column
		go func() {
			var maxWidth int
			for _, ch := range column {
				w := <-ch
				if w > maxWidth {
					maxWidth = w
				}
			}
			for _, ch := range column {
				ch <- maxWidth
			}
		}()
	}
}
<|endoftext|>