<commit_before>// Copyright ©2013 The gonum 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 mat64

import (
	"math"
	check "launchpad.net/gocheck"
)

func isLowerTriangular(a *Dense) bool {
	rows, cols := a.Dims()
	for r := 0; r < rows; r++ {
		for c := r + 1; c < cols; c++ {
			if math.Abs(a.At(r, c)) > 1e-14 {
				return false
			}
		}
	}
	return true
}

func (s *S) TestLQD(c *check.C) {
	for _, test := range []struct {
		a    [][]float64
		name string
	}{
		{
			name: "Square",
			a: [][]float64{
				{1.3, 2.4, 8.9},
				{-2.6, 8.7, 9.1},
				{5.6, 5.8, 2.1},
			},
		},
		{
			name: "Skinny",
			a: [][]float64{
				{1.3, 2.4, 8.9},
				{-2.6, 8.7, 9.1},
				{5.6, 5.8, 2.1},
				{19.4, 5.2, -26.1},
			},
		},
		{
			name: "Id",
			a: [][]float64{
				{1, 0, 0},
				{0, 1, 0},
				{0, 0, 1},
			},
		},
		{
			name: "Id",
			a: [][]float64{
				{0, 0, 2},
				{0, 1, 0},
				{3, 0, 0},
			},
		},
		{
			name: "small",
			a: [][]float64{
				{1, 1},
				{1, 2},
			},
		},
	} {

		a := NewDense(flatten(test.a))

		at := new(Dense)
		at.TCopy(a)

		lq := LQ(DenseCopyOf(at))

		rows, cols := a.Dims()

		Q := NewDense(rows, cols, nil)
		for i := 0; i < cols; i++ {
			Q.Set(i, i, 1)
		}
		lq.ApplyQ(Q, true)
		l := lq.L()

		lt := NewDense(rows, cols, nil)
		ltview := *lt
		ltview.View(0, 0, cols, cols)
		ltview.TCopy(l)
		lq.ApplyQ(lt, true)

		c.Check(isOrthogonal(Q), check.Equals, true, check.Commentf("Test %v: Q not orthogonal", test.name))
		c.Check(a.EqualsApprox(lt, 1e-13), check.Equals, true, check.Commentf("Test %v: Q*R != A", test.name))
		c.Check(isLowerTriangular(l), check.Equals, true,
			check.Commentf("Test %v: L not lower triangular", test.name))

		nrhs := 2
		barr := make([]float64, nrhs*cols)
		for i := range barr {
			barr[i] = float64(i)
		}
		b := NewDense(cols, nrhs, barr)

		x := lq.Solve(b)

		bProj := new(Dense)
		bProj.Mul(at, x)

		c.Check(b.EqualsApprox(bProj, 1e-13), check.Equals, true, check.Commentf("Test %v: A*X != B", test.name))

		qr := QR(DenseCopyOf(a))
		lambda := qr.Solve(DenseCopyOf(x))

		xCheck := new(Dense)
		xCheck.Mul(a, lambda)

		c.Check(xCheck.EqualsApprox(x, 1e-13), check.Equals, true,
			check.Commentf("Test %v: A*lambda != X", test.name))
	}
}
<commit_msg>Use concrete vars in place of new()<commit_after>// Copyright ©2013 The gonum 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 mat64

import (
	"math"
	check "launchpad.net/gocheck"
)

func isLowerTriangular(a *Dense) bool {
	rows, cols := a.Dims()
	for r := 0; r < rows; r++ {
		for c := r + 1; c < cols; c++ {
			if math.Abs(a.At(r, c)) > 1e-14 {
				return false
			}
		}
	}
	return true
}

func (s *S) TestLQD(c *check.C) {
	for _, test := range []struct {
		a    [][]float64
		name string
	}{
		{
			name: "Square",
			a: [][]float64{
				{1.3, 2.4, 8.9},
				{-2.6, 8.7, 9.1},
				{5.6, 5.8, 2.1},
			},
		},
		{
			name: "Skinny",
			a: [][]float64{
				{1.3, 2.4, 8.9},
				{-2.6, 8.7, 9.1},
				{5.6, 5.8, 2.1},
				{19.4, 5.2, -26.1},
			},
		},
		{
			name: "Id",
			a: [][]float64{
				{1, 0, 0},
				{0, 1, 0},
				{0, 0, 1},
			},
		},
		{
			name: "Id",
			a: [][]float64{
				{0, 0, 2},
				{0, 1, 0},
				{3, 0, 0},
			},
		},
		{
			name: "small",
			a: [][]float64{
				{1, 1},
				{1, 2},
			},
		},
	} {
		a := NewDense(flatten(test.a))

		at := new(Dense)
		at.TCopy(a)

		lq := LQ(DenseCopyOf(at))

		rows, cols := a.Dims()

		Q := NewDense(rows, cols, nil)
		for i := 0; i < cols; i++ {
			Q.Set(i, i, 1)
		}
		lq.ApplyQ(Q, true)
		l := lq.L()

		lt := NewDense(rows, cols, nil)
		ltview := *lt
		ltview.View(0, 0, cols, cols)
		ltview.TCopy(l)
		lq.ApplyQ(lt, true)

		c.Check(isOrthogonal(Q), check.Equals, true, check.Commentf("Test %v: Q not orthogonal", test.name))
		c.Check(a.EqualsApprox(lt, 1e-13), check.Equals, true, check.Commentf("Test %v: Q*R != A", test.name))
		c.Check(isLowerTriangular(l), check.Equals, true,
			check.Commentf("Test %v: L not lower triangular", test.name))

		nrhs := 2
		barr := make([]float64, nrhs*cols)
		for i := range barr {
			barr[i] = float64(i)
		}
		b := NewDense(cols, nrhs, barr)

		x := lq.Solve(b)

		var bProj Dense
		bProj.Mul(at, x)

		c.Check(bProj.EqualsApprox(b, 1e-13), check.Equals, true, check.Commentf("Test %v: A*X != B", test.name))

		qr := QR(DenseCopyOf(a))
		lambda := qr.Solve(DenseCopyOf(x))

		var xCheck Dense
		xCheck.Mul(a, lambda)

		c.Check(xCheck.EqualsApprox(x, 1e-13), check.Equals, true,
			check.Commentf("Test %v: A*lambda != X", test.name))
	}
}
<|endoftext|>


<commit_before>/*
Stack Messages

The Stack message Composer implementations capture a full stacktrace
information during message construction, and attach a message to that
trace. The string form of the message includes the package and file
name and line number of the last call site, while the Raw form of the
message includes the entire stack. Use with an appropriate sender to
capture the desired output.

All stack message constructors take a "skip" parameter which tells how
many stack frames to skip relative to the invocation of the
constructor. Skip values less than or equal to 0 become 1, and are
equal the call site of the constructor, use larger numbers if you're
wrapping these constructors in our own infrastructure.

In general Composers are lazy, and defer work until the message is
being sent; however, the stack Composers must capture the stack when
they're called rather than when they're sent to produce meaningful
data.
*/
package message

import (
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
	"time"
)

// types are internal, and exposed only via the composer interface.

type stackMessage struct {
	message string
	tagged  bool
	args    []interface{}
	trace   []StackFrame
	Base
}

// StackFrame
type StackFrame struct {
	Function string `bson:"function" json:"function" yaml:"function"`
	File     string `bson:"file" json:"file" yaml:"file"`
	Line     int    `bson:"line" json:"line" yaml:"line"`
}

// StackTrace structs are returned by the Raw method of the stackMessage type
type StackTrace struct {
	Message string       `bson:"message" json:"message" yaml:"message"`
	Frames  []StackFrame `bson:"frames" json:"frames" yaml:"frames"`
	Time    time.Time    `bson:"time" json:"time" yaml:"time"`
}

////////////////////////////////////////////////////////////////////////
//
// Constructors for stack frame messages.
//
////////////////////////////////////////////////////////////////////////

// NewStack builds a Composer implementation that captures the current
// stack trace with a single string message. Use the skip argument to
// skip frames if your embedding this in your own wrapper or wrappers.
func NewStack(skip int, message string) Composer {
	return &stackMessage{
		trace:   captureStack(skip),
		message: message,
	}
}

// NewStackLines returns a composer that builds a fmt.Println style
// message that also captures a stack trace. Use the skip argument to
// skip frames if your embedding this in your own wrapper or wrappers.
func NewStackLines(skip int, messages ...interface{}) Composer {
	return &stackMessage{
		trace: captureStack(skip),
		args:  messages,
	}
}

// NewStackFormatted returns a composer that builds a fmt.Printf style
// message that also captures a stack trace. Use the skip argument to
// skip frames if your embedding this in your own wrapper or wrappers.
func NewStackFormatted(skip int, message string, args ...interface{}) Composer {
	return &stackMessage{
		trace:   captureStack(skip),
		message: message,
		args:    args,
	}
}

////////////////////////////////////////////////////////////////////////
//
// Implementation of Composer methods not implemented by Base
//
////////////////////////////////////////////////////////////////////////

func (m *stackMessage) Loggable() bool { return m.message != "" || len(m.args) > 0 }
func (m *stackMessage) String() string {
	if len(m.args) > 0 && m.message == "" {
		m.message = fmt.Sprintln(append([]interface{}{m.getTag()}, m.args...))
		m.args = []interface{}{}
	} else if len(m.args) > 0 && m.message != "" {
		m.message = fmt.Sprintf(strings.Join([]string{m.getTag(), m.message}, " "), m.args...)
		m.args = []interface{}{}
	} else if !m.tagged {
		m.message = strings.Join([]string{m.getTag(), m.message}, " ")
	}

	return m.message
}

func (m *stackMessage) Raw() interface{} {
	_ = m.Collect()

	return StackTrace{
		Message: m.String(),
		Frames:  m.trace,
		Time:    m.Time,
	}
}

////////////////////////////////////////////////////////////////////////
//
// Internal Operations for Collecting and processing data.
//
////////////////////////////////////////////////////////////////////////

func captureStack(skip int) []StackFrame {
	if skip <= 0 {
		// don't recorded captureStack
		skip++
	}

	// captureStack is always called by a constructor, so we need
	// to bump it again
	skip++

	trace := []StackFrame{}

	for {
		pc, file, line, ok := runtime.Caller(skip)
		if !ok {
			break
		}

		trace = append(trace, StackFrame{
			Function: runtime.FuncForPC(pc).Name(),
			File:     file,
			Line:     line})

		skip++
	}

	return trace
}

func (m *stackMessage) getTag() string {
	if len(m.trace) >= 1 {
		frame := m.trace[0]

		// get the directory and filename
		dir, fileName := filepath.Split(frame.File)

		m.tagged = true

		return fmt.Sprintf("[%s:%d]", filepath.Join(filepath.Base(dir), fileName), frame.Line)
	}

	return ""
}
<commit_msg>fix docstring<commit_after>/*
Stack Messages

The Stack message Composer implementations capture a full stacktrace
information during message construction, and attach a message to that
trace. The string form of the message includes the package and file
name and line number of the last call site, while the Raw form of the
message includes the entire stack. Use with an appropriate sender to
capture the desired output.

All stack message constructors take a "skip" parameter which tells how
many stack frames to skip relative to the invocation of the
constructor. Skip values less than or equal to 0 become 1, and are
equal the call site of the constructor, use larger numbers if you're
wrapping these constructors in our own infrastructure.

In general Composers are lazy, and defer work until the message is
being sent; however, the stack Composers must capture the stack when
they're called rather than when they're sent to produce meaningful
data.
*/
package message

import (
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
	"time"
)

// types are internal, and exposed only via the composer interface.

type stackMessage struct {
	message string
	tagged  bool
	args    []interface{}
	trace   []StackFrame
	Base
}

// StackFrame captures a single item in a stack trace, and is used
// internally and in the StackTrace output.
type StackFrame struct {
	Function string `bson:"function" json:"function" yaml:"function"`
	File     string `bson:"file" json:"file" yaml:"file"`
	Line     int    `bson:"line" json:"line" yaml:"line"`
}

// StackTrace structs are returned by the Raw method of the stackMessage type
type StackTrace struct {
	Message string       `bson:"message" json:"message" yaml:"message"`
	Frames  []StackFrame `bson:"frames" json:"frames" yaml:"frames"`
	Time    time.Time    `bson:"time" json:"time" yaml:"time"`
}

////////////////////////////////////////////////////////////////////////
//
// Constructors for stack frame messages.
//
////////////////////////////////////////////////////////////////////////

// NewStack builds a Composer implementation that captures the current
// stack trace with a single string message. Use the skip argument to
// skip frames if your embedding this in your own wrapper or wrappers.
func NewStack(skip int, message string) Composer {
	return &stackMessage{
		trace:   captureStack(skip),
		message: message,
	}
}

// NewStackLines returns a composer that builds a fmt.Println style
// message that also captures a stack trace. Use the skip argument to
// skip frames if your embedding this in your own wrapper or wrappers.
func NewStackLines(skip int, messages ...interface{}) Composer {
	return &stackMessage{
		trace: captureStack(skip),
		args:  messages,
	}
}

// NewStackFormatted returns a composer that builds a fmt.Printf style
// message that also captures a stack trace. Use the skip argument to
// skip frames if your embedding this in your own wrapper or wrappers.
func NewStackFormatted(skip int, message string, args ...interface{}) Composer {
	return &stackMessage{
		trace:   captureStack(skip),
		message: message,
		args:    args,
	}
}

////////////////////////////////////////////////////////////////////////
//
// Implementation of Composer methods not implemented by Base
//
////////////////////////////////////////////////////////////////////////

func (m *stackMessage) Loggable() bool { return m.message != "" || len(m.args) > 0 }
func (m *stackMessage) String() string {
	if len(m.args) > 0 && m.message == "" {
		m.message = fmt.Sprintln(append([]interface{}{m.getTag()}, m.args...))
		m.args = []interface{}{}
	} else if len(m.args) > 0 && m.message != "" {
		m.message = fmt.Sprintf(strings.Join([]string{m.getTag(), m.message}, " "), m.args...)
		m.args = []interface{}{}
	} else if !m.tagged {
		m.message = strings.Join([]string{m.getTag(), m.message}, " ")
	}

	return m.message
}

func (m *stackMessage) Raw() interface{} {
	_ = m.Collect()

	return StackTrace{
		Message: m.String(),
		Frames:  m.trace,
		Time:    m.Time,
	}
}

////////////////////////////////////////////////////////////////////////
//
// Internal Operations for Collecting and processing data.
//
////////////////////////////////////////////////////////////////////////

func captureStack(skip int) []StackFrame {
	if skip <= 0 {
		// don't recorded captureStack
		skip++
	}

	// captureStack is always called by a constructor, so we need
	// to bump it again
	skip++

	trace := []StackFrame{}

	for {
		pc, file, line, ok := runtime.Caller(skip)
		if !ok {
			break
		}

		trace = append(trace, StackFrame{
			Function: runtime.FuncForPC(pc).Name(),
			File:     file,
			Line:     line})

		skip++
	}

	return trace
}

func (m *stackMessage) getTag() string {
	if len(m.trace) >= 1 {
		frame := m.trace[0]

		// get the directory and filename
		dir, fileName := filepath.Split(frame.File)

		m.tagged = true

		return fmt.Sprintf("[%s:%d]", filepath.Join(filepath.Base(dir), fileName), frame.Line)
	}

	return ""
}
<|endoftext|>


<commit_before>// Copyright 2018 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 util

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"

	multierror "github.com/hashicorp/go-multierror"
)

const (
	statCdsVersion    = "cluster_manager.cds.version"
	statLdsVersion    = "listener_manager.lds.version"
	statServerState   = "server.state"
	versionStatsRegex = "^(cluster_manager.cds|listener_manager.lds).version$"
)

type stat struct {
	name  string
	value *uint64
	found bool
}

// Stats contains values of interest from a poll of Envoy stats.
type Stats struct {
	// Hash of the contents from the last successful cluster update.
	CDSVersion uint64
	// Hash of the contents from the last successful listener update.
	LDSVersion uint64
	// Server State of Envoy.
	ServerState uint64
}

// String representation of the Stats.
func (s *Stats) String() string {
	cdsStatus := "Not Received"
	ldsStatus := "Not Received"
	if s.CDSVersion > 0 {
		cdsStatus = "Received"
	}
	if s.LDSVersion > 0 {
		ldsStatus = "Received"
	}
	return fmt.Sprintf("cds update: %s ,lds update: %s", cdsStatus, ldsStatus)
}

// GetServerState returns the current Envoy state by checking the "server.state" stat.
func GetServerState(localHostAddr string, adminPort uint16) (*uint64, error) {
	stats, err := doHTTPGet(fmt.Sprintf("http://%s:%d/stats?usedonly&filter=%s", localHostAddr, adminPort, statServerState))
	if err != nil {
		return nil, err
	}

	s := &Stats{}
	allStats := []*stat{
		{name: statServerState, value: &s.ServerState},
	}
	if err := parseStats(stats, allStats); err != nil {
		return nil, err
	}

	return &s.ServerState, nil
}

// GetVersionStats returns the version stats for CDS and LDS.
func GetVersionStats(localHostAddr string, adminPort uint16) (*Stats, error) {
	stats, err := doHTTPGet(fmt.Sprintf("http://%s:%d/stats?usedonly&filter=%s", localHostAddr, adminPort, versionStatsRegex))
	if err != nil {
		return nil, err
	}

	s := &Stats{}
	allStats := []*stat{
		{name: statCdsVersion, value: &s.CDSVersion},
		{name: statLdsVersion, value: &s.LDSVersion},
	}
	if err := parseStats(stats, allStats); err != nil {
		return nil, err
	}

	return s, nil
}

func parseStats(input *bytes.Buffer, stats []*stat) (err error) {
	for input.Len() > 0 {
		line, _ := input.ReadString('\n')
		for _, stat := range stats {
			if e := stat.processLine(line); e != nil {
				err = multierror.Append(err, e)
			}
		}
	}
	for _, stat := range stats {
		if !stat.found {
			*stat.value = 0
		}
	}
	return
}

func (s *stat) processLine(line string) error {
	if !s.found && strings.HasPrefix(line, s.name) {
		s.found = true

		parts := strings.Split(line, ":")
		if len(parts) != 2 {
			return fmt.Errorf("envoy stat %s missing separator. line:%s", s.name, line)
		}

		val, err := strconv.ParseUint(strings.TrimSpace(parts[1]), 10, 64)
		if err != nil {
			return fmt.Errorf("failed parsing Envoy stat %s (error: %s) line: %s", s.name, err.Error(), line)
		}

		*s.value = val
	}

	return nil
}
<commit_msg>Fix minor type in pilot agent logs (#18609)<commit_after>// Copyright 2018 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 util

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"

	multierror "github.com/hashicorp/go-multierror"
)

const (
	statCdsVersion    = "cluster_manager.cds.version"
	statLdsVersion    = "listener_manager.lds.version"
	statServerState   = "server.state"
	versionStatsRegex = "^(cluster_manager.cds|listener_manager.lds).version$"
)

type stat struct {
	name  string
	value *uint64
	found bool
}

// Stats contains values of interest from a poll of Envoy stats.
type Stats struct {
	// Hash of the contents from the last successful cluster update.
	CDSVersion uint64
	// Hash of the contents from the last successful listener update.
	LDSVersion uint64
	// Server State of Envoy.
	ServerState uint64
}

// String representation of the Stats.
func (s *Stats) String() string {
	cdsStatus := "Not Received"
	ldsStatus := "Not Received"
	if s.CDSVersion > 0 {
		cdsStatus = "Received"
	}
	if s.LDSVersion > 0 {
		ldsStatus = "Received"
	}
	return fmt.Sprintf("cds update: %s, lds update: %s", cdsStatus, ldsStatus)
}

// GetServerState returns the current Envoy state by checking the "server.state" stat.
func GetServerState(localHostAddr string, adminPort uint16) (*uint64, error) {
	stats, err := doHTTPGet(fmt.Sprintf("http://%s:%d/stats?usedonly&filter=%s", localHostAddr, adminPort, statServerState))
	if err != nil {
		return nil, err
	}

	s := &Stats{}
	allStats := []*stat{
		{name: statServerState, value: &s.ServerState},
	}
	if err := parseStats(stats, allStats); err != nil {
		return nil, err
	}

	return &s.ServerState, nil
}

// GetVersionStats returns the version stats for CDS and LDS.
func GetVersionStats(localHostAddr string, adminPort uint16) (*Stats, error) {
	stats, err := doHTTPGet(fmt.Sprintf("http://%s:%d/stats?usedonly&filter=%s", localHostAddr, adminPort, versionStatsRegex))
	if err != nil {
		return nil, err
	}

	s := &Stats{}
	allStats := []*stat{
		{name: statCdsVersion, value: &s.CDSVersion},
		{name: statLdsVersion, value: &s.LDSVersion},
	}
	if err := parseStats(stats, allStats); err != nil {
		return nil, err
	}

	return s, nil
}

func parseStats(input *bytes.Buffer, stats []*stat) (err error) {
	for input.Len() > 0 {
		line, _ := input.ReadString('\n')
		for _, stat := range stats {
			if e := stat.processLine(line); e != nil {
				err = multierror.Append(err, e)
			}
		}
	}
	for _, stat := range stats {
		if !stat.found {
			*stat.value = 0
		}
	}
	return
}

func (s *stat) processLine(line string) error {
	if !s.found && strings.HasPrefix(line, s.name) {
		s.found = true

		parts := strings.Split(line, ":")
		if len(parts) != 2 {
			return fmt.Errorf("envoy stat %s missing separator. line:%s", s.name, line)
		}

		val, err := strconv.ParseUint(strings.TrimSpace(parts[1]), 10, 64)
		if err != nil {
			return fmt.Errorf("failed parsing Envoy stat %s (error: %s) line: %s", s.name, err.Error(), line)
		}

		*s.value = val
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package statefulset

import (
	"fmt"
	"reflect"
	"sort"
	"time"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
	clientv1 "k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"k8s.io/client-go/util/workqueue"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/v1"
	apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
	"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
	appsinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions/apps/v1beta1"
	coreinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions/core/v1"
	appslisters "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1"
	corelisters "k8s.io/kubernetes/pkg/client/listers/core/v1"
	"k8s.io/kubernetes/pkg/controller"

	"github.com/golang/glog"
)

const (
	// period to relist statefulsets and verify pets
	statefulSetResyncPeriod = 30 * time.Second
)

// StatefulSetController controls statefulsets.
type StatefulSetController struct {
	// client interface
	kubeClient clientset.Interface
	// control returns an interface capable of syncing a stateful set.
	// Abstracted out for testing.
	control StatefulSetControlInterface
	// podLister is able to list/get pods from a shared informer's store
	podLister corelisters.PodLister
	// podListerSynced returns true if the pod shared informer has synced at least once
	podListerSynced cache.InformerSynced
	// setLister is able to list/get stateful sets from a shared informer's store
	setLister appslisters.StatefulSetLister
	// setListerSynced returns true if the stateful set shared informer has synced at least once
	setListerSynced cache.InformerSynced
	// StatefulSets that need to be synced.
	queue workqueue.RateLimitingInterface
}

// NewStatefulSetController creates a new statefulset controller.
func NewStatefulSetController(
	podInformer coreinformers.PodInformer,
	setInformer appsinformers.StatefulSetInformer,
	pvcInformer coreinformers.PersistentVolumeClaimInformer,
	kubeClient clientset.Interface,
) *StatefulSetController {
	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartLogging(glog.Infof)
	eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: v1core.New(kubeClient.Core().RESTClient()).Events("")})
	recorder := eventBroadcaster.NewRecorder(api.Scheme, clientv1.EventSource{Component: "statefulset"})

	ssc := &StatefulSetController{
		kubeClient: kubeClient,
		control: NewDefaultStatefulSetControl(
			NewRealStatefulPodControl(
				kubeClient,
				setInformer.Lister(),
				podInformer.Lister(),
				pvcInformer.Lister(),
				recorder,
			),
		),
		queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "statefulset"),
	}

	podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		// lookup the statefulset and enqueue
		AddFunc: ssc.addPod,
		// lookup current and old statefulset if labels changed
		UpdateFunc: ssc.updatePod,
		// lookup statefulset accounting for deletion tombstones
		DeleteFunc: ssc.deletePod,
	})
	ssc.podLister = podInformer.Lister()
	ssc.podListerSynced = podInformer.Informer().HasSynced

	setInformer.Informer().AddEventHandlerWithResyncPeriod(
		cache.ResourceEventHandlerFuncs{
			AddFunc: ssc.enqueueStatefulSet,
			UpdateFunc: func(old, cur interface{}) {
				oldPS := old.(*apps.StatefulSet)
				curPS := cur.(*apps.StatefulSet)
				if oldPS.Status.Replicas != curPS.Status.Replicas {
					glog.V(4).Infof("Observed updated replica count for StatefulSet: %v, %d->%d", curPS.Name, oldPS.Status.Replicas, curPS.Status.Replicas)
				}
				ssc.enqueueStatefulSet(cur)
			},
			DeleteFunc: ssc.enqueueStatefulSet,
		},
		statefulSetResyncPeriod,
	)
	ssc.setLister = setInformer.Lister()
	ssc.setListerSynced = setInformer.Informer().HasSynced

	// TODO: Watch volumes
	return ssc
}

// Run runs the statefulset controller.
func (ssc *StatefulSetController) Run(workers int, stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	defer ssc.queue.ShutDown()

	glog.Infof("Starting statefulset controller")

	if !cache.WaitForCacheSync(stopCh, ssc.podListerSynced, ssc.setListerSynced) {
		utilruntime.HandleError(fmt.Errorf("timed out waiting for caches to sync"))
		return
	}

	for i := 0; i < workers; i++ {
		go wait.Until(ssc.worker, time.Second, stopCh)
	}

	<-stopCh
	glog.Infof("Shutting down statefulset controller")
}

// addPod adds the statefulset for the pod to the sync queue
func (ssc *StatefulSetController) addPod(obj interface{}) {
	pod := obj.(*v1.Pod)
	glog.V(4).Infof("Pod %s created, labels: %+v", pod.Name, pod.Labels)
	set := ssc.getStatefulSetForPod(pod)
	if set == nil {
		return
	}
	ssc.enqueueStatefulSet(set)
}

// updatePod adds the statefulset for the current and old pods to the sync queue.
// If the labels of the pod didn't change, this method enqueues a single statefulset.
func (ssc *StatefulSetController) updatePod(old, cur interface{}) {
	curPod := cur.(*v1.Pod)
	oldPod := old.(*v1.Pod)
	if curPod.ResourceVersion == oldPod.ResourceVersion {
		// Periodic resync will send update events for all known pods.
		// Two different versions of the same pod will always have different RVs.
		return
	}
	set := ssc.getStatefulSetForPod(curPod)
	if set == nil {
		return
	}
	ssc.enqueueStatefulSet(set)
	// TODO will we need this going forward with controller ref impl?
	if !reflect.DeepEqual(curPod.Labels, oldPod.Labels) {
		if oldSet := ssc.getStatefulSetForPod(oldPod); oldSet != nil {
			ssc.enqueueStatefulSet(oldSet)
		}
	}
}

// deletePod enqueues the statefulset for the pod accounting for deletion tombstones.
func (ssc *StatefulSetController) deletePod(obj interface{}) {
	pod, ok := obj.(*v1.Pod)

	// When a delete is dropped, the relist will notice a pod in the store not
	// in the list, leading to the insertion of a tombstone object which contains
	// the deleted key/value. Note that this value might be stale. If the pod
	// changed labels the new StatefulSet will not be woken up till the periodic resync.
	if !ok {
		tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
		if !ok {
			utilruntime.HandleError(fmt.Errorf("couldn't get object from tombstone %+v", obj))
			return
		}
		pod, ok = tombstone.Obj.(*v1.Pod)
		if !ok {
			utilruntime.HandleError(fmt.Errorf("tombstone contained object that is not a pod %+v", obj))
			return
		}
	}
	glog.V(4).Infof("Pod %s/%s deleted through %v.", pod.Namespace, pod.Name, utilruntime.GetCaller())
	if set := ssc.getStatefulSetForPod(pod); set != nil {
		ssc.enqueueStatefulSet(set)
	}
}

// getPodsForStatefulSets returns the pods that match the selectors of the given statefulset.
func (ssc *StatefulSetController) getPodsForStatefulSet(set *apps.StatefulSet) ([]*v1.Pod, error) {
	sel, err := metav1.LabelSelectorAsSelector(set.Spec.Selector)
	if err != nil {
		return []*v1.Pod{}, err
	}
	return ssc.podLister.Pods(set.Namespace).List(sel)
}

// getStatefulSetForPod returns the StatefulSet managing the given pod.
func (ssc *StatefulSetController) getStatefulSetForPod(pod *v1.Pod) *apps.StatefulSet {
	sets, err := ssc.setLister.GetPodStatefulSets(pod)
	if err != nil {
		glog.V(4).Infof("No StatefulSets found for pod %v, StatefulSet controller will avoid syncing", pod.Name)
		return nil
	}
	// More than one set is selecting the same Pod
	if len(sets) > 1 {
		utilruntime.HandleError(
			fmt.Errorf(
				"user error: more than one StatefulSet is selecting pods with labels: %+v",
				pod.Labels))
		// The timestamp sort should not be necessary because we will enforce the CreatedBy requirement by
		// name
		sort.Sort(overlappingStatefulSets(sets))
		// return the first created set for which pod is a member
		for i := range sets {
			if isMemberOf(sets[i], pod) {
				return sets[i]
			}
		}
		glog.V(4).Infof("No StatefulSets found for pod %v, StatefulSet controller will avoid syncing", pod.Name)
		return nil
	}
	return sets[0]

}

// enqueueStatefulSet enqueues the given statefulset in the work queue.
func (ssc *StatefulSetController) enqueueStatefulSet(obj interface{}) {
	key, err := controller.KeyFunc(obj)
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("Cound't get key for object %+v: %v", obj, err))
		return
	}
	ssc.queue.Add(key)
}

// processNextWorkItem dequeues items, processes them, and marks them done. It enforces that the syncHandler is never
// invoked concurrently with the same key.
func (ssc *StatefulSetController) processNextWorkItem() bool {
	key, quit := ssc.queue.Get()
	if quit {
		return false
	}
	defer ssc.queue.Done(key)
	if err := ssc.sync(key.(string)); err != nil {
		utilruntime.HandleError(fmt.Errorf("Error syncing StatefulSet %v, requeuing: %v", key.(string), err))
		ssc.queue.AddRateLimited(key)
	} else {
		ssc.queue.Forget(key)
	}
	return true
}

// worker runs a worker goroutine that invokes processNextWorkItem until the the controller's queue is closed
func (ssc *StatefulSetController) worker() {
	for ssc.processNextWorkItem() {
	}
}

// sync syncs the given statefulset.
func (ssc *StatefulSetController) sync(key string) error {
	startTime := time.Now()
	defer func() {
		glog.V(4).Infof("Finished syncing statefulset %q (%v)", key, time.Now().Sub(startTime))
	}()

	namespace, name, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		return err
	}
	set, err := ssc.setLister.StatefulSets(namespace).Get(name)
	if errors.IsNotFound(err) {
		glog.Infof("StatefulSet has been deleted %v", key)
		return nil
	}
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("Unable to retrieve StatefulSet %v from store: %v", key, err))
		return err
	}

	pods, err := ssc.getPodsForStatefulSet(set)
	if err != nil {
		return err
	}

	return ssc.syncStatefulSet(set, pods)
}

// syncStatefulSet syncs a tuple of (statefulset, []*v1.Pod).
func (ssc *StatefulSetController) syncStatefulSet(set *apps.StatefulSet, pods []*v1.Pod) error {
	glog.V(2).Infof("Syncing StatefulSet %v/%v with %d pods", set.Namespace, set.Name, len(pods))
	if err := ssc.control.UpdateStatefulSet(set, pods); err != nil {
		glog.V(2).Infof("Error syncing StatefulSet %s/%s with %d pods : %s", set.Namespace, set.Name, err)
		return err
	}
	glog.V(2).Infof("Succesfully synced StatefulSet %s/%s successful", set.Namespace, set.Name)
	return nil
}
<commit_msg>statefulset: wait for pvc cache sync<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package statefulset

import (
	"fmt"
	"reflect"
	"sort"
	"time"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
	clientv1 "k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"k8s.io/client-go/util/workqueue"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/v1"
	apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
	"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
	appsinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions/apps/v1beta1"
	coreinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions/core/v1"
	appslisters "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1"
	corelisters "k8s.io/kubernetes/pkg/client/listers/core/v1"
	"k8s.io/kubernetes/pkg/controller"

	"github.com/golang/glog"
)

const (
	// period to relist statefulsets and verify pets
	statefulSetResyncPeriod = 30 * time.Second
)

// StatefulSetController controls statefulsets.
type StatefulSetController struct {
	// client interface
	kubeClient clientset.Interface
	// control returns an interface capable of syncing a stateful set.
	// Abstracted out for testing.
	control StatefulSetControlInterface
	// podLister is able to list/get pods from a shared informer's store
	podLister corelisters.PodLister
	// podListerSynced returns true if the pod shared informer has synced at least once
	podListerSynced cache.InformerSynced
	// setLister is able to list/get stateful sets from a shared informer's store
	setLister appslisters.StatefulSetLister
	// setListerSynced returns true if the stateful set shared informer has synced at least once
	setListerSynced cache.InformerSynced
	// pvcListerSynced returns true if the pvc shared informer has synced at least once
	pvcListerSynced cache.InformerSynced
	// StatefulSets that need to be synced.
	queue workqueue.RateLimitingInterface
}

// NewStatefulSetController creates a new statefulset controller.
func NewStatefulSetController(
	podInformer coreinformers.PodInformer,
	setInformer appsinformers.StatefulSetInformer,
	pvcInformer coreinformers.PersistentVolumeClaimInformer,
	kubeClient clientset.Interface,
) *StatefulSetController {
	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartLogging(glog.Infof)
	eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: v1core.New(kubeClient.Core().RESTClient()).Events("")})
	recorder := eventBroadcaster.NewRecorder(api.Scheme, clientv1.EventSource{Component: "statefulset"})

	ssc := &StatefulSetController{
		kubeClient: kubeClient,
		control: NewDefaultStatefulSetControl(
			NewRealStatefulPodControl(
				kubeClient,
				setInformer.Lister(),
				podInformer.Lister(),
				pvcInformer.Lister(),
				recorder,
			),
		),
		pvcListerSynced: pvcInformer.Informer().HasSynced,
		queue:           workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "statefulset"),
	}

	podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		// lookup the statefulset and enqueue
		AddFunc: ssc.addPod,
		// lookup current and old statefulset if labels changed
		UpdateFunc: ssc.updatePod,
		// lookup statefulset accounting for deletion tombstones
		DeleteFunc: ssc.deletePod,
	})
	ssc.podLister = podInformer.Lister()
	ssc.podListerSynced = podInformer.Informer().HasSynced

	setInformer.Informer().AddEventHandlerWithResyncPeriod(
		cache.ResourceEventHandlerFuncs{
			AddFunc: ssc.enqueueStatefulSet,
			UpdateFunc: func(old, cur interface{}) {
				oldPS := old.(*apps.StatefulSet)
				curPS := cur.(*apps.StatefulSet)
				if oldPS.Status.Replicas != curPS.Status.Replicas {
					glog.V(4).Infof("Observed updated replica count for StatefulSet: %v, %d->%d", curPS.Name, oldPS.Status.Replicas, curPS.Status.Replicas)
				}
				ssc.enqueueStatefulSet(cur)
			},
			DeleteFunc: ssc.enqueueStatefulSet,
		},
		statefulSetResyncPeriod,
	)
	ssc.setLister = setInformer.Lister()
	ssc.setListerSynced = setInformer.Informer().HasSynced

	// TODO: Watch volumes
	return ssc
}

// Run runs the statefulset controller.
func (ssc *StatefulSetController) Run(workers int, stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	defer ssc.queue.ShutDown()

	glog.Infof("Starting statefulset controller")

	if !cache.WaitForCacheSync(stopCh, ssc.podListerSynced, ssc.setListerSynced, ssc.pvcListerSynced) {
		utilruntime.HandleError(fmt.Errorf("timed out waiting for caches to sync"))
		return
	}

	for i := 0; i < workers; i++ {
		go wait.Until(ssc.worker, time.Second, stopCh)
	}

	<-stopCh
	glog.Infof("Shutting down statefulset controller")
}

// addPod adds the statefulset for the pod to the sync queue
func (ssc *StatefulSetController) addPod(obj interface{}) {
	pod := obj.(*v1.Pod)
	glog.V(4).Infof("Pod %s created, labels: %+v", pod.Name, pod.Labels)
	set := ssc.getStatefulSetForPod(pod)
	if set == nil {
		return
	}
	ssc.enqueueStatefulSet(set)
}

// updatePod adds the statefulset for the current and old pods to the sync queue.
// If the labels of the pod didn't change, this method enqueues a single statefulset.
func (ssc *StatefulSetController) updatePod(old, cur interface{}) {
	curPod := cur.(*v1.Pod)
	oldPod := old.(*v1.Pod)
	if curPod.ResourceVersion == oldPod.ResourceVersion {
		// Periodic resync will send update events for all known pods.
		// Two different versions of the same pod will always have different RVs.
		return
	}
	set := ssc.getStatefulSetForPod(curPod)
	if set == nil {
		return
	}
	ssc.enqueueStatefulSet(set)
	// TODO will we need this going forward with controller ref impl?
	if !reflect.DeepEqual(curPod.Labels, oldPod.Labels) {
		if oldSet := ssc.getStatefulSetForPod(oldPod); oldSet != nil {
			ssc.enqueueStatefulSet(oldSet)
		}
	}
}

// deletePod enqueues the statefulset for the pod accounting for deletion tombstones.
func (ssc *StatefulSetController) deletePod(obj interface{}) {
	pod, ok := obj.(*v1.Pod)

	// When a delete is dropped, the relist will notice a pod in the store not
	// in the list, leading to the insertion of a tombstone object which contains
	// the deleted key/value. Note that this value might be stale. If the pod
	// changed labels the new StatefulSet will not be woken up till the periodic resync.
	if !ok {
		tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
		if !ok {
			utilruntime.HandleError(fmt.Errorf("couldn't get object from tombstone %+v", obj))
			return
		}
		pod, ok = tombstone.Obj.(*v1.Pod)
		if !ok {
			utilruntime.HandleError(fmt.Errorf("tombstone contained object that is not a pod %+v", obj))
			return
		}
	}
	glog.V(4).Infof("Pod %s/%s deleted through %v.", pod.Namespace, pod.Name, utilruntime.GetCaller())
	if set := ssc.getStatefulSetForPod(pod); set != nil {
		ssc.enqueueStatefulSet(set)
	}
}

// getPodsForStatefulSets returns the pods that match the selectors of the given statefulset.
func (ssc *StatefulSetController) getPodsForStatefulSet(set *apps.StatefulSet) ([]*v1.Pod, error) {
	sel, err := metav1.LabelSelectorAsSelector(set.Spec.Selector)
	if err != nil {
		return []*v1.Pod{}, err
	}
	return ssc.podLister.Pods(set.Namespace).List(sel)
}

// getStatefulSetForPod returns the StatefulSet managing the given pod.
func (ssc *StatefulSetController) getStatefulSetForPod(pod *v1.Pod) *apps.StatefulSet {
	sets, err := ssc.setLister.GetPodStatefulSets(pod)
	if err != nil {
		glog.V(4).Infof("No StatefulSets found for pod %v, StatefulSet controller will avoid syncing", pod.Name)
		return nil
	}
	// More than one set is selecting the same Pod
	if len(sets) > 1 {
		utilruntime.HandleError(
			fmt.Errorf(
				"user error: more than one StatefulSet is selecting pods with labels: %+v",
				pod.Labels))
		// The timestamp sort should not be necessary because we will enforce the CreatedBy requirement by
		// name
		sort.Sort(overlappingStatefulSets(sets))
		// return the first created set for which pod is a member
		for i := range sets {
			if isMemberOf(sets[i], pod) {
				return sets[i]
			}
		}
		glog.V(4).Infof("No StatefulSets found for pod %v, StatefulSet controller will avoid syncing", pod.Name)
		return nil
	}
	return sets[0]

}

// enqueueStatefulSet enqueues the given statefulset in the work queue.
func (ssc *StatefulSetController) enqueueStatefulSet(obj interface{}) {
	key, err := controller.KeyFunc(obj)
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("Cound't get key for object %+v: %v", obj, err))
		return
	}
	ssc.queue.Add(key)
}

// processNextWorkItem dequeues items, processes them, and marks them done. It enforces that the syncHandler is never
// invoked concurrently with the same key.
func (ssc *StatefulSetController) processNextWorkItem() bool {
	key, quit := ssc.queue.Get()
	if quit {
		return false
	}
	defer ssc.queue.Done(key)
	if err := ssc.sync(key.(string)); err != nil {
		utilruntime.HandleError(fmt.Errorf("Error syncing StatefulSet %v, requeuing: %v", key.(string), err))
		ssc.queue.AddRateLimited(key)
	} else {
		ssc.queue.Forget(key)
	}
	return true
}

// worker runs a worker goroutine that invokes processNextWorkItem until the the controller's queue is closed
func (ssc *StatefulSetController) worker() {
	for ssc.processNextWorkItem() {
	}
}

// sync syncs the given statefulset.
func (ssc *StatefulSetController) sync(key string) error {
	startTime := time.Now()
	defer func() {
		glog.V(4).Infof("Finished syncing statefulset %q (%v)", key, time.Now().Sub(startTime))
	}()

	namespace, name, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		return err
	}
	set, err := ssc.setLister.StatefulSets(namespace).Get(name)
	if errors.IsNotFound(err) {
		glog.Infof("StatefulSet has been deleted %v", key)
		return nil
	}
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("Unable to retrieve StatefulSet %v from store: %v", key, err))
		return err
	}

	pods, err := ssc.getPodsForStatefulSet(set)
	if err != nil {
		return err
	}

	return ssc.syncStatefulSet(set, pods)
}

// syncStatefulSet syncs a tuple of (statefulset, []*v1.Pod).
func (ssc *StatefulSetController) syncStatefulSet(set *apps.StatefulSet, pods []*v1.Pod) error {
	glog.V(2).Infof("Syncing StatefulSet %v/%v with %d pods", set.Namespace, set.Name, len(pods))
	if err := ssc.control.UpdateStatefulSet(set, pods); err != nil {
		glog.V(2).Infof("Error syncing StatefulSet %s/%s with %d pods : %s", set.Namespace, set.Name, err)
		return err
	}
	glog.V(2).Infof("Succesfully synced StatefulSet %s/%s successful", set.Namespace, set.Name)
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2020 Authors of Cilium
//
// 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.

// +build privileged_tests

package linuxrouting

import (
	"net"
	"testing"

	"github.com/cilium/cilium/pkg/datapath/linux/linux_defaults"
	"github.com/cilium/cilium/pkg/datapath/linux/route"
	"github.com/cilium/cilium/pkg/mac"
	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netns"

	. "gopkg.in/check.v1"
)

func Test(t *testing.T) {
	TestingT(t)
}

type LinuxRoutingSuite struct{}

var _ = Suite(&LinuxRoutingSuite{})

func (e *LinuxRoutingSuite) TestConfigure(c *C) {
	currentNS, err := netns.Get()
	c.Assert(err, IsNil)
	defer func() {
		c.Assert(netns.Set(currentNS), IsNil)
		c.Log("Set back to previous network ns")
	}()

	ip, ri := getFakes(c)
	masterMAC := ri.MasterIfMAC
	runFuncInNetNS(c, func() { runConfigureThenDelete(c, ri, ip, 1500, false) }, masterMAC)
	runFuncInNetNS(c, func() { runConfigureThenDelete(c, ri, ip, 1500, true) }, masterMAC)
}

func (e *LinuxRoutingSuite) TestConfigureRoutewithIncompatibleIP(c *C) {
	_, ri := getFakes(c)
	ipv6 := net.ParseIP("fd00::2").To16()
	c.Assert(ipv6, NotNil)
	err := ri.Configure(ipv6, 1500, true)
	c.Assert(err, NotNil)
	c.Assert(err, ErrorMatches, "IP not compatible")
}

func (e *LinuxRoutingSuite) TestDeleteRoutewithIncompatibleIP(c *C) {
	ipv6 := net.ParseIP("fd00::2").To16()
	c.Assert(ipv6, NotNil)
	err := Delete(ipv6)
	c.Assert(err, NotNil)
	c.Assert(err, ErrorMatches, "IP not compatible")
}

func (e *LinuxRoutingSuite) TestDelete(c *C) {
	fakeIP, fakeRoutingInfo := getFakes(c)
	masterMAC := fakeRoutingInfo.MasterIfMAC

	tests := []struct {
		name    string
		preRun  func() net.IP
		wantErr bool
	}{
		{
			name: "valid IP addr matching rules",
			preRun: func() net.IP {
				runConfigure(c, fakeRoutingInfo, fakeIP, 1500, false)
				return fakeIP
			},
			wantErr: false,
		},
		{
			name: "IP addr doesn't match rules",
			preRun: func() net.IP {
				ip := net.ParseIP("192.168.2.233")
				c.Assert(ip, NotNil)

				runConfigure(c, fakeRoutingInfo, fakeIP, 1500, false)
				return ip
			},
			wantErr: true,
		},
		{
			name: "IP addr matches more than number expected",
			preRun: func() net.IP {
				ip := net.ParseIP("192.168.2.233")
				c.Assert(ip, NotNil)

				runConfigure(c, fakeRoutingInfo, ip, 1500, false)

				// Find interface ingress rules so that we can create a
				// near-duplicate.
				rules, err := route.ListRules(netlink.FAMILY_V4, &route.Rule{
					Priority: linux_defaults.RulePriorityIngress,
				})
				c.Assert(err, IsNil)
				c.Assert(len(rules), Not(Equals), 0)

				// Insert almost duplicate rule; the reason for this is to
				// trigger an error while trying to delete the ingress rule. We
				// are setting the Src because ingress rules don't have
				// one (only Dst), thus we set Src to create a near-duplicate.
				r := rules[0]
				r.Src = &net.IPNet{IP: fakeIP, Mask: net.CIDRMask(32, 32)}
				c.Assert(netlink.RuleAdd(&r), IsNil)

				return ip
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		c.Log(tt.name)
		runFuncInNetNS(c, func() {
			ip := tt.preRun()
			err := Delete(ip)
			c.Assert((err != nil), Equals, tt.wantErr)
		}, masterMAC)
	}
}

func runFuncInNetNS(c *C, run func(), macAddr mac.MAC) {
	networkNS, err := netns.New()
	c.Assert(err, IsNil)
	c.Logf("Inside new network ns %v", networkNS.UniqueId())
	defer func() {
		uid := networkNS.UniqueId()
		c.Assert(networkNS.Close(), IsNil)
		c.Logf("Closed new network ns %v", uid)
	}()

	ifaceCleanup := createDummyDevice(c, macAddr)
	defer ifaceCleanup()

	run()
}

func runConfigureThenDelete(c *C, ri RoutingInfo, ip net.IP, mtu int, masq bool) {
	// Create rules and routes
	beforeCreationRules, beforeCreationRoutes := listRulesAndRoutes(c, netlink.FAMILY_V4)
	runConfigure(c, ri, ip, mtu, masq)
	afterCreationRules, afterCreationRoutes := listRulesAndRoutes(c, netlink.FAMILY_V4)

	c.Assert(len(afterCreationRules), Not(Equals), 0)
	c.Assert(len(afterCreationRoutes), Not(Equals), 0)
	c.Assert(len(beforeCreationRules), Not(Equals), len(afterCreationRules))
	c.Assert(len(beforeCreationRoutes), Not(Equals), len(afterCreationRoutes))

	// Delete rules and routes
	beforeDeletionRules, beforeDeletionRoutes := listRulesAndRoutes(c, netlink.FAMILY_V4)
	runDelete(c, ip)
	afterDeletionRules, afterDeletionRoutes := listRulesAndRoutes(c, netlink.FAMILY_V4)

	c.Assert(len(beforeDeletionRules), Not(Equals), len(afterDeletionRules))
	c.Assert(len(beforeDeletionRoutes), Not(Equals), len(afterDeletionRoutes))
	c.Assert(len(afterDeletionRules), Equals, len(beforeCreationRules))
	c.Assert(len(afterDeletionRoutes), Equals, len(beforeCreationRoutes))
}

func runConfigure(c *C, ri RoutingInfo, ip net.IP, mtu int, masq bool) {
	err := ri.Configure(ip, mtu, masq)
	c.Assert(err, IsNil)
}

func runDelete(c *C, ip net.IP) {
	err := Delete(ip)
	c.Assert(err, IsNil)
}

// listRulesAndRoutes returns all rules and routes configured on the machine
// this test is running on. Note that this function is intended to be used
// within a network namespace for isolation.
func listRulesAndRoutes(c *C, family int) ([]netlink.Rule, []netlink.Route) {
	rules, err := route.ListRules(family, nil)
	c.Assert(err, IsNil)

	// Rules are created under specific tables, so find the routes that are in
	// those tables.
	var routes []netlink.Route
	for _, r := range rules {
		rr, err := netlink.RouteListFiltered(family, &netlink.Route{
			Table: r.Table,
		}, netlink.RT_FILTER_TABLE)
		c.Assert(err, IsNil)

		routes = append(routes, rr...)
	}

	return rules, routes
}

// createDummyDevice creates a new dummy device with a MAC of `macAddr` to be
// used as a harness in this test. This function returns a function which can
// be used to remove the device for cleanup purposes.
func createDummyDevice(c *C, macAddr mac.MAC) func() {
	dummy := &netlink.Dummy{
		LinkAttrs: netlink.LinkAttrs{
			// NOTE: This name must be less than 16 chars, source:
			// https://elixir.bootlin.com/linux/v5.6/source/include/uapi/linux/if.h#L33
			Name:         "linuxrout-test",
			HardwareAddr: net.HardwareAddr(macAddr),
		},
	}
	err := netlink.LinkAdd(dummy)
	c.Assert(err, IsNil)

	c.Log("Added dummy device")

	return func() {
		c.Assert(netlink.LinkDel(dummy), IsNil)
		c.Log("Cleaned up dummy device")
	}
}

// getFakes returns a fake IP simulating an Endpoint IP and RoutingInfo as test
// harnesses.
func getFakes(c *C) (net.IP, RoutingInfo) {
	fakeGateway := net.ParseIP("192.168.2.1")
	c.Assert(fakeGateway, NotNil)

	_, fakeCIDR, err := net.ParseCIDR("192.168.0.0/16")
	c.Assert(err, IsNil)
	c.Assert(fakeCIDR, NotNil)

	fakeMAC, err := mac.ParseMAC("00:11:22:33:44:55")
	c.Assert(err, IsNil)
	c.Assert(fakeMAC, NotNil)

	fakeRoutingInfo, err := parse(fakeGateway.String(),
		[]string{fakeCIDR.String()},
		fakeMAC.String())
	c.Assert(err, IsNil)
	c.Assert(fakeRoutingInfo, NotNil)

	fakeIP := net.ParseIP("192.168.2.123")
	c.Assert(fakeIP, NotNil)

	return fakeIP, *fakeRoutingInfo
}
<commit_msg>linux/routing: List devices when adding dummy dev<commit_after>// Copyright 2020 Authors of Cilium
//
// 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.

// +build privileged_tests

package linuxrouting

import (
	"net"
	"testing"

	"github.com/cilium/cilium/pkg/datapath/linux/linux_defaults"
	"github.com/cilium/cilium/pkg/datapath/linux/route"
	"github.com/cilium/cilium/pkg/mac"
	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netns"

	. "gopkg.in/check.v1"
)

func Test(t *testing.T) {
	TestingT(t)
}

type LinuxRoutingSuite struct{}

var _ = Suite(&LinuxRoutingSuite{})

func (e *LinuxRoutingSuite) TestConfigure(c *C) {
	currentNS, err := netns.Get()
	c.Assert(err, IsNil)
	defer func() {
		c.Assert(netns.Set(currentNS), IsNil)
		c.Log("Set back to previous network ns")
	}()

	ip, ri := getFakes(c)
	masterMAC := ri.MasterIfMAC
	runFuncInNetNS(c, func() { runConfigureThenDelete(c, ri, ip, 1500, false) }, masterMAC)
	runFuncInNetNS(c, func() { runConfigureThenDelete(c, ri, ip, 1500, true) }, masterMAC)
}

func (e *LinuxRoutingSuite) TestConfigureRoutewithIncompatibleIP(c *C) {
	_, ri := getFakes(c)
	ipv6 := net.ParseIP("fd00::2").To16()
	c.Assert(ipv6, NotNil)
	err := ri.Configure(ipv6, 1500, true)
	c.Assert(err, NotNil)
	c.Assert(err, ErrorMatches, "IP not compatible")
}

func (e *LinuxRoutingSuite) TestDeleteRoutewithIncompatibleIP(c *C) {
	ipv6 := net.ParseIP("fd00::2").To16()
	c.Assert(ipv6, NotNil)
	err := Delete(ipv6)
	c.Assert(err, NotNil)
	c.Assert(err, ErrorMatches, "IP not compatible")
}

func (e *LinuxRoutingSuite) TestDelete(c *C) {
	fakeIP, fakeRoutingInfo := getFakes(c)
	masterMAC := fakeRoutingInfo.MasterIfMAC

	tests := []struct {
		name    string
		preRun  func() net.IP
		wantErr bool
	}{
		{
			name: "valid IP addr matching rules",
			preRun: func() net.IP {
				runConfigure(c, fakeRoutingInfo, fakeIP, 1500, false)
				return fakeIP
			},
			wantErr: false,
		},
		{
			name: "IP addr doesn't match rules",
			preRun: func() net.IP {
				ip := net.ParseIP("192.168.2.233")
				c.Assert(ip, NotNil)

				runConfigure(c, fakeRoutingInfo, fakeIP, 1500, false)
				return ip
			},
			wantErr: true,
		},
		{
			name: "IP addr matches more than number expected",
			preRun: func() net.IP {
				ip := net.ParseIP("192.168.2.233")
				c.Assert(ip, NotNil)

				runConfigure(c, fakeRoutingInfo, ip, 1500, false)

				// Find interface ingress rules so that we can create a
				// near-duplicate.
				rules, err := route.ListRules(netlink.FAMILY_V4, &route.Rule{
					Priority: linux_defaults.RulePriorityIngress,
				})
				c.Assert(err, IsNil)
				c.Assert(len(rules), Not(Equals), 0)

				// Insert almost duplicate rule; the reason for this is to
				// trigger an error while trying to delete the ingress rule. We
				// are setting the Src because ingress rules don't have
				// one (only Dst), thus we set Src to create a near-duplicate.
				r := rules[0]
				r.Src = &net.IPNet{IP: fakeIP, Mask: net.CIDRMask(32, 32)}
				c.Assert(netlink.RuleAdd(&r), IsNil)

				return ip
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		c.Log(tt.name)
		runFuncInNetNS(c, func() {
			ip := tt.preRun()
			err := Delete(ip)
			c.Assert((err != nil), Equals, tt.wantErr)
		}, masterMAC)
	}
}

func runFuncInNetNS(c *C, run func(), macAddr mac.MAC) {
	networkNS, err := netns.New()
	c.Assert(err, IsNil)
	c.Logf("Inside new network ns %v", networkNS.UniqueId())
	defer func() {
		uid := networkNS.UniqueId()
		c.Assert(networkNS.Close(), IsNil)
		c.Logf("Closed new network ns %v", uid)
	}()

	ifaceCleanup := createDummyDevice(c, macAddr)
	defer ifaceCleanup()

	run()
}

func runConfigureThenDelete(c *C, ri RoutingInfo, ip net.IP, mtu int, masq bool) {
	// Create rules and routes
	beforeCreationRules, beforeCreationRoutes := listRulesAndRoutes(c, netlink.FAMILY_V4)
	runConfigure(c, ri, ip, mtu, masq)
	afterCreationRules, afterCreationRoutes := listRulesAndRoutes(c, netlink.FAMILY_V4)

	c.Assert(len(afterCreationRules), Not(Equals), 0)
	c.Assert(len(afterCreationRoutes), Not(Equals), 0)
	c.Assert(len(beforeCreationRules), Not(Equals), len(afterCreationRules))
	c.Assert(len(beforeCreationRoutes), Not(Equals), len(afterCreationRoutes))

	// Delete rules and routes
	beforeDeletionRules, beforeDeletionRoutes := listRulesAndRoutes(c, netlink.FAMILY_V4)
	runDelete(c, ip)
	afterDeletionRules, afterDeletionRoutes := listRulesAndRoutes(c, netlink.FAMILY_V4)

	c.Assert(len(beforeDeletionRules), Not(Equals), len(afterDeletionRules))
	c.Assert(len(beforeDeletionRoutes), Not(Equals), len(afterDeletionRoutes))
	c.Assert(len(afterDeletionRules), Equals, len(beforeCreationRules))
	c.Assert(len(afterDeletionRoutes), Equals, len(beforeCreationRoutes))
}

func runConfigure(c *C, ri RoutingInfo, ip net.IP, mtu int, masq bool) {
	err := ri.Configure(ip, mtu, masq)
	c.Assert(err, IsNil)
}

func runDelete(c *C, ip net.IP) {
	err := Delete(ip)
	c.Assert(err, IsNil)
}

// listRulesAndRoutes returns all rules and routes configured on the machine
// this test is running on. Note that this function is intended to be used
// within a network namespace for isolation.
func listRulesAndRoutes(c *C, family int) ([]netlink.Rule, []netlink.Route) {
	rules, err := route.ListRules(family, nil)
	c.Assert(err, IsNil)

	// Rules are created under specific tables, so find the routes that are in
	// those tables.
	var routes []netlink.Route
	for _, r := range rules {
		rr, err := netlink.RouteListFiltered(family, &netlink.Route{
			Table: r.Table,
		}, netlink.RT_FILTER_TABLE)
		c.Assert(err, IsNil)

		routes = append(routes, rr...)
	}

	return rules, routes
}

// createDummyDevice creates a new dummy device with a MAC of `macAddr` to be
// used as a harness in this test. This function returns a function which can
// be used to remove the device for cleanup purposes.
func createDummyDevice(c *C, macAddr mac.MAC) func() {
	if linkExistsWithMAC(c, macAddr) {
		c.Logf("Found device with identical mac addr: %s", macAddr.String())
		c.FailNow()
	}

	dummy := &netlink.Dummy{
		LinkAttrs: netlink.LinkAttrs{
			// NOTE: This name must be less than 16 chars, source:
			// https://elixir.bootlin.com/linux/v5.6/source/include/uapi/linux/if.h#L33
			Name:         "linuxrout-test",
			HardwareAddr: net.HardwareAddr(macAddr),
		},
	}
	err := netlink.LinkAdd(dummy)
	c.Assert(err, IsNil)

	c.Log("Added dummy device")

	found := linkExistsWithMAC(c, macAddr)
	if !found {
		c.Log("Couldn't find device even after creation")
	}
	c.Assert(found, Equals, true)

	return func() {
		c.Assert(netlink.LinkDel(dummy), IsNil)
		c.Log("Cleaned up dummy device")
	}
}

// getFakes returns a fake IP simulating an Endpoint IP and RoutingInfo as test
// harnesses.
func getFakes(c *C) (net.IP, RoutingInfo) {
	fakeGateway := net.ParseIP("192.168.2.1")
	c.Assert(fakeGateway, NotNil)

	_, fakeCIDR, err := net.ParseCIDR("192.168.0.0/16")
	c.Assert(err, IsNil)
	c.Assert(fakeCIDR, NotNil)

	fakeMAC, err := mac.ParseMAC("00:11:22:33:44:55")
	c.Assert(err, IsNil)
	c.Assert(fakeMAC, NotNil)

	fakeRoutingInfo, err := parse(fakeGateway.String(),
		[]string{fakeCIDR.String()},
		fakeMAC.String())
	c.Assert(err, IsNil)
	c.Assert(fakeRoutingInfo, NotNil)

	fakeIP := net.ParseIP("192.168.2.123")
	c.Assert(fakeIP, NotNil)

	return fakeIP, *fakeRoutingInfo
}

func linkExistsWithMAC(c *C, macAddr mac.MAC) bool {
	links, err := netlink.LinkList()
	c.Assert(err, IsNil)

	for _, link := range links {
		if link.Attrs().HardwareAddr.String() == macAddr.String() {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/bmatcuk/doublestar"
)

type GiterminismConfig struct {
	Config config `json:"config"`
	Helm   helm   `json:"helm"`
}

type config struct {
	AllowUncommitted          bool                `json:"allowUncommitted"`
	AllowUncommittedTemplates []string            `json:"allowUncommittedTemplates"`
	GoTemplateRendering       goTemplateRendering `json:"goTemplateRendering"`
	Stapel                    stapel              `json:"stapel"`
	Dockerfile                dockerfile          `json:"dockerfile"`
}

func (c config) IsUncommittedTemplateFileAccepted(path string) (bool, error) {
	return isPathMatched(c.AllowUncommittedTemplates, path, true)
}

type goTemplateRendering struct {
	AllowEnvVariables     []string `json:"allowEnvVariables"`
	AllowUncommittedFiles []string `json:"allowUncommittedFiles"`
}

func (r goTemplateRendering) IsEnvNameAccepted(name string) (bool, error) {
	for _, pattern := range r.AllowEnvVariables {
		if strings.HasPrefix(pattern, "/") && strings.HasSuffix(pattern, "/") {
			expr := fmt.Sprintf("^%s$", pattern[1:len(pattern)-1])
			r, err := regexp.Compile(expr)
			if err != nil {
				return false, err
			}

			return r.MatchString(name), nil
		} else {
			return pattern == name, nil
		}
	}

	return false, nil
}

func (r goTemplateRendering) IsUncommittedFileAccepted(path string) (bool, error) {
	return isPathMatched(r.AllowUncommittedFiles, path, true)
}

type stapel struct {
	AllowFromLatest bool  `json:"allowFromLatest"`
	Git             git   `json:"git"`
	Mount           mount `json:"mount"`
}

type git struct {
	AllowBranch bool `json:"allowBranch"`
}

type mount struct {
	AllowBuildDir  bool     `json:"allowBuildDir"`
	AllowFromPaths []string `json:"allowFromPaths"`
}

func (m mount) IsFromPathAccepted(path string) (bool, error) {
	return isPathMatched(m.AllowFromPaths, path, true)
}

type dockerfile struct {
	AllowUncommitted                  []string `json:"allowUncommitted"`
	AllowUncommittedDockerignoreFiles []string `json:"allowUncommittedDockerignoreFiles"`
	AllowContextAddFile               []string `json:"allowContextAddFile"`
}

func (d dockerfile) IsContextAddFileAccepted(path string) (bool, error) {
	return isPathMatched(d.AllowContextAddFile, path, true)
}

func (d dockerfile) IsUncommittedAccepted(path string) (bool, error) {
	return isPathMatched(d.AllowUncommitted, path, true)
}

func (d dockerfile) IsUncommittedDockerignoreAccepted(path string) (bool, error) {
	return isPathMatched(d.AllowUncommittedDockerignoreFiles, path, true)
}

type helm struct {
	AllowUncommittedFiles []string `json:"allowUncommittedFiles"`
}

func isPathMatched(patterns []string, path string, withGlobs bool) (bool, error) {
	path = filepath.ToSlash(path)
	for _, pattern := range patterns {
		pattern = filepath.ToSlash(pattern)

		var expr string
		var matchFunc func(string, string) (bool, error)
		if strings.HasPrefix(pattern, "/") && strings.HasSuffix(pattern, "/") && withGlobs {
			expr = fmt.Sprintf("^%s$", pattern[1:len(pattern)-1])
			matchFunc = doublestar.Match
		} else {
			expr = pattern
			matchFunc = func(pattern string, path string) (bool, error) {
				return pattern == path, nil
			}
		}

		if matched, err := matchFunc(expr, path); err != nil {
			return false, fmt.Errorf("unable to match path (pattern: %s, path %s): %s", pattern, path, err)
		} else if matched {
			return true, nil
		}
	}

	return false, nil
}
<commit_msg>Fix glob pattern wrapped in ^ and $<commit_after>package config

import (
	"fmt"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/bmatcuk/doublestar"
)

type GiterminismConfig struct {
	Config config `json:"config"`
	Helm   helm   `json:"helm"`
}

type config struct {
	AllowUncommitted          bool                `json:"allowUncommitted"`
	AllowUncommittedTemplates []string            `json:"allowUncommittedTemplates"`
	GoTemplateRendering       goTemplateRendering `json:"goTemplateRendering"`
	Stapel                    stapel              `json:"stapel"`
	Dockerfile                dockerfile          `json:"dockerfile"`
}

func (c config) IsUncommittedTemplateFileAccepted(path string) (bool, error) {
	return isPathMatched(c.AllowUncommittedTemplates, path, true)
}

type goTemplateRendering struct {
	AllowEnvVariables     []string `json:"allowEnvVariables"`
	AllowUncommittedFiles []string `json:"allowUncommittedFiles"`
}

func (r goTemplateRendering) IsEnvNameAccepted(name string) (bool, error) {
	for _, pattern := range r.AllowEnvVariables {
		if strings.HasPrefix(pattern, "/") && strings.HasSuffix(pattern, "/") {
			expr := fmt.Sprintf("^%s$", pattern[1:len(pattern)-1])
			r, err := regexp.Compile(expr)
			if err != nil {
				return false, err
			}

			return r.MatchString(name), nil
		} else {
			return pattern == name, nil
		}
	}

	return false, nil
}

func (r goTemplateRendering) IsUncommittedFileAccepted(path string) (bool, error) {
	return isPathMatched(r.AllowUncommittedFiles, path, true)
}

type stapel struct {
	AllowFromLatest bool  `json:"allowFromLatest"`
	Git             git   `json:"git"`
	Mount           mount `json:"mount"`
}

type git struct {
	AllowBranch bool `json:"allowBranch"`
}

type mount struct {
	AllowBuildDir  bool     `json:"allowBuildDir"`
	AllowFromPaths []string `json:"allowFromPaths"`
}

func (m mount) IsFromPathAccepted(path string) (bool, error) {
	return isPathMatched(m.AllowFromPaths, path, true)
}

type dockerfile struct {
	AllowUncommitted                  []string `json:"allowUncommitted"`
	AllowUncommittedDockerignoreFiles []string `json:"allowUncommittedDockerignoreFiles"`
	AllowContextAddFile               []string `json:"allowContextAddFile"`
}

func (d dockerfile) IsContextAddFileAccepted(path string) (bool, error) {
	return isPathMatched(d.AllowContextAddFile, path, true)
}

func (d dockerfile) IsUncommittedAccepted(path string) (bool, error) {
	return isPathMatched(d.AllowUncommitted, path, true)
}

func (d dockerfile) IsUncommittedDockerignoreAccepted(path string) (bool, error) {
	return isPathMatched(d.AllowUncommittedDockerignoreFiles, path, true)
}

type helm struct {
	AllowUncommittedFiles []string `json:"allowUncommittedFiles"`
}

func isPathMatched(patterns []string, path string, withGlobs bool) (bool, error) {
	path = filepath.ToSlash(path)
	for _, pattern := range patterns {
		pattern = filepath.ToSlash(pattern)

		var expr string
		var matchFunc func(string, string) (bool, error)
		if strings.HasPrefix(pattern, "/") && strings.HasSuffix(pattern, "/") && withGlobs {
			expr = pattern[1 : len(pattern)-1]
			matchFunc = doublestar.Match
		} else {
			expr = pattern
			matchFunc = func(pattern string, path string) (bool, error) {
				return pattern == path, nil
			}
		}

		if matched, err := matchFunc(expr, path); err != nil {
			return false, fmt.Errorf("unable to match path (pattern: %s, path %s): %s", pattern, path, err)
		} else if matched {
			return true, nil
		}
	}

	return false, nil
}
<|endoftext|>


<commit_before>package qsim

// A Queue holds Jobs until they're ready for processing.
type Queue struct {
	// The Jobs currently in the queue
	Jobs []*Job

	// Callback lists
	cbBeforeAppend []func(q *Queue, j *Job)
	cbAfterAppend  []func(q *Queue, j *Job)
	cbBeforeShift  []func(q *Queue, j *Job)
	cbAfterShift   []func(q *Queue, j *Job)
}

// Append adds a Job to the tail of the queue.
func (q *Queue) Append(j *Job) {
	q.beforeAppend(j)
	q.Jobs = append(q.Jobs, j)
	q.afterAppend(j)
}

// Shift removes a Job from the head of the queue.
//
// It returns the Job that was removed, as well as the number of Jobs
// still left in the queue after shifting. When Shift is called on an
// empty queue, j will be nil. So an appropriate use of Shift looks like
// this:
//
//  j, nrem := q.Shift()
//	if j != nil {
//    // Do something with j
//  }
func (q *Queue) Shift() (j *Job, nrem int) {
	if len(q.Jobs) == 0 {
		q.beforeShift(nil)
		q.afterShift(nil)
		return nil, 0
	}
	j = q.Jobs[0]
	q.beforeShift(j)
	q.Jobs = q.Jobs[1:]
	q.afterShift(j)
	return j, len(q.Jobs)
}

// OnBeforeAppend adds a callback to be run immediately before a Job is
// appended to the queue.
//
// The callback will be passed the queue itself and the job that's about
// to be appended.
func (q *Queue) OnBeforeAppend(f func(q *Queue, j *Job)) {
	q.cbBeforeAppend = append(q.cbBeforeAppend, f)
}
func (q *Queue) beforeAppend(j *Job) {
	for _, cb := range q.cbBeforeAppend {
		cb(q, j)
	}
}

// OnAfterAppend adds a callback to be run immediately after a Job is
// appended to the queue.
//
// The callback will be passed the queue itself and the job that was just
// appended.
func (q *Queue) OnAfterAppend(f func(q *Queue, j *Job)) {
	q.cbAfterAppend = append(q.cbAfterAppend, f)
}
func (q *Queue) afterAppend(j *Job) {
	for _, cb := range q.cbAfterAppend {
		cb(q, j)
	}
}

// OnBeforeShift adds a callback to be run immediately before a Job is
// shifted out of the queue.
//
// The callback will be passed the queue itself and the job that's about
// to be shifted. If Shift is called on an empty queue, this callback
// will run but j will be nil.
func (q *Queue) OnBeforeShift(f func(q *Queue, j *Job)) {
	q.cbBeforeShift = append(q.cbBeforeShift, f)
}
func (q *Queue) beforeShift(j *Job) {
	for _, cb := range q.cbBeforeShift {
		cb(q, j)
	}
}

// OnAfterShift adds a callback to be run immediately after a Job is
// shifted out of the queue.
//
// The callback will be passed the queue itself and the job that was
// just shifted. If Shift is called on an empty queue, this callback
// will run but j will be nil.
func (q *Queue) OnAfterShift(f func(q *Queue, j *Job)) {
	q.cbAfterShift = append(q.cbAfterShift, f)
}
func (q *Queue) afterShift(j *Job) {
	for _, cb := range q.cbAfterShift {
		cb(q, j)
	}
}

// NewQueue creates an empty Queue.
func NewQueue() (q *Queue) {
	q = new(Queue)
	q.Jobs = make([]*Job, 0)
	return q
}
<commit_msg>Another attempt at fixing Godoc formatting<commit_after>package qsim

// A Queue holds Jobs until they're ready for processing.
type Queue struct {
	// The Jobs currently in the queue
	Jobs []*Job

	// Callback lists
	cbBeforeAppend []func(q *Queue, j *Job)
	cbAfterAppend  []func(q *Queue, j *Job)
	cbBeforeShift  []func(q *Queue, j *Job)
	cbAfterShift   []func(q *Queue, j *Job)
}

// Append adds a Job to the tail of the queue.
func (q *Queue) Append(j *Job) {
	q.beforeAppend(j)
	q.Jobs = append(q.Jobs, j)
	q.afterAppend(j)
}

// Shift removes a Job from the head of the queue.
//
// It returns the Job that was removed, as well as the number of Jobs
// still left in the queue after shifting. When Shift is called on an
// empty queue, j will be nil. So an appropriate use of Shift looks like
// this:
//
//  j, nrem := q.Shift()
//  if j != nil {
//    // Do something with j
//  }
func (q *Queue) Shift() (j *Job, nrem int) {
	if len(q.Jobs) == 0 {
		q.beforeShift(nil)
		q.afterShift(nil)
		return nil, 0
	}
	j = q.Jobs[0]
	q.beforeShift(j)
	q.Jobs = q.Jobs[1:]
	q.afterShift(j)
	return j, len(q.Jobs)
}

// OnBeforeAppend adds a callback to be run immediately before a Job is
// appended to the queue.
//
// The callback will be passed the queue itself and the job that's about
// to be appended.
func (q *Queue) OnBeforeAppend(f func(q *Queue, j *Job)) {
	q.cbBeforeAppend = append(q.cbBeforeAppend, f)
}
func (q *Queue) beforeAppend(j *Job) {
	for _, cb := range q.cbBeforeAppend {
		cb(q, j)
	}
}

// OnAfterAppend adds a callback to be run immediately after a Job is
// appended to the queue.
//
// The callback will be passed the queue itself and the job that was just
// appended.
func (q *Queue) OnAfterAppend(f func(q *Queue, j *Job)) {
	q.cbAfterAppend = append(q.cbAfterAppend, f)
}
func (q *Queue) afterAppend(j *Job) {
	for _, cb := range q.cbAfterAppend {
		cb(q, j)
	}
}

// OnBeforeShift adds a callback to be run immediately before a Job is
// shifted out of the queue.
//
// The callback will be passed the queue itself and the job that's about
// to be shifted. If Shift is called on an empty queue, this callback
// will run but j will be nil.
func (q *Queue) OnBeforeShift(f func(q *Queue, j *Job)) {
	q.cbBeforeShift = append(q.cbBeforeShift, f)
}
func (q *Queue) beforeShift(j *Job) {
	for _, cb := range q.cbBeforeShift {
		cb(q, j)
	}
}

// OnAfterShift adds a callback to be run immediately after a Job is
// shifted out of the queue.
//
// The callback will be passed the queue itself and the job that was
// just shifted. If Shift is called on an empty queue, this callback
// will run but j will be nil.
func (q *Queue) OnAfterShift(f func(q *Queue, j *Job)) {
	q.cbAfterShift = append(q.cbAfterShift, f)
}
func (q *Queue) afterShift(j *Job) {
	for _, cb := range q.cbAfterShift {
		cb(q, j)
	}
}

// NewQueue creates an empty Queue.
func NewQueue() (q *Queue) {
	q = new(Queue)
	q.Jobs = make([]*Job, 0)
	return q
}
<|endoftext|>


<commit_before>/**
 * Author:        Tony.Shao
 * Email:         xiocode@gmail.com
 * Github:        github.com/xiocode
 * File:          redis.go
 * Description:   fork revel's cache
**/

package cache

import (
	"time"

	"github.com/garyburd/redigo/redis"
)

// Wraps the Redis client to meet the Cache interface.
type RedisCache struct {
	pool              *redis.Pool
	defaultExpiration time.Duration
}

// until redigo supports sharding/clustering, only one host will be in hostList
func NewRedisCache(host string, password string, defaultExpiration time.Duration) RedisCache {
	var pool = &redis.Pool{
		MaxIdle:     5,
		IdleTimeout: 120 * time.Second,
		Dial: func() (redis.Conn, error) {
			protocol := "tcp"
			c, err := redis.DialTimeout(protocol, host, 1*time.Second, 1*time.Second, 1*time.Second)
			if err != nil {
				return nil, err
			}

			// check with PING
			if _, err := c.Do("PING"); err != nil {
				c.Close()
				return nil, err
			}

			return c, err
		},
		// custom connection test method
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			if _, err := c.Do("PING"); err != nil {
				return err
			}
			return nil
		},
	}
	return RedisCache{pool, defaultExpiration}
}

func (c RedisCache) Set(key string, value interface{}, expires time.Duration) error {
	conn := c.pool.Get()
	defer conn.Close()
	return c.invoke(conn.Do, key, value, expires)
}

func (c RedisCache) Add(key string, value interface{}, expires time.Duration) error {
	conn := c.pool.Get()
	defer conn.Close()
	existed, err := exists(conn, key)
	if err != nil {
		return err
	} else if existed {
		return ErrNotStored
	}
	return c.invoke(conn.Do, key, value, expires)
}

func (c RedisCache) Replace(key string, value interface{}, expires time.Duration) error {
	conn := c.pool.Get()
	defer conn.Close()
	existed, err := exists(conn, key)
	if err != nil {
		return err
	} else if !existed {
		return ErrNotStored
	}
	err = c.invoke(conn.Do, key, value, expires)
	if value == nil {
		return ErrNotStored
	} else {
		return err
	}
}

func (c RedisCache) Get(key string, ptrValue interface{}) error {
	conn := c.pool.Get()
	defer conn.Close()
	raw, err := conn.Do("GET", key)
	if err != nil {
		return err
	} else if raw == nil {
		return ErrCacheMiss
	}
	item, err := redis.Bytes(raw, err)
	if err != nil {
		return err
	}
	return Deserialize(item, ptrValue)
}

func generalizeStringSlice(strs []string) []interface{} {
	ret := make([]interface{}, len(strs))
	for i, str := range strs {
		ret[i] = str
	}
	return ret
}

func (c RedisCache) GetMulti(keys ...string) (Getter, error) {
	conn := c.pool.Get()
	defer conn.Close()

	items, err := redis.Values(conn.Do("MGET", generalizeStringSlice(keys)...))
	if err != nil {
		return nil, err
	} else if items == nil {
		return nil, ErrCacheMiss
	}

	m := make(map[string][]byte)
	for i, key := range keys {
		m[key] = nil
		if i < len(items) && items[i] != nil {
			s, ok := items[i].([]byte)
			if ok {
				m[key] = s
			}
		}
	}
	return RedisItemMapGetter(m), nil
}

func exists(conn redis.Conn, key string) (bool, error) {
	return redis.Bool(conn.Do("EXISTS", key))
}

func (c RedisCache) Exists(key string) (bool, error) {
	conn := c.pool.Get()
	defer conn.Close()
	return exists(conn, key)
}

func (c RedisCache) Delete(key string) error {
	conn := c.pool.Get()
	defer conn.Close()
	existed, err := redis.Bool(conn.Do("DEL", key))
	if err == nil && !existed {
		err = ErrCacheMiss
	}
	return err
}

func (c RedisCache) Increment(key string, delta uint64) (uint64, error) {
	conn := c.pool.Get()
	defer conn.Close()
	// Check for existance *before* increment as per the cache contract.
	// redis will auto create the key, and we don't want that. Since we need to do increment
	// ourselves instead of natively via INCRBY (redis doesn't support wrapping), we get the value
	// and do the exists check this way to minimize calls to Redis
	val, err := conn.Do("GET", key)
	if err != nil {
		return 0, err
	} else if val == nil {
		return 0, ErrCacheMiss
	}
	currentVal, err := redis.Int64(val, nil)
	if err != nil {
		return 0, err
	}
	var sum int64 = currentVal + int64(delta)
	_, err = conn.Do("SET", key, sum)
	if err != nil {
		return 0, err
	}
	return uint64(sum), nil
}

func (c RedisCache) Decrement(key string, delta uint64) (newValue uint64, err error) {
	conn := c.pool.Get()
	defer conn.Close()
	// Check for existance *before* increment as per the cache contract.
	// redis will auto create the key, and we don't want that, hence the exists call
	existed, err := exists(conn, key)
	if err != nil {
		return 0, err
	} else if !existed {
		return 0, ErrCacheMiss
	}
	// Decrement contract says you can only go to 0
	// so we go fetch the value and if the delta is greater than the amount,
	// 0 out the value
	currentVal, err := redis.Int64(conn.Do("GET", key))
	if err != nil {
		return 0, err
	}
	if delta > uint64(currentVal) {
		tempint, err := redis.Int64(conn.Do("DECRBY", key, currentVal))
		return uint64(tempint), err
	}
	tempint, err := redis.Int64(conn.Do("DECRBY", key, delta))
	return uint64(tempint), err
}

func (c RedisCache) Flush() error {
	conn := c.pool.Get()
	defer conn.Close()
	_, err := conn.Do("FLUSHALL")
	return err
}

func (c RedisCache) invoke(f func(string, ...interface{}) (interface{}, error),
	key string, value interface{}, expires time.Duration) error {

	switch expires {
	case DEFAULT:
		expires = c.defaultExpiration
	case FOREVER:
		expires = time.Duration(0)
	}

	b, err := Serialize(value)
	if err != nil {
		return err
	}
	conn := c.pool.Get()
	defer conn.Close()
	if expires > 0 {
		_, err := f("SETEX", key, int32(expires/time.Second), b)
		return err
	} else {
		_, err := f("SET", key, b)
		return err
	}
}

// Implement a Getter on top of the returned item map.
type RedisItemMapGetter map[string][]byte

func (g RedisItemMapGetter) Get(key string, ptrValue interface{}) error {
	item, ok := g[key]
	if !ok {
		return ErrCacheMiss
	}
	return Deserialize(item, ptrValue)
}
<commit_msg>Update redis.go<commit_after>/**
 * Author:        Tony.Shao
 * Email:         xiocode@gmail.com
 * Github:        github.com/xiocode
 * File:          redis.go
 * Description:   fork revel's cache
**/

package cache

import (
	"time"

	"github.com/garyburd/redigo/redis"
)

// Wraps the Redis client to meet the Cache interface.
type RedisCache struct {
	pool              *redis.Pool
	defaultExpiration time.Duration
}

// until redigo supports sharding/clustering, only one host will be in hostList
func NewRedisCache(host string, password string, defaultExpiration time.Duration) RedisCache {
	var pool = &redis.Pool{
		MaxIdle:     5,
		IdleTimeout: 120 * time.Second,
		Dial: func() (redis.Conn, error) {
			protocol := "tcp"
			c, err := redis.DialTimeout(protocol, host, 1*time.Second, 1*time.Second, 1*time.Second)
			if err != nil {
				return nil, err
			}

			// check with PING
			if _, err := c.Do("PING"); err != nil {
				c.Close()
				return nil, err
			}

			return c, err
		},
		// custom connection test method
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			if _, err := c.Do("PING"); err != nil {
				return err
			}
			return nil
		},
	}
	return RedisCache{pool, defaultExpiration}
}

func (c RedisCache) Set(key string, value interface{}, expires time.Duration) error {
	conn := c.pool.Get()
	defer conn.Close()
	return c.invoke(conn.Do, key, value, expires)
}

func (c RedisCache) Add(key string, value interface{}, expires time.Duration) error {
	conn := c.pool.Get()
	defer conn.Close()
	existed, err := exists(conn, key)
	if err != nil {
		return err
	} else if existed {
		return ErrNotStored
	}
	return c.invoke(conn.Do, key, value, expires)
}

func (c RedisCache) Replace(key string, value interface{}, expires time.Duration) error {
	conn := c.pool.Get()
	defer conn.Close()
	existed, err := exists(conn, key)
	if err != nil {
		return err
	} else if !existed {
		return ErrNotStored
	}
	err = c.invoke(conn.Do, key, value, expires)
	if value == nil {
		return ErrNotStored
	} else {
		return err
	}
}

func (c RedisCache) Get(key string, ptrValue interface{}) error {
	conn := c.pool.Get()
	defer conn.Close()
	raw, err := conn.Do("GET", key)
	if err != nil {
		return err
	} else if raw == nil {
		return ErrCacheMiss
	}
	item, err := redis.Bytes(raw, err)
	if err != nil {
		return err
	}
	return Deserialize(item, ptrValue)
}

func generalizeStringSlice(strs []string) []interface{} {
	ret := make([]interface{}, len(strs))
	for i, str := range strs {
		ret[i] = str
	}
	return ret
}

func (c RedisCache) GetMulti(keys ...string) (Getter, error) {
	conn := c.pool.Get()
	defer conn.Close()

	items, err := redis.Values(conn.Do("MGET", generalizeStringSlice(keys)...))
	if err != nil {
		return nil, err
	} else if items == nil {
		return nil, ErrCacheMiss
	}

	m := make(map[string][]byte)
	for i, key := range keys {
		m[key] = nil
		if i < len(items) && items[i] != nil {
			s, ok := items[i].([]byte)
			if ok {
				m[key] = s
			}
		}
	}
	return RedisItemMapGetter(m), nil
}

func exists(conn redis.Conn, key string) (bool, error) {
	return redis.Bool(conn.Do("EXISTS", key))
}

func (c RedisCache) Exists(key string) (bool, error) {
	conn := c.pool.Get()
	defer conn.Close()
	return exists(conn, key)
}

func (c RedisCache) Delete(key string) error {
	conn := c.pool.Get()
	defer conn.Close()
	existed, err := redis.Bool(conn.Do("DEL", key))
	if err == nil && !existed {
		err = ErrCacheMiss
	}
	return err
}

func (c RedisCache) Increment(key string, delta uint64) (uint64, error) {
	conn := c.pool.Get()
	defer conn.Close()
	// Check for existance *before* increment as per the cache contract.
	// redis will auto create the key, and we don't want that. Since we need to do increment
	// ourselves instead of natively via INCRBY (redis doesn't support wrapping), we get the value
	// and do the exists check this way to minimize calls to Redis
	existed, err := exists(conn, key)
	if err != nil {
		return 0, err
	} else if !existed {
		return 0, ErrCacheMiss
	}

	val, err := redis.Int64(conn.Do("INCRBY", key, delta))
	if err != nil {
		return 0, err
	}
	return uint64(val), nil
}

func (c RedisCache) Decrement(key string, delta uint64) (newValue uint64, err error) {
	conn := c.pool.Get()
	defer conn.Close()
	// Check for existance *before* increment as per the cache contract.
	// redis will auto create the key, and we don't want that, hence the exists call
	existed, err := exists(conn, key)
	if err != nil {
		return 0, err
	} else if !existed {
		return 0, ErrCacheMiss
	}
	// Decrement contract says you can only go to 0
	// so we go fetch the value and if the delta is greater than the amount,
	// 0 out the value
	currentVal, err := redis.Int64(conn.Do("GET", key))
	if err != nil {
		return 0, err
	}
	if delta > uint64(currentVal) {
		tempint, err := redis.Int64(conn.Do("DECRBY", key, currentVal))
		return uint64(tempint), err
	}
	tempint, err := redis.Int64(conn.Do("DECRBY", key, delta))
	return uint64(tempint), err
}

func (c RedisCache) Flush() error {
	conn := c.pool.Get()
	defer conn.Close()
	_, err := conn.Do("FLUSHALL")
	return err
}

func (c RedisCache) invoke(f func(string, ...interface{}) (interface{}, error),
	key string, value interface{}, expires time.Duration) error {

	switch expires {
	case DEFAULT:
		expires = c.defaultExpiration
	case FOREVER:
		expires = time.Duration(0)
	}

	b, err := Serialize(value)
	if err != nil {
		return err
	}
	conn := c.pool.Get()
	defer conn.Close()
	if expires > 0 {
		_, err := f("SETEX", key, int32(expires/time.Second), b)
		return err
	} else {
		_, err := f("SET", key, b)
		return err
	}
}

// Implement a Getter on top of the returned item map.
type RedisItemMapGetter map[string][]byte

func (g RedisItemMapGetter) Get(key string, ptrValue interface{}) error {
	item, ok := g[key]
	if !ok {
		return ErrCacheMiss
	}
	return Deserialize(item, ptrValue)
}
<|endoftext|>


<commit_before>package main

import "fmt"
import "log"
// import "errors"
import "strings"

func go_sucks_constraints() {
	_ = fmt.Printf
	_ = log.Ldate
}

type Check struct {

}

type CheckHelper struct {
	checks []Check
	// Parent Node
	ref Reference
}

func (n *Rule) BuildChecks() {
	// vis := new(getChecks)
	ch := &CheckHelper{}
	// Walk(vis, l)
	// ch.Parent = n

	for _, bi := range n.Bag {
		bi.BuildBagChecks(ch)
	}

	n.When.BuildChecks()

	// for c := range vis.checks {
	// fmt.Printf("%v\n", ch)
	// }
}

func (n *When) BuildChecks() {
	if (n == nil) {
		fmt.Printf("No when clause\n")
	} else {
		fmt.Printf("checks on %s\n", n.String())
	}
}

type RefPart interface {
	refPart()
	String() string
}
type RefPartCell struct {
	Name string
}
type RefPartPosition struct {
	Offset int
}

func (*RefPartCell) refPart() {}
func (*RefPartPosition) refPart() {}

func (r *RefPartCell) String() string {
	return r.Name
}
func (r *RefPartPosition) String() string {
	return fmt.Sprintf("%d", r.Offset)
}

type Reference struct {
	Ref []RefPart
}

func (r *Reference) String() string {
	children := []string{}
	for _, c := range r.Ref {
		children = append(children, c.String())
	}
	return strings.Join(children, ".")
}

func (r *Reference) addCellEntry(s string) {
	r.Ref = append(r.Ref, &RefPartCell{s})
}
func (r *Reference) addPositionEntry(n int) {
	r.Ref = append(r.Ref, &RefPartPosition{n})
}
// func (r *Reference) setPositionEntry(n int) {
// 	r.Ref[len(r.Ref)-1] = &RefPartPosition{n}
// }

func (n ComputationCell) BuildBagChecks(ch *CheckHelper) {
	// fmt.Printf("Building checks for comp cell %s\n", n)
	_ = n.Name
	fmt.Printf("%s\n", n.String())
	ch.ref.addCellEntry(n.Name)
	// ch.ref.Ref = append(ch.ref.Ref, &RefPartCell{n.Name})
	n.Computation.BuildTopKChecks(ch)
	// fmt.Printf("Building checks for comp cell %s\n", n)
}
func (n MapCell) BuildBagChecks(ch *CheckHelper) {
	fmt.Printf("Building checks for map cell %s\n", n)
}

func (n BagCell) BuildBagChecks(ch *CheckHelper) {
	panic("Don't handle Bag Cells yet")
}
func (n *Variable) BuildBagChecks(ch *CheckHelper) {
	panic("Don't handle Bag Variables yet")
}
//---------------------------------------------------------------
func (n *DotK) BuildTopKChecks(ch *CheckHelper) {
	panic("Don't handle BuildTopKChecks DotK yet")
}
func (n *Kra) BuildTopKChecks(ch *CheckHelper) {
	if len(n.Children) == 0 {
		panic("Didn't expect size 0 kra")
	}

	allowMore := false
	lasti := len(n.Children) - 1
	fmt.Printf("lasti: %d\n", lasti)
	if lasti > 0 {
		switch c := n.Children[lasti].(type) {
		case *Variable:
			_ = c
			allowMore = true
			fmt.Printf("Ok, variable\n")
			n.Children = n.Children[:lasti]
		}
	}
	_ = allowMore

	if (allowMore) {
		fmt.Printf("%s must have at least %d things in it\n", ch.ref.String(), len(n.Children))
	} else {
		fmt.Printf("%s must have exactly %d things in it\n", ch.ref.String(), len(n.Children))
	}
	// ch.ref.addPositionEntry(0)
	for i, v := range n.Children {
		// ch.ref.setPositionEntry(i)
		v.BuildKChecks(ch, ch.ref, i)
		// fmt.Printf("asdf\n")
		fmt.Printf("%s\n", v.String())
	}
	// panic("Don't handle BuildTopKChecks Kra yet")
}
func (n *Variable) BuildTopKChecks(ch *CheckHelper) {
	panic("Don't handle BuildTopKChecks Variable yet")
}
func (n *Rewrite) BuildTopKChecks(ch *CheckHelper) {
	panic("Don't handle BuildTopKChecks Rewrite yet")
}
func (n *Appl) BuildTopKChecks(ch *CheckHelper) {
	panic("Don't handle BuildTopKChecks Appl yet")
}
func (n *Paren) BuildTopKChecks(ch *CheckHelper) {
	panic("Don't handle BuildTopKChecks Paren yet")
}
//---------------------------------------------------------------
func (n *DotK) BuildKChecks(ch *CheckHelper, ref Reference, i int) {
	panic("Don't handle BuildKChecks DotK yet")
}
func (n *Kra) BuildKChecks(ch *CheckHelper, ref Reference, i int) {
	panic("Don't handle BuildKChecks Kra yet")
}
func (n *Variable) BuildKChecks(ch *CheckHelper, ref Reference, i int) {
	ref.addPositionEntry(i)
	fmt.Printf("bind %s to %s\n", n.String(), ref.String())
	// panic("Don't handle BuildKChecks Variable yet")
}
func (n *Rewrite) BuildKChecks(ch *CheckHelper, ref Reference, i int) {
	n.LHS.BuildKChecks(ch, ref, i)

	ref.addPositionEntry(i)
	fmt.Printf("%s should be replaced with %s\n", ref.String(), n.RHS.String())

	// panic("Don't handle BuildKChecks Rewrite yet")
}
func (n *Appl) BuildKChecks(ch *CheckHelper, ref Reference, i int) {
	ref.addPositionEntry(i)
	fmt.Printf("%s must have the '%s label\n", ref.String(), n.Label.String())
	fmt.Printf("%s must have %d arguments\n", ref.String(), len(n.Body))
	for i, c := range n.Body {
		c.BuildKChecks(ch, ref, i)
	}
	// panic("Don't handle BuildKChecks Appl yet")
}
func (n *Paren) BuildKChecks(ch *CheckHelper, ref Reference, i int) {
	panic("Don't handle BuildKChecks Paren yet")
}
//---------------------------------------------------------------
// func (vis *getChecks) VisitPre(node Node) Visitor {
// 	// fmt.Printf("Visiting %s\n", node)
// 	switch n := node.(type) {
// 	case *Variable:
// 		// fmt.Printf("Handling %s\n", n)
// 		if n.Default {
// 			vis.implicitVariables = append(vis.implicitVariables, n)
// 		} else {
// 			vis.explicitVariables = append(vis.explicitVariables, n)
// 		}
// 		// pp.s += fmt.Sprintf("%s:%s", n.Name, n.Sort)
// 	}
// 	return vis
// }

// func (vis *getChecks) VisitPost(node Node) { }

// type getChecks struct {
// 	checks []Check
// 	err error
// }
<commit_msg>starting to represent constraints with objects<commit_after>package main

import "fmt"
import "log"
// import "errors"
import "strings"

func go_sucks_constraints() {
	_ = fmt.Printf
	_ = log.Ldate
}

type Check interface {
	String() string
}
type CheckNumArgs struct {
	Loc Reference
	Num int
}
type CheckLabel struct {
	Loc Reference
	Label Label
}
type CheckNumCellArgs struct {
	Loc Reference
	Num int
	Exact bool
}
func (ch *CheckNumArgs) String() string {
	return fmt.Sprintf("CheckNumArgs: %s must have %d arguments\n", ch.Loc.String(), ch.Num)
}
func (ch *CheckLabel) String() string {
	return fmt.Sprintf("CheckLabel: %s must have the '%s label\n", ch.Loc.String(), ch.Label)
}
func (ch *CheckNumCellArgs) String() string {
	if ch.Exact {
		return fmt.Sprintf("CheckNumCellArgs: %s must have exactly %d things in it\n", ch.Loc.String(), ch.Num)
	} else {
		return fmt.Sprintf("CheckNumCellArgs: %s must have at least %d things in it\n", ch.Loc.String(), ch.Num)
	}

}

type CheckHelper struct {
	checks []Check
	// Parent Node
	ref Reference
}

func (ch *CheckHelper) AddCheck(check Check) {
	ch.checks = append(ch.checks, check)
}

func (n *Rule) BuildChecks() {
	// vis := new(getChecks)
	ch := &CheckHelper{}
	// Walk(vis, l)
	// ch.Parent = n

	fmt.Printf("\n%s", n.String())

	for _, bi := range n.Bag {
		bi.BuildBagChecks(ch)
	}

	n.When.BuildChecks()

	fmt.Printf("Checks:\n")
	for _, checks := range ch.checks {
		fmt.Printf(checks.String())
	}

	// for c := range vis.checks {
	// fmt.Printf("%v\n", ch)
	// }
}

func (n *When) BuildChecks() {
	if (n == nil) {
		fmt.Printf("No when clause\n")
	} else {
		fmt.Printf("checks on %s\n", n.String())
	}
}

type RefPart interface {
	refPart()
	String() string
}
type RefPartCell struct {
	Name string
}
type RefPartPosition struct {
	Offset int
}

func (*RefPartCell) refPart() {}
func (*RefPartPosition) refPart() {}

func (r *RefPartCell) String() string {
	return r.Name
}
func (r *RefPartPosition) String() string {
	return fmt.Sprintf("%d", r.Offset)
}

type Reference struct {
	Ref []RefPart
}

func (r *Reference) String() string {
	children := []string{}
	for _, c := range r.Ref {
		children = append(children, c.String())
	}
	return strings.Join(children, ".")
}

func (r *Reference) addCellEntry(s string) {
	r.Ref = append(r.Ref, &RefPartCell{s})
}
func (r *Reference) addPositionEntry(n int) {
	r.Ref = append(r.Ref, &RefPartPosition{n})
}
// func (r *Reference) setPositionEntry(n int) {
// 	r.Ref[len(r.Ref)-1] = &RefPartPosition{n}
// }

func (n ComputationCell) BuildBagChecks(ch *CheckHelper) {
	// fmt.Printf("Building checks for comp cell %s\n", n)
	_ = n.Name
	// fmt.Printf("%s\n", n.String())
	ch.ref.addCellEntry(n.Name)
	// ch.ref.Ref = append(ch.ref.Ref, &RefPartCell{n.Name})
	n.Computation.BuildTopKChecks(ch)
	// fmt.Printf("Building checks for comp cell %s\n", n)
}
func (n MapCell) BuildBagChecks(ch *CheckHelper) {
	fmt.Printf("Building checks for map cell %s\n", n)
}

func (n BagCell) BuildBagChecks(ch *CheckHelper) {
	panic("Don't handle Bag Cells yet")
}
func (n *Variable) BuildBagChecks(ch *CheckHelper) {
	panic("Don't handle Bag Variables yet")
}
//---------------------------------------------------------------
func (n *DotK) BuildTopKChecks(ch *CheckHelper) {
	panic("Don't handle BuildTopKChecks DotK yet")
}
func (n *Kra) BuildTopKChecks(ch *CheckHelper) {
	if len(n.Children) == 0 {
		panic("Didn't expect size 0 kra")
	}

	allowMore := false
	lasti := len(n.Children) - 1
	fmt.Printf("lasti: %d\n", lasti)
	if lasti > 0 {
		switch c := n.Children[lasti].(type) {
		case *Variable:
			_ = c
			allowMore = true
			fmt.Printf("Ok, variable\n")
			n.Children = n.Children[:lasti]
		}
	}
	_ = allowMore

	check := &CheckNumCellArgs{Loc: ch.ref, Num: len(n.Children)}
	if (allowMore) {
		check.Exact = false
	} else {
		check.Exact = true
	}
	// ch.ref.addPositionEntry(0)
	for i, v := range n.Children {
		// ch.ref.setPositionEntry(i)
		v.BuildKChecks(ch, ch.ref, i)
		// fmt.Printf("asdf\n")
		fmt.Printf("%s\n", v.String())
	}
	// panic("Don't handle BuildTopKChecks Kra yet")
}
func (n *Variable) BuildTopKChecks(ch *CheckHelper) {
	panic("Don't handle BuildTopKChecks Variable yet")
}
func (n *Rewrite) BuildTopKChecks(ch *CheckHelper) {
	panic("Don't handle BuildTopKChecks Rewrite yet")
}
func (n *Appl) BuildTopKChecks(ch *CheckHelper) {
	panic("Don't handle BuildTopKChecks Appl yet")
}
func (n *Paren) BuildTopKChecks(ch *CheckHelper) {
	panic("Don't handle BuildTopKChecks Paren yet")
}
//---------------------------------------------------------------
func (n *DotK) BuildKChecks(ch *CheckHelper, ref Reference, i int) {
	panic("Don't handle BuildKChecks DotK yet")
}
func (n *Kra) BuildKChecks(ch *CheckHelper, ref Reference, i int) {
	panic("Don't handle BuildKChecks Kra yet")
}
func (n *Variable) BuildKChecks(ch *CheckHelper, ref Reference, i int) {
	ref.addPositionEntry(i)
	fmt.Printf("bind %s to %s\n", n.String(), ref.String())
	// panic("Don't handle BuildKChecks Variable yet")
}
func (n *Rewrite) BuildKChecks(ch *CheckHelper, ref Reference, i int) {
	n.LHS.BuildKChecks(ch, ref, i)

	ref.addPositionEntry(i)
	fmt.Printf("%s should be replaced with %s\n", ref.String(), n.RHS.String())

	// panic("Don't handle BuildKChecks Rewrite yet")
}
func (n *Appl) BuildKChecks(ch *CheckHelper, ref Reference, i int) {
	ref.addPositionEntry(i)
	// fmt.Printf("%s must have the '%s label\n", ref.String(), n.Label.String())
	checkLabel := &CheckLabel{Loc: ref, Label: n.Label}
	ch.AddCheck(checkLabel)
	checkArgs := &CheckNumArgs{Num: len(n.Body), Loc: ref}
	ch.AddCheck(checkArgs)
	for i, c := range n.Body {
		c.BuildKChecks(ch, ref, i)
	}
	// panic("Don't handle BuildKChecks Appl yet")
}
func (n *Paren) BuildKChecks(ch *CheckHelper, ref Reference, i int) {
	panic("Don't handle BuildKChecks Paren yet")
}
//---------------------------------------------------------------
// func (vis *getChecks) VisitPre(node Node) Visitor {
// 	// fmt.Printf("Visiting %s\n", node)
// 	switch n := node.(type) {
// 	case *Variable:
// 		// fmt.Printf("Handling %s\n", n)
// 		if n.Default {
// 			vis.implicitVariables = append(vis.implicitVariables, n)
// 		} else {
// 			vis.explicitVariables = append(vis.explicitVariables, n)
// 		}
// 		// pp.s += fmt.Sprintf("%s:%s", n.Name, n.Sort)
// 	}
// 	return vis
// }

// func (vis *getChecks) VisitPost(node Node) { }

// type getChecks struct {
// 	checks []Check
// 	err error
// }
<|endoftext|>