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

package prefilter

import (
	"fmt"
	"io"
	"net"
	"os/exec"
	"path"
	"syscall"

	"github.com/cilium/cilium/pkg/bpf"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/maps/cidrmap"
)

type preFilterMapType int

const (
	prefixesV4Dyn preFilterMapType = iota
	prefixesV4Fix
	prefixesV6Dyn
	prefixesV6Fix
	mapCount
)

const (
	// Arbitrary chosen for now. We don't preallocate elements,
	// so we could bump the limit if needed later on.
	maxLKeys = 1024 * 64
	maxHKeys = 1024 * 1024 * 20
)

type preFilterMaps [mapCount]*cidrmap.CIDRMap

type preFilterConfig struct {
	dyn4Enabled bool
	dyn6Enabled bool
	fix4Enabled bool
	fix6Enabled bool
}

// PreFilter holds global info on related CIDR maps participating in prefilter
type PreFilter struct {
	maps     preFilterMaps
	config   preFilterConfig
	revision int64
	mutex    lock.RWMutex
}

// WriteConfig dumps the configuration for the corresponding header file
func (p *PreFilter) WriteConfig(fw io.Writer) {
	p.mutex.RLock()
	defer p.mutex.RUnlock()

	fmt.Fprintf(fw, "#define CIDR4_HMAP_ELEMS %d\n", maxHKeys)
	fmt.Fprintf(fw, "#define CIDR4_LMAP_ELEMS %d\n", maxLKeys)

	fmt.Fprintf(fw, "#define CIDR4_HMAP_NAME %s\n", path.Base(p.maps[prefixesV4Fix].String()))
	fmt.Fprintf(fw, "#define CIDR4_LMAP_NAME %s\n", path.Base(p.maps[prefixesV4Dyn].String()))
	fmt.Fprintf(fw, "#define CIDR6_HMAP_NAME %s\n", path.Base(p.maps[prefixesV6Fix].String()))
	fmt.Fprintf(fw, "#define CIDR6_LMAP_NAME %s\n", path.Base(p.maps[prefixesV6Dyn].String()))

	if p.config.fix4Enabled {
		fmt.Fprintf(fw, "#define CIDR4_FILTER\n")
		if p.config.dyn4Enabled {
			fmt.Fprintf(fw, "#define CIDR4_LPM_PREFILTER\n")
		}
	}
	if p.config.fix6Enabled {
		fmt.Fprintf(fw, "#define CIDR6_FILTER\n")
		if p.config.dyn6Enabled {
			fmt.Fprintf(fw, "#define CIDR6_LPM_PREFILTER\n")
		}
	}
}

func (p *PreFilter) dumpOneMap(which preFilterMapType, to []string) []string {
	if p.maps[which] == nil {
		return to
	}
	return p.maps[which].CIDRDump(to)
}

// Dump dumps revision and CIDRs as string slice of all participating maps
func (p *PreFilter) Dump(to []string) ([]string, int64) {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	for i := prefixesV4Dyn; i < mapCount; i++ {
		to = p.dumpOneMap(i, to)
	}
	return to, p.revision
}

func (p *PreFilter) selectMap(ones, bits int) preFilterMapType {
	if bits == net.IPv4len*8 {
		if ones == bits {
			return prefixesV4Fix
		}
		return prefixesV4Dyn
	} else if bits == net.IPv6len*8 {
		if ones == bits {
			return prefixesV6Fix
		}
		return prefixesV6Dyn
	} else {
		return mapCount
	}
}

// Insert inserts slice of CIDRs (doh!) for the latest revision
func (p *PreFilter) Insert(revision int64, cidrs []net.IPNet) error {
	var undoQueue []net.IPNet
	var ret error

	p.mutex.Lock()
	defer p.mutex.Unlock()
	if revision != 0 && p.revision != revision {
		return fmt.Errorf("Latest revision is %d not %d", p.revision, revision)
	}
	for _, cidr := range cidrs {
		ones, bits := cidr.Mask.Size()
		which := p.selectMap(ones, bits)
		if which == mapCount || p.maps[which] == nil {
			ret = fmt.Errorf("No map enabled for CIDR string %s", cidr.String())
			break
		}
		err := p.maps[which].InsertCIDR(cidr)
		if err != nil {
			ret = fmt.Errorf("Error inserting CIDR string %s: %s", cidr.String(), err)
			break
		} else {
			undoQueue = append(undoQueue, cidr)
		}
	}
	if ret == nil {
		p.revision++
		return ret
	}
	for _, cidr := range undoQueue {
		ones, bits := cidr.Mask.Size()
		which := p.selectMap(ones, bits)
		p.maps[which].DeleteCIDR(cidr)
	}
	return ret
}

// Delete deletes slice of CIDRs (doh!) for the latest revision
func (p *PreFilter) Delete(revision int64, cidrs []net.IPNet) error {
	var undoQueue []net.IPNet
	var ret error

	p.mutex.Lock()
	defer p.mutex.Unlock()
	if revision != 0 && p.revision != revision {
		return fmt.Errorf("Latest revision is %d not %d", p.revision, revision)
	}
	for _, cidr := range cidrs {
		ones, bits := cidr.Mask.Size()
		which := p.selectMap(ones, bits)
		if which == mapCount || p.maps[which] == nil {
			return fmt.Errorf("No map enabled for CIDR string %s", cidr.String())
		}
		// Lets check obvious cases first, so we don't need to painfully unroll
		if p.maps[which].CIDRExists(cidr) == false {
			return fmt.Errorf("No map entry for CIDR string %s", cidr.String())
		}
	}
	for _, cidr := range cidrs {
		ones, bits := cidr.Mask.Size()
		which := p.selectMap(ones, bits)
		err := p.maps[which].DeleteCIDR(cidr)
		if err != nil {
			ret = fmt.Errorf("Error deleting CIDR string %s: %s", cidr.String(), err)
			break
		} else {
			undoQueue = append(undoQueue, cidr)
		}
	}
	if ret == nil {
		p.revision++
		return ret
	}
	for _, cidr := range undoQueue {
		ones, bits := cidr.Mask.Size()
		which := p.selectMap(ones, bits)
		p.maps[which].InsertCIDR(cidr)
	}
	return ret
}

func (p *PreFilter) initOneMap(which preFilterMapType) error {
	var prefixdyn bool
	var prefixlen int
	var maxelems uint32
	var path string
	var err error
	var skip bool

	switch which {
	case prefixesV4Dyn:
		prefixlen = net.IPv4len * 8
		prefixdyn = true
		maxelems = maxLKeys
		path = bpf.MapPath(cidrmap.MapName + "v4_dyn")
		skip = p.config.dyn4Enabled == false
	case prefixesV4Fix:
		prefixlen = net.IPv4len * 8
		prefixdyn = false
		maxelems = maxHKeys
		path = bpf.MapPath(cidrmap.MapName + "v4_fix")
		skip = p.config.fix4Enabled == false
	case prefixesV6Dyn:
		prefixlen = net.IPv6len * 8
		prefixdyn = true
		maxelems = maxLKeys
		path = bpf.MapPath(cidrmap.MapName + "v6_dyn")
		skip = p.config.dyn6Enabled == false
	case prefixesV6Fix:
		prefixlen = net.IPv6len * 8
		prefixdyn = false
		maxelems = maxHKeys
		path = bpf.MapPath(cidrmap.MapName + "v6_fix")
		skip = p.config.fix4Enabled == false
	}
	if skip == false {
		p.maps[which], _, err = cidrmap.OpenMapElems(path, prefixlen, prefixdyn, maxelems)
		if err != nil {
			return err
		}
	}
	return nil
}

func (p *PreFilter) init() (*PreFilter, error) {
	for i := prefixesV4Dyn; i < mapCount; i++ {
		if err := p.initOneMap(i); err != nil {
			return nil, err
		}
	}
	return p, nil
}

// ProbePreFilter checks whether XDP mode is supported on given device
func ProbePreFilter(device, mode string) error {
	cmd := exec.Command("ip", "-force", "link", "set", "dev", device, mode, "off")
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("Cannot run ip command: %v", err)
	}
	if err := cmd.Wait(); err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				switch status.ExitStatus() {
				case 2:
					return fmt.Errorf("Mode %s not supported on device %s", mode, device)
				default:
					return fmt.Errorf("Prefilter not supported on OS")
				}
			}
		} else {
			return fmt.Errorf("Cannot wait for ip command: %v", err)
		}
	}
	return nil
}

// NewPreFilter returns prefilter handle
func NewPreFilter() (*PreFilter, error) {
	// dyn{4,6} officially disabled for now due to missing
	// dump (get_next_key) from kernel side.
	c := preFilterConfig{
		dyn4Enabled: false,
		dyn6Enabled: false,
		fix4Enabled: true,
		fix6Enabled: true,
	}
	p := &PreFilter{
		revision: 1,
		config:   c,
	}
	// Only needed here given we access pinned maps.
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return p.init()
}
<commit_msg>cilium: probe and enable LPM map in prefilter<commit_after>// Copyright 2017-2018 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.

package prefilter

import (
	"fmt"
	"io"
	"net"
	"os/exec"
	"path"
	"syscall"
	"unsafe"

	"github.com/cilium/cilium/pkg/bpf"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/maps/cidrmap"
)

type preFilterMapType int

const (
	prefixesV4Dyn preFilterMapType = iota
	prefixesV4Fix
	prefixesV6Dyn
	prefixesV6Fix
	mapCount
)

const (
	// Arbitrary chosen for now. We don't preallocate elements,
	// so we could bump the limit if needed later on.
	maxLKeys = 1024 * 64
	maxHKeys = 1024 * 1024 * 20
)

type preFilterMaps [mapCount]*cidrmap.CIDRMap

type preFilterConfig struct {
	dyn4Enabled bool
	dyn6Enabled bool
	fix4Enabled bool
	fix6Enabled bool
}

// PreFilter holds global info on related CIDR maps participating in prefilter
type PreFilter struct {
	maps     preFilterMaps
	config   preFilterConfig
	revision int64
	mutex    lock.RWMutex
}

// WriteConfig dumps the configuration for the corresponding header file
func (p *PreFilter) WriteConfig(fw io.Writer) {
	p.mutex.RLock()
	defer p.mutex.RUnlock()

	fmt.Fprintf(fw, "#define CIDR4_HMAP_ELEMS %d\n", maxHKeys)
	fmt.Fprintf(fw, "#define CIDR4_LMAP_ELEMS %d\n", maxLKeys)

	fmt.Fprintf(fw, "#define CIDR4_HMAP_NAME %s\n", path.Base(p.maps[prefixesV4Fix].String()))
	fmt.Fprintf(fw, "#define CIDR4_LMAP_NAME %s\n", path.Base(p.maps[prefixesV4Dyn].String()))
	fmt.Fprintf(fw, "#define CIDR6_HMAP_NAME %s\n", path.Base(p.maps[prefixesV6Fix].String()))
	fmt.Fprintf(fw, "#define CIDR6_LMAP_NAME %s\n", path.Base(p.maps[prefixesV6Dyn].String()))

	if p.config.fix4Enabled {
		fmt.Fprintf(fw, "#define CIDR4_FILTER\n")
		if p.config.dyn4Enabled {
			fmt.Fprintf(fw, "#define CIDR4_LPM_PREFILTER\n")
		}
	}
	if p.config.fix6Enabled {
		fmt.Fprintf(fw, "#define CIDR6_FILTER\n")
		if p.config.dyn6Enabled {
			fmt.Fprintf(fw, "#define CIDR6_LPM_PREFILTER\n")
		}
	}
}

func (p *PreFilter) dumpOneMap(which preFilterMapType, to []string) []string {
	if p.maps[which] == nil {
		return to
	}
	return p.maps[which].CIDRDump(to)
}

// Dump dumps revision and CIDRs as string slice of all participating maps
func (p *PreFilter) Dump(to []string) ([]string, int64) {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	for i := prefixesV4Dyn; i < mapCount; i++ {
		to = p.dumpOneMap(i, to)
	}
	return to, p.revision
}

func (p *PreFilter) selectMap(ones, bits int) preFilterMapType {
	if bits == net.IPv4len*8 {
		if ones == bits {
			return prefixesV4Fix
		}
		return prefixesV4Dyn
	} else if bits == net.IPv6len*8 {
		if ones == bits {
			return prefixesV6Fix
		}
		return prefixesV6Dyn
	} else {
		return mapCount
	}
}

// Insert inserts slice of CIDRs (doh!) for the latest revision
func (p *PreFilter) Insert(revision int64, cidrs []net.IPNet) error {
	var undoQueue []net.IPNet
	var ret error

	p.mutex.Lock()
	defer p.mutex.Unlock()
	if revision != 0 && p.revision != revision {
		return fmt.Errorf("Latest revision is %d not %d", p.revision, revision)
	}
	for _, cidr := range cidrs {
		ones, bits := cidr.Mask.Size()
		which := p.selectMap(ones, bits)
		if which == mapCount || p.maps[which] == nil {
			ret = fmt.Errorf("No map enabled for CIDR string %s", cidr.String())
			break
		}
		err := p.maps[which].InsertCIDR(cidr)
		if err != nil {
			ret = fmt.Errorf("Error inserting CIDR string %s: %s", cidr.String(), err)
			break
		} else {
			undoQueue = append(undoQueue, cidr)
		}
	}
	if ret == nil {
		p.revision++
		return ret
	}
	for _, cidr := range undoQueue {
		ones, bits := cidr.Mask.Size()
		which := p.selectMap(ones, bits)
		p.maps[which].DeleteCIDR(cidr)
	}
	return ret
}

// Delete deletes slice of CIDRs (doh!) for the latest revision
func (p *PreFilter) Delete(revision int64, cidrs []net.IPNet) error {
	var undoQueue []net.IPNet
	var ret error

	p.mutex.Lock()
	defer p.mutex.Unlock()
	if revision != 0 && p.revision != revision {
		return fmt.Errorf("Latest revision is %d not %d", p.revision, revision)
	}
	for _, cidr := range cidrs {
		ones, bits := cidr.Mask.Size()
		which := p.selectMap(ones, bits)
		if which == mapCount || p.maps[which] == nil {
			return fmt.Errorf("No map enabled for CIDR string %s", cidr.String())
		}
		// Lets check obvious cases first, so we don't need to painfully unroll
		if p.maps[which].CIDRExists(cidr) == false {
			return fmt.Errorf("No map entry for CIDR string %s", cidr.String())
		}
	}
	for _, cidr := range cidrs {
		ones, bits := cidr.Mask.Size()
		which := p.selectMap(ones, bits)
		err := p.maps[which].DeleteCIDR(cidr)
		if err != nil {
			ret = fmt.Errorf("Error deleting CIDR string %s: %s", cidr.String(), err)
			break
		} else {
			undoQueue = append(undoQueue, cidr)
		}
	}
	if ret == nil {
		p.revision++
		return ret
	}
	for _, cidr := range undoQueue {
		ones, bits := cidr.Mask.Size()
		which := p.selectMap(ones, bits)
		p.maps[which].InsertCIDR(cidr)
	}
	return ret
}

func (p *PreFilter) initOneMap(which preFilterMapType) error {
	var prefixdyn bool
	var prefixlen int
	var maxelems uint32
	var path string
	var err error
	var skip bool

	switch which {
	case prefixesV4Dyn:
		prefixlen = net.IPv4len * 8
		prefixdyn = true
		maxelems = maxLKeys
		path = bpf.MapPath(cidrmap.MapName + "v4_dyn")
		skip = p.config.dyn4Enabled == false
	case prefixesV4Fix:
		prefixlen = net.IPv4len * 8
		prefixdyn = false
		maxelems = maxHKeys
		path = bpf.MapPath(cidrmap.MapName + "v4_fix")
		skip = p.config.fix4Enabled == false
	case prefixesV6Dyn:
		prefixlen = net.IPv6len * 8
		prefixdyn = true
		maxelems = maxLKeys
		path = bpf.MapPath(cidrmap.MapName + "v6_dyn")
		skip = p.config.dyn6Enabled == false
	case prefixesV6Fix:
		prefixlen = net.IPv6len * 8
		prefixdyn = false
		maxelems = maxHKeys
		path = bpf.MapPath(cidrmap.MapName + "v6_fix")
		skip = p.config.fix4Enabled == false
	}
	if skip == false {
		p.maps[which], _, err = cidrmap.OpenMapElems(path, prefixlen, prefixdyn, maxelems)
		if err != nil {
			return err
		}
	}
	return nil
}

func (p *PreFilter) init() (*PreFilter, error) {
	for i := prefixesV4Dyn; i < mapCount; i++ {
		if err := p.initOneMap(i); err != nil {
			return nil, err
		}
	}
	return p, nil
}

// ProbePreFilter checks whether XDP mode is supported on given device
func ProbePreFilter(device, mode string) error {
	cmd := exec.Command("ip", "-force", "link", "set", "dev", device, mode, "off")
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("Cannot run ip command: %v", err)
	}
	if err := cmd.Wait(); err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				switch status.ExitStatus() {
				case 2:
					return fmt.Errorf("Mode %s not supported on device %s", mode, device)
				default:
					return fmt.Errorf("Prefilter not supported on OS")
				}
			}
		} else {
			return fmt.Errorf("Cannot wait for ip command: %v", err)
		}
	}
	return nil
}

type probeKey struct {
	Prefixlen uint32
	Key       uint32
}

type probeValue struct {
	Value uint32
}

func (p *probeKey) String() string             { return fmt.Sprintf("key=%d", p.Key) }
func (p *probeKey) GetKeyPtr() unsafe.Pointer  { return unsafe.Pointer(p) }
func (p *probeKey) NewValue() bpf.MapValue     { return &probeValue{} }
func (p *probeKey) DeepCopyMapKey() bpf.MapKey { return &probeKey{p.Prefixlen, p.Key} }

func (p *probeValue) String() string                 { return fmt.Sprintf("value=%d", p.Value) }
func (p *probeValue) GetValuePtr() unsafe.Pointer    { return unsafe.Pointer(p) }
func (p *probeValue) DeepCopyMapValue() bpf.MapValue { return &probeValue{p.Value} }

func ProbePreFilterDyn() bool {
	m := bpf.NewMap("cilium_test", bpf.MapTypeLPMTrie,
		&probeKey{}, int(unsafe.Sizeof(probeKey{})),
		&probeValue{}, int(unsafe.Sizeof(probeValue{})),
		1, bpf.BPF_F_NO_PREALLOC, 0, bpf.ConvertKeyValue).WithCache()
	_, err := m.OpenOrCreateUnpinned()
	defer m.Close()
	if err != nil {
		return false
	}
	err = bpf.UpdateElement(m.GetFd(), unsafe.Pointer(&probeKey{}),
		unsafe.Pointer(&probeValue{}), bpf.BPF_ANY)
	if err != nil {
		return false
	}
	err = bpf.GetNextKey(m.GetFd(), nil, unsafe.Pointer(&probeKey{}))
	if err != nil {
		return false
	}
	return true
}

// NewPreFilter returns prefilter handle
func NewPreFilter() (*PreFilter, error) {
	haveDyn := ProbePreFilterDyn()
	c := preFilterConfig{
		dyn4Enabled: haveDyn,
		dyn6Enabled: haveDyn,
		fix4Enabled: true,
		fix6Enabled: true,
	}
	p := &PreFilter{
		revision: 1,
		config:   c,
	}
	// Only needed here given we access pinned maps.
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return p.init()
}
<|endoftext|>


<commit_before>//
// Copyright 2022 The AFF 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 collector

type Collector interface{}
<commit_msg>add new collector interface (#18)<commit_after>//
// Copyright 2022 The AFF 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 collector

import (
	"context"

	"github.com/guacsec/guac/pkg/ingestor/processor"
)

const (
	BufferChannelSize int = 1000
)

type Collector interface {
	// RetrieveArtifacts collects the documents from the collector. It emits each collected
	// document through the channel to be collected and processed by the upstream processor.
	// The function should block until all the artifacts are collected and return a nil error
	// or return an error from the collector crashing. This function can keep running and check
	// for new artifacts as they are being uploaded by polling on an interval or run once and
	// grab all the artifacts and end.
	RetrieveArtifacts(ctx context.Context, docChannel chan<- *processor.Document) error
	// IsDone indicated when the collector is finished collecting
	IsDone() bool
	// Type returns the collector type
	Type() string
}

var (
	documentCollector = map[string]Collector{}
)

// Collect takes all the collectors and starts collecting artifacts
func Collect(ctx context.Context) (<-chan *processor.Document, <-chan error, int, error) {
	// docChan to collect artifacts
	docChan := make(chan *processor.Document, BufferChannelSize)
	// errChan to receive error from collectors
	errChan := make(chan error, len(documentCollector))
	for _, collector := range documentCollector {
		c := collector
		go func() {
			errChan <- c.RetrieveArtifacts(ctx, docChan)
		}()
	}
	return docChan, errChan, len(documentCollector), nil
}
<|endoftext|>


<commit_before>// Package rows provides Json-schema based database rows to arbitrary struct
// scanner generation
package generows

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"

	"go/format"

	"github.com/cihangir/gene/generators/common"
	"github.com/cihangir/schema"
)

type Generator struct{}

// Generate generates and writes the errors of the schema
func (g *Generator) Generate(context *common.Context, s *schema.Schema) ([]common.Output, error) {
	temp := template.New("rowscanner.tmpl").Funcs(context.TemplateFuncs)
	if _, err := temp.Parse(RowScannerTemplate); err != nil {
		return nil, err
	}

	outputs := make([]common.Output, 0)
	for _, def := range common.SortedObjectSchemas(s.Definitions) {
		data := struct {
			Schema *schema.Schema
		}{
			Schema: def,
		}

		var buf bytes.Buffer

		if err := temp.ExecuteTemplate(&buf, "rowscanner.tmpl", data); err != nil {
			return nil, err
		}

		f, err := format.Source(buf.Bytes())
		if err != nil {
			return nil, err
		}

		outputs = append(outputs, common.Output{
			Content: f,
			Path: fmt.Sprintf(
				"%s%s_rowscanner.go",
				context.Config.Target,
				strings.ToLower(def.Title),
			),
		})
	}

	return outputs, nil
}
<commit_msg>generator/rows: make generation of modules configurable<commit_after>// Package rows provides Json-schema based database rows to arbitrary struct
// scanner generation
package generows

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"

	"go/format"

	"github.com/cihangir/gene/generators/common"
	"github.com/cihangir/schema"
)

type Generator struct{}

// Generate generates and writes the errors of the schema
func (g *Generator) Generate(context *common.Context, s *schema.Schema) ([]common.Output, error) {
	if !common.IsIn("rows", context.Config.Generators...) {
		return nil, nil
	}

	temp := template.New("rowscanner.tmpl").Funcs(context.TemplateFuncs)
	if _, err := temp.Parse(RowScannerTemplate); err != nil {
		return nil, err
	}

	outputs := make([]common.Output, 0)
	for _, def := range common.SortedObjectSchemas(s.Definitions) {
		data := struct {
			Schema *schema.Schema
		}{
			Schema: def,
		}

		var buf bytes.Buffer

		if err := temp.ExecuteTemplate(&buf, "rowscanner.tmpl", data); err != nil {
			return nil, err
		}

		f, err := format.Source(buf.Bytes())
		if err != nil {
			return nil, err
		}

		outputs = append(outputs, common.Output{
			Content: f,
			Path: fmt.Sprintf(
				"%s%s_rowscanner.go",
				context.Config.Target,
				strings.ToLower(def.Title),
			),
		})
	}

	return outputs, nil
}
<|endoftext|>


<commit_before>/*
 * RRDA (RRDA REST DNS API) 1.1.0
 * Copyright (c) 2012-2020, Frederic Cambus
 * https://www.statdns.com
 *
 * Created: 2012-03-11
 * Last Updated: 2020-03-16
 *
 * RRDA is released under the BSD 2-Clause license.
 * See LICENSE file for details.
 */

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/miekg/dns"
	"golang.org/x/net/idna"
	"io"
	"net"
	"net/http"
	"net/http/fcgi"
	"os"
	"strings"
)

type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

type Question struct {
	Name  string `json:"name"`
	Type  string `json:"type"`
	Class string `json:"class"`
}

type Section struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Class    string `json:"class"`
	Ttl      uint32 `json:"ttl"`
	Rdlength uint16 `json:"rdlength"`
	Rdata    string `json:"rdata"`
}

type Message struct {
	Question   []*Question `json:"question"`
	Answer     []*Section  `json:"answer"`
	Authority  []*Section  `json:"authority,omitempty"`
	Additional []*Section  `json:"additional,omitempty"`
}

// Return rdata
func rdata(RR dns.RR) string {
	return strings.Replace(RR.String(), RR.Header().String(), "", -1)
}

// Return an HTTP Error along with a JSON-encoded error message
func error(w http.ResponseWriter, status int, code int, message string) {
	if output, err := json.Marshal(Error{Code: code, Message: message}); err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(status)
		fmt.Fprintln(w, string(output))
	}
}

// Generate JSON output
func jsonify(w http.ResponseWriter, r *http.Request, question []dns.Question, answer []dns.RR, authority []dns.RR, additional []dns.RR) {
	var answerArray, authorityArray, additionalArray []*Section

	callback := r.URL.Query().Get("callback")

	for _, answer := range answer {
		answerArray = append(answerArray, &Section{answer.Header().Name, dns.TypeToString[answer.Header().Rrtype], dns.ClassToString[answer.Header().Class], answer.Header().Ttl, answer.Header().Rdlength, rdata(answer)})
	}

	for _, authority := range authority {
		authorityArray = append(authorityArray, &Section{authority.Header().Name, dns.TypeToString[authority.Header().Rrtype], dns.ClassToString[authority.Header().Class], authority.Header().Ttl, authority.Header().Rdlength, rdata(authority)})
	}

	for _, additional := range additional {
		additionalArray = append(additionalArray, &Section{additional.Header().Name, dns.TypeToString[additional.Header().Rrtype], dns.ClassToString[additional.Header().Class], additional.Header().Ttl, additional.Header().Rdlength, rdata(additional)})
	}

	if json, err := json.MarshalIndent(Message{[]*Question{&Question{question[0].Name, dns.TypeToString[question[0].Qtype], dns.ClassToString[question[0].Qclass]}}, answerArray, authorityArray, additionalArray}, "", "    "); err == nil {
		if callback != "" {
			io.WriteString(w, callback+"("+string(json)+");")
		} else {
			io.WriteString(w, string(json))
		}
	}
}

// Perform DNS resolution
func resolve(w http.ResponseWriter, r *http.Request, server string, domain string, querytype uint16) {
	m := new(dns.Msg)
	m.SetQuestion(domain, querytype)
	m.MsgHdr.RecursionDesired = true

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Access-Control-Allow-Origin", "*")

	c := new(dns.Client)

Redo:
	if in, _, err := c.Exchange(m, server); err == nil { // Second return value is RTT, not used for now
		if in.MsgHdr.Truncated {
			c.Net = "tcp"
			goto Redo
		}

		switch in.MsgHdr.Rcode {
		case dns.RcodeServerFailure:
			error(w, 500, 502, "The name server encountered an internal failure while processing this request (SERVFAIL)")
		case dns.RcodeNameError:
			error(w, 500, 503, "Some name that ought to exist, does not exist (NXDOMAIN)")
		case dns.RcodeRefused:
			error(w, 500, 505, "The name server refuses to perform the specified operation for policy or security reasons (REFUSED)")
		default:
			jsonify(w, r, in.Question, in.Answer, in.Ns, in.Extra)
		}
	} else {
		error(w, 500, 501, "DNS server could not be reached")
	}
}

// Handler for DNS queries
func query(w http.ResponseWriter, r *http.Request) {
	server := r.URL.Query().Get(":server")
	domain := dns.Fqdn(r.URL.Query().Get(":domain"))
	querytype := r.URL.Query().Get(":querytype")

	if domain, err := idna.ToASCII(domain); err == nil { // Valid domain name (ASCII or IDN)
		if _, isDomain := dns.IsDomainName(domain); isDomain { // Well-formed domain name
			if querytype, ok := dns.StringToType[strings.ToUpper(querytype)]; ok { // Valid DNS query type
				resolve(w, r, server, domain, querytype)
			} else {
				error(w, 400, 404, "Invalid DNS query type")
			}
		} else {
			error(w, 400, 402, "Input string is not a well-formed domain name")
		}
	} else {
		error(w, 400, 401, "Input string could not be parsed")
	}
}

// Handler for reverse DNS queries
func ptr(w http.ResponseWriter, r *http.Request) {
	server := r.URL.Query().Get(":server")
	ip := r.URL.Query().Get(":ip")

	if arpa, err := dns.ReverseAddr(ip); err == nil { // Valid IP address (IPv4 or IPv6)
		resolve(w, r, server, arpa, dns.TypePTR)
	} else {
		error(w, 400, 403, "Input string is not a valid IP address")
	}
}

func main() {
	fastcgi := flag.Bool("fastcgi", false, "Enable FastCGI mode")
	host := flag.String("host", "127.0.0.1", "Set the server host")
	port := flag.String("port", "8080", "Set the server port")
	version := flag.Bool("version", false, "Display version")

	mode := "HTTP"

	flag.Usage = func() {
		fmt.Println("\nUSAGE:")
		flag.PrintDefaults()
	}
	flag.Parse()

	if *version {
		fmt.Println("RRDA 1.1.0")
		os.Exit(0)
	}

	if *fastcgi {
		mode = "FastCGI"
	}

	address := *host + ":" + *port

	fmt.Println("Listening on ("+mode+" mode):", address)

	m := pat.New()
	m.Get("/:server/x/:ip", http.HandlerFunc(ptr))
	m.Get("/:server/:domain/:querytype", http.HandlerFunc(query))

	if *fastcgi {
		listener, _ := net.Listen("tcp", address)

		if err := fcgi.Serve(listener, m); err != nil {
			fmt.Println("\nERROR:", err)
			os.Exit(1)
		}
	} else {
		if err := http.ListenAndServe(address, m); err != nil {
			fmt.Println("\nERROR:", err)
			os.Exit(1)
		}
	}
}
<commit_msg>Add SPDX short license identifier in source file.<commit_after>/*
 * RRDA (RRDA REST DNS API) 1.1.0
 * Copyright (c) 2012-2020, Frederic Cambus
 * https://www.statdns.com
 *
 * Created: 2012-03-11
 * Last Updated: 2020-03-16
 *
 * RRDA is released under the BSD 2-Clause license.
 * See LICENSE file for details.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/miekg/dns"
	"golang.org/x/net/idna"
	"io"
	"net"
	"net/http"
	"net/http/fcgi"
	"os"
	"strings"
)

type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

type Question struct {
	Name  string `json:"name"`
	Type  string `json:"type"`
	Class string `json:"class"`
}

type Section struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Class    string `json:"class"`
	Ttl      uint32 `json:"ttl"`
	Rdlength uint16 `json:"rdlength"`
	Rdata    string `json:"rdata"`
}

type Message struct {
	Question   []*Question `json:"question"`
	Answer     []*Section  `json:"answer"`
	Authority  []*Section  `json:"authority,omitempty"`
	Additional []*Section  `json:"additional,omitempty"`
}

// Return rdata
func rdata(RR dns.RR) string {
	return strings.Replace(RR.String(), RR.Header().String(), "", -1)
}

// Return an HTTP Error along with a JSON-encoded error message
func error(w http.ResponseWriter, status int, code int, message string) {
	if output, err := json.Marshal(Error{Code: code, Message: message}); err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(status)
		fmt.Fprintln(w, string(output))
	}
}

// Generate JSON output
func jsonify(w http.ResponseWriter, r *http.Request, question []dns.Question, answer []dns.RR, authority []dns.RR, additional []dns.RR) {
	var answerArray, authorityArray, additionalArray []*Section

	callback := r.URL.Query().Get("callback")

	for _, answer := range answer {
		answerArray = append(answerArray, &Section{answer.Header().Name, dns.TypeToString[answer.Header().Rrtype], dns.ClassToString[answer.Header().Class], answer.Header().Ttl, answer.Header().Rdlength, rdata(answer)})
	}

	for _, authority := range authority {
		authorityArray = append(authorityArray, &Section{authority.Header().Name, dns.TypeToString[authority.Header().Rrtype], dns.ClassToString[authority.Header().Class], authority.Header().Ttl, authority.Header().Rdlength, rdata(authority)})
	}

	for _, additional := range additional {
		additionalArray = append(additionalArray, &Section{additional.Header().Name, dns.TypeToString[additional.Header().Rrtype], dns.ClassToString[additional.Header().Class], additional.Header().Ttl, additional.Header().Rdlength, rdata(additional)})
	}

	if json, err := json.MarshalIndent(Message{[]*Question{&Question{question[0].Name, dns.TypeToString[question[0].Qtype], dns.ClassToString[question[0].Qclass]}}, answerArray, authorityArray, additionalArray}, "", "    "); err == nil {
		if callback != "" {
			io.WriteString(w, callback+"("+string(json)+");")
		} else {
			io.WriteString(w, string(json))
		}
	}
}

// Perform DNS resolution
func resolve(w http.ResponseWriter, r *http.Request, server string, domain string, querytype uint16) {
	m := new(dns.Msg)
	m.SetQuestion(domain, querytype)
	m.MsgHdr.RecursionDesired = true

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Access-Control-Allow-Origin", "*")

	c := new(dns.Client)

Redo:
	if in, _, err := c.Exchange(m, server); err == nil { // Second return value is RTT, not used for now
		if in.MsgHdr.Truncated {
			c.Net = "tcp"
			goto Redo
		}

		switch in.MsgHdr.Rcode {
		case dns.RcodeServerFailure:
			error(w, 500, 502, "The name server encountered an internal failure while processing this request (SERVFAIL)")
		case dns.RcodeNameError:
			error(w, 500, 503, "Some name that ought to exist, does not exist (NXDOMAIN)")
		case dns.RcodeRefused:
			error(w, 500, 505, "The name server refuses to perform the specified operation for policy or security reasons (REFUSED)")
		default:
			jsonify(w, r, in.Question, in.Answer, in.Ns, in.Extra)
		}
	} else {
		error(w, 500, 501, "DNS server could not be reached")
	}
}

// Handler for DNS queries
func query(w http.ResponseWriter, r *http.Request) {
	server := r.URL.Query().Get(":server")
	domain := dns.Fqdn(r.URL.Query().Get(":domain"))
	querytype := r.URL.Query().Get(":querytype")

	if domain, err := idna.ToASCII(domain); err == nil { // Valid domain name (ASCII or IDN)
		if _, isDomain := dns.IsDomainName(domain); isDomain { // Well-formed domain name
			if querytype, ok := dns.StringToType[strings.ToUpper(querytype)]; ok { // Valid DNS query type
				resolve(w, r, server, domain, querytype)
			} else {
				error(w, 400, 404, "Invalid DNS query type")
			}
		} else {
			error(w, 400, 402, "Input string is not a well-formed domain name")
		}
	} else {
		error(w, 400, 401, "Input string could not be parsed")
	}
}

// Handler for reverse DNS queries
func ptr(w http.ResponseWriter, r *http.Request) {
	server := r.URL.Query().Get(":server")
	ip := r.URL.Query().Get(":ip")

	if arpa, err := dns.ReverseAddr(ip); err == nil { // Valid IP address (IPv4 or IPv6)
		resolve(w, r, server, arpa, dns.TypePTR)
	} else {
		error(w, 400, 403, "Input string is not a valid IP address")
	}
}

func main() {
	fastcgi := flag.Bool("fastcgi", false, "Enable FastCGI mode")
	host := flag.String("host", "127.0.0.1", "Set the server host")
	port := flag.String("port", "8080", "Set the server port")
	version := flag.Bool("version", false, "Display version")

	mode := "HTTP"

	flag.Usage = func() {
		fmt.Println("\nUSAGE:")
		flag.PrintDefaults()
	}
	flag.Parse()

	if *version {
		fmt.Println("RRDA 1.1.0")
		os.Exit(0)
	}

	if *fastcgi {
		mode = "FastCGI"
	}

	address := *host + ":" + *port

	fmt.Println("Listening on ("+mode+" mode):", address)

	m := pat.New()
	m.Get("/:server/x/:ip", http.HandlerFunc(ptr))
	m.Get("/:server/:domain/:querytype", http.HandlerFunc(query))

	if *fastcgi {
		listener, _ := net.Listen("tcp", address)

		if err := fcgi.Serve(listener, m); err != nil {
			fmt.Println("\nERROR:", err)
			os.Exit(1)
		}
	} else {
		if err := http.ListenAndServe(address, m); err != nil {
			fmt.Println("\nERROR:", err)
			os.Exit(1)
		}
	}
}
<|endoftext|>


<commit_before>package ginkgo

import (
	"bytes"
	"fmt"
	"sort"
	"time"

	"github.com/openshift/origin/pkg/monitor"
)

func createEventsForTests(tests []*testCase) []*monitor.EventInterval {
	eventsForTests := []*monitor.EventInterval{}
	for _, test := range tests {
		if !test.failed {
			continue
		}
		eventsForTests = append(eventsForTests,
			&monitor.EventInterval{
				From: test.start,
				To:   test.end,
				Condition: &monitor.Condition{
					Level:   monitor.Info,
					Locator: fmt.Sprintf("test=%q", test.name),
					Message: "running",
				},
			},
			&monitor.EventInterval{
				From: test.end,
				To:   test.end,
				Condition: &monitor.Condition{
					Level:   monitor.Info,
					Locator: fmt.Sprintf("test=%q", test.name),
					Message: "failed",
				},
			},
		)
	}
	return eventsForTests
}

func createSyntheticTestsFromMonitor(m *monitor.Monitor, eventsForTests []*monitor.EventInterval, monitorDuration time.Duration) ([]*JUnitTestCase, *bytes.Buffer, *bytes.Buffer) {
	var syntheticTestResults []*JUnitTestCase

	events := m.Events(time.Time{}, time.Time{})
	events = append(events, eventsForTests...)
	sort.Sort(events)

	buf, errBuf := &bytes.Buffer{}, &bytes.Buffer{}
	fmt.Fprintf(buf, "\nTimeline:\n\n")
	errorCount := 0
	for _, event := range events {
		if event.Level == monitor.Error {
			errorCount++
			fmt.Fprintln(errBuf, event.String())
		}
		fmt.Fprintln(buf, event.String())
	}
	fmt.Fprintln(buf)

	if errorCount > 0 {
		syntheticTestResults = append(
			syntheticTestResults,
			&JUnitTestCase{
				Name:      "[sig-arch] Monitor cluster while tests execute",
				SystemOut: buf.String(),
				Duration:  monitorDuration.Seconds(),
				FailureOutput: &FailureOutput{
					Output: fmt.Sprintf("%d error level events were detected during this test run:\n\n%s", errorCount, errBuf.String()),
				},
			},
			// write a passing test to trigger detection of this issue as a flake, indicating we have no idea whether
			// these are actual failures or not
			&JUnitTestCase{
				Name:     "[sig-arch] Monitor cluster while tests execute",
				Duration: monitorDuration.Seconds(),
			},
		)
	}

	return syntheticTestResults, buf, errBuf
}
<commit_msg>add junit for illegal pod state transitions<commit_after>package ginkgo

import (
	"bytes"
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/openshift/origin/pkg/monitor"
)

func createEventsForTests(tests []*testCase) []*monitor.EventInterval {
	eventsForTests := []*monitor.EventInterval{}
	for _, test := range tests {
		if !test.failed {
			continue
		}
		eventsForTests = append(eventsForTests,
			&monitor.EventInterval{
				From: test.start,
				To:   test.end,
				Condition: &monitor.Condition{
					Level:   monitor.Info,
					Locator: fmt.Sprintf("test=%q", test.name),
					Message: "running",
				},
			},
			&monitor.EventInterval{
				From: test.end,
				To:   test.end,
				Condition: &monitor.Condition{
					Level:   monitor.Info,
					Locator: fmt.Sprintf("test=%q", test.name),
					Message: "failed",
				},
			},
		)
	}
	return eventsForTests
}

func createSyntheticTestsFromMonitor(m *monitor.Monitor, eventsForTests []*monitor.EventInterval, monitorDuration time.Duration) ([]*JUnitTestCase, *bytes.Buffer, *bytes.Buffer) {
	var syntheticTestResults []*JUnitTestCase

	events := m.Events(time.Time{}, time.Time{})
	events = append(events, eventsForTests...)
	sort.Sort(events)

	buf, errBuf := &bytes.Buffer{}, &bytes.Buffer{}
	fmt.Fprintf(buf, "\nTimeline:\n\n")
	errorCount := 0
	for _, event := range events {
		if event.Level == monitor.Error {
			errorCount++
			fmt.Fprintln(errBuf, event.String())
		}
		fmt.Fprintln(buf, event.String())
	}
	fmt.Fprintln(buf)

	if errorCount > 0 {
		syntheticTestResults = append(
			syntheticTestResults,
			&JUnitTestCase{
				Name:      "[sig-arch] Monitor cluster while tests execute",
				SystemOut: buf.String(),
				Duration:  monitorDuration.Seconds(),
				FailureOutput: &FailureOutput{
					Output: fmt.Sprintf("%d error level events were detected during this test run:\n\n%s", errorCount, errBuf.String()),
				},
			},
			// write a passing test to trigger detection of this issue as a flake, indicating we have no idea whether
			// these are actual failures or not
			&JUnitTestCase{
				Name:     "[sig-arch] Monitor cluster while tests execute",
				Duration: monitorDuration.Seconds(),
			},
		)
	}

	// check events
	syntheticTestResults = append(syntheticTestResults, testPodTransitions(events)...)

	return syntheticTestResults, buf, errBuf
}

func testPodTransitions(events []*monitor.EventInterval) []*JUnitTestCase {
	success := &JUnitTestCase{Name: "[sig-node] pods should never transition back to pending"}

	failures := []string{}
	for _, event := range events {
		if strings.Contains(event.Message, "pod should not transition") || strings.Contains(event.Message, "pod moved back to Pending") {
			failures = append(failures, fmt.Sprintf("%v - %v", event.Locator, event.Message))
		}
	}
	if len(failures) == 0 {
		return []*JUnitTestCase{success}
	}

	failure := &JUnitTestCase{
		Name:      "[sig-node] pods should never transition back to pending",
		SystemOut: strings.Join(failures, "\n"),
		FailureOutput: &FailureOutput{
			Output: fmt.Sprintf("%d pods illegally transitioned to Pending", len(failures)),
		},
	}

	// write a passing test to trigger detection of this issue as a flake. Doing this first to try to see how frequent the issue actually is
	return []*JUnitTestCase{failure, success}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Rook Authors. All rights reserved.

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 proc

import (
	"errors"
	"os/exec"
	"testing"
	"time"

	"github.com/rook/rook/pkg/util/exec/test"
	"github.com/stretchr/testify/assert"
)

func TestRestartDelay(t *testing.T) {
	var zeroTime time.Time

	// test cases for when last retry check time is not available
	assert.Equal(t, float64(0), calcRetryDelay(0, 0, zeroTime, zeroTime, zeroTime))
	assert.Equal(t, float64(0), calcRetryDelay(0, 2, zeroTime, zeroTime, zeroTime))
	assert.Equal(t, float64(1), calcRetryDelay(2, 0, zeroTime, zeroTime, zeroTime))
	assert.Equal(t, float64(2), calcRetryDelay(2, 1, zeroTime, zeroTime, zeroTime))
	assert.Equal(t, float64(4), calcRetryDelay(2, 2, zeroTime, zeroTime, zeroTime))
	assert.Equal(t, float64(30), calcRetryDelay(2, 5, zeroTime, zeroTime, zeroTime))
	assert.Equal(t, float64(30), calcRetryDelay(2, 100, zeroTime, zeroTime, zeroTime))

	// test cases for when last retry check is available and retry count is 0 and the process hasn't been
	// running long at all. the time delay should be 2^(number of secs since last retry), with a max limit of 30.
	now := time.Now()
	lastStartTime := now
	lastRetryCheck := now
	assert.Equal(t, float64(1), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	lastRetryCheck = now.Add(-1 * time.Second)
	assert.Equal(t, float64(2), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	lastRetryCheck = now.Add(-2 * time.Second)
	assert.Equal(t, float64(4), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	lastRetryCheck = now.Add(-4 * time.Second)
	assert.Equal(t, float64(16), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	lastRetryCheck = now.Add(-5 * time.Second)
	assert.Equal(t, float64(30), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	lastRetryCheck = now.Add(-999999999 * time.Second)
	assert.Equal(t, float64(30), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	// test case for when the process has been running for quite awhile.  we should not delay for very long since we
	// are clearly not in a rapid retry loop.
	lastStartTime = now.Add(-100 * time.Second)
	assert.Equal(t, float64(1), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	// test cases for when both retry count and last retry check are available, retry count should be favored
	lastRetryCheck = now.Add(-4 * time.Second)
	assert.Equal(t, float64(2), calcRetryDelay(2, 1, now, lastStartTime, lastRetryCheck))
}

func TestMonitoredRestart(t *testing.T) {
	executor := &test.MockExecutor{}
	procMgr := New(executor)
	cmd := &exec.Cmd{Args: []string{"/my/path", "1", "2", "3"}}
	proc := newMonitoredProc(procMgr, cmd)
	proc.retrySecondsExponentBase = 0.0

	commands := 0
	executor.MockStartExecuteCommand = func(debug bool, name string, command string, args ...string) (*exec.Cmd, error) {
		commands++
		cmd := &exec.Cmd{Args: append([]string{command}, args...)}
		switch {
		case commands == 1:
			return cmd, errors.New("test failure")
		case commands == 2:
			assert.Equal(t, 1, proc.retries)
			break
		}
		return cmd, nil
	}

	iter := 0
	proc.waitForExit = func() {
		logger.Debugf("waited for process")
		assert.True(t, proc.monitor)
		switch {
		case iter == 0:
		case iter == 1:
			assert.True(t, proc.monitor)
			logger.Debugf("stop monitoring")
			proc.monitor = false
			break
		}
		iter++
	}

	proc.Monitor("testproc")
	assert.False(t, proc.monitor)
	assert.Equal(t, proc.retries, 0)
	assert.Equal(t, proc.totalRetries, 2)
}
<commit_msg>cleanup: cleanup codebase<commit_after>/*
Copyright 2016 The Rook Authors. All rights reserved.

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 proc

import (
	"errors"
	"os/exec"
	"testing"
	"time"

	"github.com/rook/rook/pkg/util/exec/test"
	"github.com/stretchr/testify/assert"
)

func TestRestartDelay(t *testing.T) {
	var zeroTime time.Time

	// test cases for when last retry check time is not available
	assert.Equal(t, float64(0), calcRetryDelay(0, 0, zeroTime, zeroTime, zeroTime))
	assert.Equal(t, float64(0), calcRetryDelay(0, 2, zeroTime, zeroTime, zeroTime))
	assert.Equal(t, float64(1), calcRetryDelay(2, 0, zeroTime, zeroTime, zeroTime))
	assert.Equal(t, float64(2), calcRetryDelay(2, 1, zeroTime, zeroTime, zeroTime))
	assert.Equal(t, float64(4), calcRetryDelay(2, 2, zeroTime, zeroTime, zeroTime))
	assert.Equal(t, float64(30), calcRetryDelay(2, 5, zeroTime, zeroTime, zeroTime))
	assert.Equal(t, float64(30), calcRetryDelay(2, 100, zeroTime, zeroTime, zeroTime))

	// test cases for when last retry check is available and retry count is 0 and the process hasn't been
	// running long at all. the time delay should be 2^(number of secs since last retry), with a max limit of 30.
	now := time.Now()
	lastStartTime := now
	lastRetryCheck := now
	assert.Equal(t, float64(1), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	lastRetryCheck = now.Add(-1 * time.Second)
	assert.Equal(t, float64(2), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	lastRetryCheck = now.Add(-2 * time.Second)
	assert.Equal(t, float64(4), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	lastRetryCheck = now.Add(-4 * time.Second)
	assert.Equal(t, float64(16), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	lastRetryCheck = now.Add(-5 * time.Second)
	assert.Equal(t, float64(30), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	lastRetryCheck = now.Add(-999999999 * time.Second)
	assert.Equal(t, float64(30), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	// test case for when the process has been running for quite awhile.  we should not delay for very long since we
	// are clearly not in a rapid retry loop.
	lastStartTime = now.Add(-100 * time.Second)
	assert.Equal(t, float64(1), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))

	// test cases for when both retry count and last retry check are available, retry count should be favored
	lastRetryCheck = now.Add(-4 * time.Second)
	assert.Equal(t, float64(2), calcRetryDelay(2, 1, now, lastStartTime, lastRetryCheck))
}

func TestMonitoredRestart(t *testing.T) {
	executor := &test.MockExecutor{}
	procMgr := New(executor)
	cmd := &exec.Cmd{Args: []string{"/my/path", "1", "2", "3"}}
	proc := newMonitoredProc(procMgr, cmd)
	proc.retrySecondsExponentBase = 0.0

	commands := 0
	executor.MockStartExecuteCommand = func(debug bool, name string, command string, args ...string) (*exec.Cmd, error) {
		commands++
		cmd := &exec.Cmd{Args: append([]string{command}, args...)}
		switch {
		case commands == 1:
			return cmd, errors.New("test failure")
		case commands == 2:
			assert.Equal(t, 1, proc.retries)
		}
		return cmd, nil
	}

	iter := 0
	proc.waitForExit = func() {
		logger.Debugf("waited for process")
		assert.True(t, proc.monitor)
		switch {
		case iter == 0:
		case iter == 1:
			assert.True(t, proc.monitor)
			logger.Debugf("stop monitoring")
			proc.monitor = false
		}
		iter++
	}

	proc.Monitor("testproc")
	assert.False(t, proc.monitor)
	assert.Equal(t, proc.retries, 0)
	assert.Equal(t, proc.totalRetries, 2)
}
<|endoftext|>


<commit_before>package main

const releaseVersion = "0.3.0+git"
<commit_msg>chore(release_version): bump to 0.4.0<commit_after>package main

const releaseVersion = "0.4.0"
<|endoftext|>


<commit_before>// Copyright 2016 Palantir Technologies, Inc.
//
// 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 builtintasks

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"sort"
	"strings"

	"github.com/palantir/pkg/matcher"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"gopkg.in/yaml.v2"

	"github.com/palantir/godel/framework/godel/config"
	"github.com/palantir/godel/framework/godellauncher"
)

func UpgradeLegacyConfigTask(upgradeTasks []godellauncher.UpgradeConfigTask) godellauncher.Task {
	const (
		upgradeLegacyConfigCmdName = "upgrade-legacy-config"
		dryRunFlagName             = "dry-run"
		printContentFlagName       = "print-content"
	)

	var (
		dryRunFlagVal       bool
		printContentFlagVal bool
	)

	cmd := &cobra.Command{
		Use:   upgradeLegacyConfigCmdName,
		Short: "Upgrade the legacy configuration",
	}

	cmd.Flags().BoolVar(&dryRunFlagVal, dryRunFlagName, false, "print what the upgrade operation would do without writing changes")
	cmd.Flags().BoolVar(&printContentFlagVal, printContentFlagName, false, "print the content of the changes to stdout in addition to writing them")

	cmd.SilenceErrors = true
	cmd.SilenceUsage = true
	return godellauncher.Task{
		Name:        cmd.Use,
		Description: cmd.Short,
		RunImpl: func(t *godellauncher.Task, global godellauncher.GlobalConfig, stdout io.Writer) error {
			args := []string{global.Executable}
			args = append(args, global.Task)
			args = append(args, global.TaskArgs...)
			os.Args = args

			cmd.RunE = func(cmd *cobra.Command, args []string) error {
				projectDir, err := global.ProjectDir()
				if err != nil {
					return err
				}
				cfgDirPath, err := godellauncher.ConfigDirPath(projectDir)
				if err != nil {
					return err
				}

				// record all of the original YML files in the directory
				originalYMLFiles, err := dirYMLFiles(cfgDirPath)
				if err != nil {
					return err
				}
				// track all of the upgraded YML files
				knownConfigFiles := make(map[string]struct{})

				upgradeTasksMap := make(map[string]godellauncher.UpgradeConfigTask)
				for _, upgradeTask := range upgradeTasks {
					upgradeTasksMap[upgradeTask.ID] = upgradeTask
				}

				var failedUpgrades []string
				// perform hard-coded one-time upgrades
				for _, currUpgrader := range hardCodedLegacyUpgraders {
					if err := currUpgrader.upgradeConfig(cfgDirPath, dryRunFlagVal, printContentFlagVal, stdout); err != nil {
						failedUpgrades = append(failedUpgrades, upgradeError(projectDir, path.Join(cfgDirPath, currUpgrader.configFileName()), err))
					}
					knownConfigFiles[currUpgrader.configFileName()] = struct{}{}
				}

				var legacyConfigUpgraderIDs []string
				for _, upgradeTask := range upgradeTasks {
					// consider current configuration file for the plugin as known (don't warn if these files already
					// existed in config directory but were not processed by a legacy config upgrader).
					knownConfigFiles[upgradeTask.ConfigFile] = struct{}{}
					if upgradeTask.LegacyConfigFile == "" {
						continue
					}
					legacyConfigUpgraderIDs = append(legacyConfigUpgraderIDs, upgradeTask.ID)
				}
				sort.Strings(legacyConfigUpgraderIDs)
				for _, k := range legacyConfigUpgraderIDs {
					upgradeTask, ok := upgradeTasksMap[k]
					if !ok {
						// legacy task does not have an upgrader: continue
						continue
					}
					knownConfigFiles[upgradeTask.LegacyConfigFile] = struct{}{}
					if err := upgradeLegacyConfig(upgradeTask, cfgDirPath, global, dryRunFlagVal, printContentFlagVal, stdout); err != nil {
						failedUpgrades = append(failedUpgrades, upgradeError(projectDir, path.Join(cfgDirPath, upgradeTask.ConfigFile), err))
						continue
					}
				}

				var unhandledYMLFiles []string
				for _, k := range originalYMLFiles {
					if _, ok := knownConfigFiles[k]; ok {
						continue
					}
					unhandledYMLFiles = append(unhandledYMLFiles, k)
				}
				if err := processUnhandledYMLFiles(cfgDirPath, unhandledYMLFiles, dryRunFlagVal, stdout); err != nil {
					return err
				}

				if len(failedUpgrades) == 0 {
					return nil
				}
				dryRunPrintln(stdout, dryRunFlagVal, "Failed to upgrade configuration:")
				for _, upgrade := range failedUpgrades {
					dryRunPrintln(stdout, dryRunFlagVal, "\t"+upgrade)
				}
				return fmt.Errorf("")
			}

			rootCmd := godellauncher.CobraCmdToRootCmd(cmd)
			rootCmd.SetOutput(stdout)
			return rootCmd.Execute()
		},
	}
}

var hardCodedLegacyUpgraders = []hardCodedLegacyUpgrader{
	&hardCodedLegacyUpgraderImpl{
		fileName: "exclude.yml",
		upgradeConfigFn: func(configDirPath string, dryRun, printContent bool, stdout io.Writer) error {
			// godel.yml itself is compatible. Only work to be performed is if "exclude.yml" exists and contains entries that
			// differ from godel.yml.
			legacyExcludeFilePath := path.Join(configDirPath, "exclude.yml")
			if _, err := os.Stat(legacyExcludeFilePath); os.IsNotExist(err) {
				// if legacy file does not exist, there is no upgrade to be performed
				return nil
			}
			legacyConfigBytes, err := ioutil.ReadFile(legacyExcludeFilePath)
			if err != nil {
				return errors.Wrapf(err, "failed to read legacy configuration file")
			}
			var excludeCfg matcher.NamesPathsCfg
			if err := yaml.UnmarshalStrict(legacyConfigBytes, &excludeCfg); err != nil {
				return errors.Wrapf(err, "failed to unmarshal legacy exclude configuration")
			}

			currentGodelConfig, err := config.ReadGodelConfigFromFile(path.Join(configDirPath, "godel.yml"))
			if err != nil {
				return errors.Wrapf(err, "failed to read godel configuration")
			}

			existingNames := make(map[string]struct{})
			for _, name := range currentGodelConfig.Exclude.Names {
				existingNames[name] = struct{}{}
			}
			existingPaths := make(map[string]struct{})
			for _, path := range currentGodelConfig.Exclude.Paths {
				existingPaths[path] = struct{}{}
			}

			modified := false
			for _, legacyName := range excludeCfg.Names {
				if _, ok := existingNames[legacyName]; ok {
					continue
				}
				currentGodelConfig.Exclude.Names = append(currentGodelConfig.Exclude.Names, legacyName)
				modified = true
			}
			for _, legacyPath := range excludeCfg.Paths {
				if _, ok := existingPaths[legacyPath]; ok {
					continue
				}
				currentGodelConfig.Exclude.Names = append(currentGodelConfig.Exclude.Paths, legacyPath)
				modified = true
			}

			// back up old configuration by moving it
			if err := backupConfigFile(legacyExcludeFilePath, dryRun, stdout); err != nil {
				return errors.Wrapf(err, "failed to back up legacy configuration file")
			}

			if !modified {
				// exclude.yml did not provide any new excludes: no need to write
				return nil
			}

			upgradedCfgBytes, err := yaml.Marshal(currentGodelConfig)
			if err != nil {
				return errors.Wrapf(err, "failed to marshal upgraded godel configuration")
			}

			if !dryRun {
				// write migrated configuration
				if err := ioutil.WriteFile(path.Join(configDirPath, "godel.yml"), upgradedCfgBytes, 0644); err != nil {
					return errors.Wrapf(err, "failed to write upgraded configuration")
				}
			}
			printUpgradedConfig("godel.yml", upgradedCfgBytes, dryRun, printContent, stdout)
			return nil
		},
	},
}

func dirYMLFiles(inputDir string) ([]string, error) {
	fis, err := ioutil.ReadDir(inputDir)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to read input directory")
	}
	var ymlFiles []string
	for _, fi := range fis {
		if fi.IsDir() {
			continue
		}
		if strings.HasSuffix(fi.Name(), ".yml") {
			ymlFiles = append(ymlFiles, fi.Name())
		}
	}
	return ymlFiles, nil
}

type hardCodedLegacyUpgrader interface {
	configFileName() string
	upgradeConfig(configDirPath string, dryRun, printContent bool, stdout io.Writer) error
}

type hardCodedLegacyUpgraderImpl struct {
	fileName        string
	upgradeConfigFn func(configDirPath string, dryRun, printContent bool, stdout io.Writer) error
}

func (u *hardCodedLegacyUpgraderImpl) configFileName() string {
	return u.fileName
}

func (u *hardCodedLegacyUpgraderImpl) upgradeConfig(configDirPath string, dryRun, printContent bool, stdout io.Writer) error {
	return u.upgradeConfigFn(configDirPath, dryRun, printContent, stdout)
}

func upgradeLegacyConfig(upgradeTask godellauncher.UpgradeConfigTask, configDirPath string, global godellauncher.GlobalConfig, dryRun, printContent bool, stdout io.Writer) error {
	legacyConfigFilePath := path.Join(configDirPath, upgradeTask.LegacyConfigFile)
	if _, err := os.Stat(legacyConfigFilePath); os.IsNotExist(err) {
		// if legacy file does not exist, there is no upgrade to be performed
		return nil
	}
	legacyConfigBytes, err := ioutil.ReadFile(legacyConfigFilePath)
	if err != nil {
		return errors.Wrapf(err, "failed to read legacy configuration file")
	}

	var ymlConfig yaml.MapSlice
	if err := yaml.Unmarshal(legacyConfigBytes, &ymlConfig); err != nil {
		return errors.Wrapf(err, "failed to unmarshal YAML configuration")
	}
	// add "legacy-config: true" as a key to indicate that this is a legacy configuration
	ymlConfig = append([]yaml.MapItem{{Key: "legacy-config", Value: true}}, ymlConfig...)

	ymlCfgBytes, err := yaml.Marshal(ymlConfig)
	if err != nil {
		return errors.Wrapf(err, "failed to marshal YAML")
	}
	upgradedCfgBytes, err := upgradeTask.Run(ymlCfgBytes, global, stdout)
	if err != nil {
		return errors.Wrapf(err, "failed to upgrade configuration")
	}

	// back up old configuration
	if err := backupConfigFile(legacyConfigFilePath, dryRun, stdout); err != nil {
		return errors.Wrapf(err, "failed to back up legacy configuration file")
	}

	// back up destination file if it already exists
	dstFilePath := path.Join(configDirPath, upgradeTask.ConfigFile)
	if err := backupConfigFile(dstFilePath, dryRun, stdout); err != nil {
		return errors.Wrapf(err, "failed to back up existing configuration file")
	}

	// upgraded configuration is empty: no need to write
	if string(upgradedCfgBytes) == "" {
		return nil
	}

	if !dryRun {
		// write migrated configuration
		if err := ioutil.WriteFile(dstFilePath, upgradedCfgBytes, 0644); err != nil {
			return errors.Wrapf(err, "failed to write upgraded configuration")
		}
	}
	printUpgradedConfig(upgradeTask.ConfigFile, upgradedCfgBytes, dryRun, printContent, stdout)
	return nil
}

func processUnhandledYMLFiles(configDir string, unknownYMLFiles []string, dryRun bool, stdout io.Writer) error {
	if len(unknownYMLFiles) == 0 {
		return nil
	}

	var unknownNonEmptyFiles []string
	for _, currUnknownFile := range unknownYMLFiles {
		currPath := path.Join(configDir, currUnknownFile)
		bytes, err := ioutil.ReadFile(currPath)
		if err != nil {
			return errors.Wrapf(err, "failed to read configuration file")
		}
		// if unknown file is empty, just back it up
		if string(bytes) == "" {
			if err := backupConfigFile(currPath, dryRun, stdout); err != nil {
				return err
			}
			continue
		}
		unknownNonEmptyFiles = append(unknownNonEmptyFiles, currUnknownFile)
	}

	if len(unknownNonEmptyFiles) == 0 {
		return nil
	}

	// if non-empty unknown files were present, print warning
	dryRunPrintln(stdout, dryRun, fmt.Sprintf(`WARNING: The following configuration file(s) were non-empty and had no known upgraders for legacy configuration: %v`, unknownNonEmptyFiles))
	dryRunPrintln(stdout, dryRun, fmt.Sprintf(`         If these configuration file(s) are for plugins, add the plugins to the configuration and re-run the upgrade-legacy-config task.`))
	return nil
}
<commit_msg>Touch up output message (#326)<commit_after>// Copyright 2016 Palantir Technologies, Inc.
//
// 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 builtintasks

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"sort"
	"strings"

	"github.com/palantir/pkg/matcher"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"gopkg.in/yaml.v2"

	"github.com/palantir/godel/framework/godel/config"
	"github.com/palantir/godel/framework/godellauncher"
)

func UpgradeLegacyConfigTask(upgradeTasks []godellauncher.UpgradeConfigTask) godellauncher.Task {
	const (
		upgradeLegacyConfigCmdName = "upgrade-legacy-config"
		dryRunFlagName             = "dry-run"
		printContentFlagName       = "print-content"
	)

	var (
		dryRunFlagVal       bool
		printContentFlagVal bool
	)

	cmd := &cobra.Command{
		Use:   upgradeLegacyConfigCmdName,
		Short: "Upgrade the legacy configuration",
	}

	cmd.Flags().BoolVar(&dryRunFlagVal, dryRunFlagName, false, "print what the upgrade operation would do without writing changes")
	cmd.Flags().BoolVar(&printContentFlagVal, printContentFlagName, false, "print the content of the changes to stdout in addition to writing them")

	cmd.SilenceErrors = true
	cmd.SilenceUsage = true
	return godellauncher.Task{
		Name:        cmd.Use,
		Description: cmd.Short,
		RunImpl: func(t *godellauncher.Task, global godellauncher.GlobalConfig, stdout io.Writer) error {
			args := []string{global.Executable}
			args = append(args, global.Task)
			args = append(args, global.TaskArgs...)
			os.Args = args

			cmd.RunE = func(cmd *cobra.Command, args []string) error {
				projectDir, err := global.ProjectDir()
				if err != nil {
					return err
				}
				cfgDirPath, err := godellauncher.ConfigDirPath(projectDir)
				if err != nil {
					return err
				}

				// record all of the original YML files in the directory
				originalYMLFiles, err := dirYMLFiles(cfgDirPath)
				if err != nil {
					return err
				}
				// track all of the upgraded YML files
				knownConfigFiles := make(map[string]struct{})

				upgradeTasksMap := make(map[string]godellauncher.UpgradeConfigTask)
				for _, upgradeTask := range upgradeTasks {
					upgradeTasksMap[upgradeTask.ID] = upgradeTask
				}

				var failedUpgrades []string
				// perform hard-coded one-time upgrades
				for _, currUpgrader := range hardCodedLegacyUpgraders {
					if err := currUpgrader.upgradeConfig(cfgDirPath, dryRunFlagVal, printContentFlagVal, stdout); err != nil {
						failedUpgrades = append(failedUpgrades, upgradeError(projectDir, path.Join(cfgDirPath, currUpgrader.configFileName()), err))
					}
					knownConfigFiles[currUpgrader.configFileName()] = struct{}{}
				}

				var legacyConfigUpgraderIDs []string
				for _, upgradeTask := range upgradeTasks {
					// consider current configuration file for the plugin as known (don't warn if these files already
					// existed in config directory but were not processed by a legacy config upgrader).
					knownConfigFiles[upgradeTask.ConfigFile] = struct{}{}
					if upgradeTask.LegacyConfigFile == "" {
						continue
					}
					legacyConfigUpgraderIDs = append(legacyConfigUpgraderIDs, upgradeTask.ID)
				}
				sort.Strings(legacyConfigUpgraderIDs)
				for _, k := range legacyConfigUpgraderIDs {
					upgradeTask, ok := upgradeTasksMap[k]
					if !ok {
						// legacy task does not have an upgrader: continue
						continue
					}
					knownConfigFiles[upgradeTask.LegacyConfigFile] = struct{}{}
					if err := upgradeLegacyConfig(upgradeTask, cfgDirPath, global, dryRunFlagVal, printContentFlagVal, stdout); err != nil {
						failedUpgrades = append(failedUpgrades, upgradeError(projectDir, path.Join(cfgDirPath, upgradeTask.ConfigFile), err))
						continue
					}
				}

				var unhandledYMLFiles []string
				for _, k := range originalYMLFiles {
					if _, ok := knownConfigFiles[k]; ok {
						continue
					}
					unhandledYMLFiles = append(unhandledYMLFiles, k)
				}
				if err := processUnhandledYMLFiles(cfgDirPath, unhandledYMLFiles, dryRunFlagVal, stdout); err != nil {
					return err
				}

				if len(failedUpgrades) == 0 {
					return nil
				}
				dryRunPrintln(stdout, dryRunFlagVal, "Failed to upgrade configuration:")
				for _, upgrade := range failedUpgrades {
					dryRunPrintln(stdout, dryRunFlagVal, "\t"+upgrade)
				}
				return fmt.Errorf("")
			}

			rootCmd := godellauncher.CobraCmdToRootCmd(cmd)
			rootCmd.SetOutput(stdout)
			return rootCmd.Execute()
		},
	}
}

var hardCodedLegacyUpgraders = []hardCodedLegacyUpgrader{
	&hardCodedLegacyUpgraderImpl{
		fileName: "exclude.yml",
		upgradeConfigFn: func(configDirPath string, dryRun, printContent bool, stdout io.Writer) error {
			// godel.yml itself is compatible. Only work to be performed is if "exclude.yml" exists and contains entries that
			// differ from godel.yml.
			legacyExcludeFilePath := path.Join(configDirPath, "exclude.yml")
			if _, err := os.Stat(legacyExcludeFilePath); os.IsNotExist(err) {
				// if legacy file does not exist, there is no upgrade to be performed
				return nil
			}
			legacyConfigBytes, err := ioutil.ReadFile(legacyExcludeFilePath)
			if err != nil {
				return errors.Wrapf(err, "failed to read legacy configuration file")
			}
			var excludeCfg matcher.NamesPathsCfg
			if err := yaml.UnmarshalStrict(legacyConfigBytes, &excludeCfg); err != nil {
				return errors.Wrapf(err, "failed to unmarshal legacy exclude configuration")
			}

			currentGodelConfig, err := config.ReadGodelConfigFromFile(path.Join(configDirPath, "godel.yml"))
			if err != nil {
				return errors.Wrapf(err, "failed to read godel configuration")
			}

			existingNames := make(map[string]struct{})
			for _, name := range currentGodelConfig.Exclude.Names {
				existingNames[name] = struct{}{}
			}
			existingPaths := make(map[string]struct{})
			for _, path := range currentGodelConfig.Exclude.Paths {
				existingPaths[path] = struct{}{}
			}

			modified := false
			for _, legacyName := range excludeCfg.Names {
				if _, ok := existingNames[legacyName]; ok {
					continue
				}
				currentGodelConfig.Exclude.Names = append(currentGodelConfig.Exclude.Names, legacyName)
				modified = true
			}
			for _, legacyPath := range excludeCfg.Paths {
				if _, ok := existingPaths[legacyPath]; ok {
					continue
				}
				currentGodelConfig.Exclude.Names = append(currentGodelConfig.Exclude.Paths, legacyPath)
				modified = true
			}

			// back up old configuration by moving it
			if err := backupConfigFile(legacyExcludeFilePath, dryRun, stdout); err != nil {
				return errors.Wrapf(err, "failed to back up legacy configuration file")
			}

			if !modified {
				// exclude.yml did not provide any new excludes: no need to write
				return nil
			}

			upgradedCfgBytes, err := yaml.Marshal(currentGodelConfig)
			if err != nil {
				return errors.Wrapf(err, "failed to marshal upgraded godel configuration")
			}

			if !dryRun {
				// write migrated configuration
				if err := ioutil.WriteFile(path.Join(configDirPath, "godel.yml"), upgradedCfgBytes, 0644); err != nil {
					return errors.Wrapf(err, "failed to write upgraded configuration")
				}
			}
			printUpgradedConfig("godel.yml", upgradedCfgBytes, dryRun, printContent, stdout)
			return nil
		},
	},
}

func dirYMLFiles(inputDir string) ([]string, error) {
	fis, err := ioutil.ReadDir(inputDir)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to read input directory")
	}
	var ymlFiles []string
	for _, fi := range fis {
		if fi.IsDir() {
			continue
		}
		if strings.HasSuffix(fi.Name(), ".yml") {
			ymlFiles = append(ymlFiles, fi.Name())
		}
	}
	return ymlFiles, nil
}

type hardCodedLegacyUpgrader interface {
	configFileName() string
	upgradeConfig(configDirPath string, dryRun, printContent bool, stdout io.Writer) error
}

type hardCodedLegacyUpgraderImpl struct {
	fileName        string
	upgradeConfigFn func(configDirPath string, dryRun, printContent bool, stdout io.Writer) error
}

func (u *hardCodedLegacyUpgraderImpl) configFileName() string {
	return u.fileName
}

func (u *hardCodedLegacyUpgraderImpl) upgradeConfig(configDirPath string, dryRun, printContent bool, stdout io.Writer) error {
	return u.upgradeConfigFn(configDirPath, dryRun, printContent, stdout)
}

func upgradeLegacyConfig(upgradeTask godellauncher.UpgradeConfigTask, configDirPath string, global godellauncher.GlobalConfig, dryRun, printContent bool, stdout io.Writer) error {
	legacyConfigFilePath := path.Join(configDirPath, upgradeTask.LegacyConfigFile)
	if _, err := os.Stat(legacyConfigFilePath); os.IsNotExist(err) {
		// if legacy file does not exist, there is no upgrade to be performed
		return nil
	}
	legacyConfigBytes, err := ioutil.ReadFile(legacyConfigFilePath)
	if err != nil {
		return errors.Wrapf(err, "failed to read legacy configuration file")
	}

	var ymlConfig yaml.MapSlice
	if err := yaml.Unmarshal(legacyConfigBytes, &ymlConfig); err != nil {
		return errors.Wrapf(err, "failed to unmarshal YAML configuration")
	}
	// add "legacy-config: true" as a key to indicate that this is a legacy configuration
	ymlConfig = append([]yaml.MapItem{{Key: "legacy-config", Value: true}}, ymlConfig...)

	ymlCfgBytes, err := yaml.Marshal(ymlConfig)
	if err != nil {
		return errors.Wrapf(err, "failed to marshal YAML")
	}
	upgradedCfgBytes, err := upgradeTask.Run(ymlCfgBytes, global, stdout)
	if err != nil {
		return errors.Wrapf(err, "failed to upgrade configuration")
	}

	// back up old configuration
	if err := backupConfigFile(legacyConfigFilePath, dryRun, stdout); err != nil {
		return errors.Wrapf(err, "failed to back up legacy configuration file")
	}

	// back up destination file if it already exists
	dstFilePath := path.Join(configDirPath, upgradeTask.ConfigFile)
	if err := backupConfigFile(dstFilePath, dryRun, stdout); err != nil {
		return errors.Wrapf(err, "failed to back up existing configuration file")
	}

	// upgraded configuration is empty: no need to write
	if string(upgradedCfgBytes) == "" {
		return nil
	}

	if !dryRun {
		// write migrated configuration
		if err := ioutil.WriteFile(dstFilePath, upgradedCfgBytes, 0644); err != nil {
			return errors.Wrapf(err, "failed to write upgraded configuration")
		}
	}
	printUpgradedConfig(upgradeTask.ConfigFile, upgradedCfgBytes, dryRun, printContent, stdout)
	return nil
}

func processUnhandledYMLFiles(configDir string, unknownYMLFiles []string, dryRun bool, stdout io.Writer) error {
	if len(unknownYMLFiles) == 0 {
		return nil
	}

	var unknownNonEmptyFiles []string
	for _, currUnknownFile := range unknownYMLFiles {
		currPath := path.Join(configDir, currUnknownFile)
		bytes, err := ioutil.ReadFile(currPath)
		if err != nil {
			return errors.Wrapf(err, "failed to read configuration file")
		}
		// if unknown file is empty, just back it up
		if string(bytes) == "" {
			if err := backupConfigFile(currPath, dryRun, stdout); err != nil {
				return err
			}
			continue
		}
		unknownNonEmptyFiles = append(unknownNonEmptyFiles, currUnknownFile)
	}

	if len(unknownNonEmptyFiles) == 0 {
		return nil
	}

	// if non-empty unknown files were present, print warning
	dryRunPrintln(stdout, dryRun, fmt.Sprintf(`WARNING: The following configuration file(s) were non-empty and had no known upgraders for legacy configuration: %v`, unknownNonEmptyFiles))
	dryRunPrintln(stdout, dryRun, fmt.Sprintf(`         If these configuration file(s) are for plugins, add the plugins to the configuration and rerun the upgrade-legacy-config task.`))
	return nil
}
<|endoftext|>


<commit_before>package render

import (
	"github.com/huangjunwen/JustSQL/context"
	"github.com/pingcap/tidb/mysql"
	"reflect"
	"strings"
	"text/template"
)

func notNil(v interface{}) (res bool) {
	defer func() {
		if r := recover(); r != nil {
			res = false
		}
		return
	}()
	res = !reflect.ValueOf(v).IsNil()
	return
}

// Return columns that need explicit values to insert.
func columnsForInsert(table *context.TableData) []*context.ColumnData {
	ret := make([]*context.ColumnData, 0)
	for _, col := range table.Columns {
		// Skip auto increment column
		if col.IsAutoIncrement {
			continue
		}
		// Skip time column with default now()
		switch tp := col.ColumnInfo.FieldType.Tp; tp {
		case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp:
			if col.DefaultValue == interface{}("CURRENT_TIMESTAMP") {
				continue
			}
		}
		ret = append(ret, col)
	}
	return ret
}

// Return columns that need explicit values to update.
func columnsForUpdate(table *context.TableData) []*context.ColumnData {
	ret := make([]*context.ColumnData, 0)
	for _, col := range table.Columns {
		// Skip ON UPDATE CURRENT_TIMESTAMP
		if col.IsOnUpdateNow {
			continue
		}
		ret = append(ret, col)
	}
	return ret
}

// Return 'col1, col2, col3, ...'
func columnNameList(cols []*context.ColumnData) string {
	parts := make([]string, 0, len(cols))
	for _, col := range cols {
		parts = append(parts, col.Name.O)
	}
	return strings.Join(parts, ", ")
}

func placeholder() string {
	return "?"
}

// Return '?, ?, ?, ...'
func placeholderList(n int) string {
	s := strings.Repeat(placeholder()+", ", n)
	return s[:len(s)-2]
}

func buildExtraFuncs(ctx *context.Context) template.FuncMap {

	tctx := ctx.TypeContext

	// Import pkg (its path) and return a unique name.
	imp := func(pkg_path string) (string, error) {
		return tctx.CurrScope().UsePkg(pkg_path), nil
	}

	return template.FuncMap{
		"imp":                imp,
		"not_nil":            notNil,
		"columns_for_insert": columnsForInsert,
		"columns_for_update": columnsForUpdate,
		"column_name_list":   columnNameList,
		"placeholder":        placeholder,
		"placeholder_list":   placeholderList,
	}

}
<commit_msg>Fix a bug: placeholder_list panic when n <= 1<commit_after>package render

import (
	"github.com/huangjunwen/JustSQL/context"
	"github.com/pingcap/tidb/mysql"
	"reflect"
	"strings"
	"text/template"
)

func notNil(v interface{}) (res bool) {
	defer func() {
		if r := recover(); r != nil {
			res = false
		}
		return
	}()
	res = !reflect.ValueOf(v).IsNil()
	return
}

// Return columns that need explicit values to insert.
func columnsForInsert(table *context.TableData) []*context.ColumnData {
	ret := make([]*context.ColumnData, 0)
	for _, col := range table.Columns {
		// Skip auto increment column
		if col.IsAutoIncrement {
			continue
		}
		// Skip time column with default now()
		switch tp := col.ColumnInfo.FieldType.Tp; tp {
		case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp:
			if col.DefaultValue == interface{}("CURRENT_TIMESTAMP") {
				continue
			}
		}
		ret = append(ret, col)
	}
	return ret
}

// Return columns that need explicit values to update.
func columnsForUpdate(table *context.TableData) []*context.ColumnData {
	ret := make([]*context.ColumnData, 0)
	for _, col := range table.Columns {
		// Skip ON UPDATE CURRENT_TIMESTAMP
		if col.IsOnUpdateNow {
			continue
		}
		ret = append(ret, col)
	}
	return ret
}

// Return 'col1, col2, col3, ...'
func columnNameList(cols []*context.ColumnData) string {
	parts := make([]string, 0, len(cols))
	for _, col := range cols {
		parts = append(parts, col.Name.O)
	}
	return strings.Join(parts, ", ")
}

func placeholder() string {
	return "?"
}

// Return '?, ?, ?, ...'
func placeholderList(n int) string {
	if n <= 0 {
		return ""
	} else if n == 1 {
		return placeholder()
	}
	s := strings.Repeat(placeholder()+", ", n)
	return s[:len(s)-2]
}

func buildExtraFuncs(ctx *context.Context) template.FuncMap {

	tctx := ctx.TypeContext

	// Import pkg (its path) and return a unique name.
	imp := func(pkg_path string) (string, error) {
		return tctx.CurrScope().UsePkg(pkg_path), nil
	}

	return template.FuncMap{
		"imp":                imp,
		"not_nil":            notNil,
		"columns_for_insert": columnsForInsert,
		"columns_for_update": columnsForUpdate,
		"column_name_list":   columnNameList,
		"placeholder":        placeholder,
		"placeholder_list":   placeholderList,
	}

}
<|endoftext|>


<commit_before>package translate_test

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"

	"github.com/jBugman/fun-lang/translate"
)

const fullSource = `
package main

import (
	"fmt"
	"io"
)

func inc(val int) int {
	return val + 1
}

func print42() {
	fmt.Println(42)
}

func main() {
	line := "Hello World!"
	fmt.Fprintln(io.Discard, line)
}
`

func ExampleFun_Module() {
	fset := token.NewFileSet()
	tree, _ := parser.ParseFile(fset, "source.go", fullSource, 0)
	result, err := translate.NewFun(fset).Module(tree)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Print(result)
	}
	// Output:
	// module Main where
	//
	// import "fmt"
	// import "io"
	//
	// inc :: int -> int
	// inc val = val + 1
	//
	// print42 :: ()
	// print42 = fmt.Println 42
	//
	// main :: ()
	// main = do
	//     line := "Hello World!"
	//     fmt.Fprintln(io.Discard, line)
}

func ExampleFun_Import() {
	fset := token.NewFileSet()
	tree := &ast.ImportSpec{
		Path: &ast.BasicLit{
			Kind:  token.STRING,
			Value: "fmt",
		},
	}
	result, err := translate.NewFun(fset).Import(tree)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Print(result)
	}
	// Output:
	// import "fmt"
}

func ExampleFun_Expression_selector() {
	fset := token.NewFileSet()
	tree := &ast.SelectorExpr{
		X:   &ast.Ident{Name: "fmt"},
		Sel: &ast.Ident{Name: "Println"},
	}
	result, err := translate.NewFun(fset).Expression(tree)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Print(result)
	}
	// Output:
	// fmt.Println
}

func ExampleFun_Expression_binary() {
	fset := token.NewFileSet()
	tree := &ast.BinaryExpr{
		X: &ast.Ident{
			Name: "val",
			Obj:  &ast.Object{Kind: ast.Var, Name: "val"},
		},
		Op: token.ADD,
		Y:  &ast.BasicLit{Kind: token.INT, Value: "1"},
	}
	result, err := translate.NewFun(fset).Expression(tree)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Print(result)
	}
	// Output:
	// val + 1
}

func ExampleFun_Statement_return() {
	fset := token.NewFileSet()
	tree := &ast.ReturnStmt{
		Results: []ast.Expr{
			&ast.BasicLit{Kind: token.INT, Value: "42"},
		},
	}
	result, err := translate.NewFun(fset).Statement(tree)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Print(result)
	}
	// Output:
	// 42
}

func ExampleFun_Statement_tuple() {
	fset := token.NewFileSet()
	expr, err := parser.ParseExpr("func() {return 'a', 9.99}")
	returnExpr := expr.(*ast.FuncLit).Body.List[0]
	result, err := translate.NewFun(fset).Statement(returnExpr)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Print(result)
	}
	// Output:
	// ('a', 9.99)
}

func ExampleFun_Expression_brokenLiteral_testError() {
	fset := token.NewFileSet()
	tree := &ast.BasicLit{
		Kind:  token.BREAK,
		Value: "SNAKE!",
	}
	_, err := translate.NewFun(fset).Expression(tree)
	fmt.Print(err)
	// Output:
	// unexpected literal type:
	//      0  *ast.BasicLit {
	//      1  .  ValuePos: -
	//      2  .  Kind: break
	//      3  .  Value: "snake!"
	//      4  }
}
<commit_msg>Fixed broken test<commit_after>package translate_test

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"

	"github.com/jBugman/fun-lang/translate"
)

const fullSource = `
package main

import (
	"fmt"
	"io"
)

func inc(val int) int {
	return val + 1
}

func print42() {
	fmt.Println(42)
}

func main() {
	line := "Hello World!"
	fmt.Fprintln(io.Discard, line)
}
`

func ExampleFun_Module() {
	fset := token.NewFileSet()
	tree, _ := parser.ParseFile(fset, "source.go", fullSource, 0)
	result, err := translate.NewFun(fset).Module(tree)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Print(result)
	}
	// Output:
	// module Main where
	//
	// import "fmt"
	// import "io"
	//
	// inc :: int -> int
	// inc val = val + 1
	//
	// print42 :: ()
	// print42 = fmt.Println 42
	//
	// main :: ()
	// main = do
	//     line := "Hello World!"
	//     fmt.Fprintln(io.Discard, line)
}

func ExampleFun_Import() {
	fset := token.NewFileSet()
	tree := &ast.ImportSpec{
		Path: &ast.BasicLit{
			Kind:  token.STRING,
			Value: "fmt",
		},
	}
	result, err := translate.NewFun(fset).Import(tree)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Print(result)
	}
	// Output:
	// import "fmt"
}

func ExampleFun_Expression_selector() {
	fset := token.NewFileSet()
	tree := &ast.SelectorExpr{
		X:   &ast.Ident{Name: "fmt"},
		Sel: &ast.Ident{Name: "Println"},
	}
	result, err := translate.NewFun(fset).Expression(tree)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Print(result)
	}
	// Output:
	// fmt.Println
}

func ExampleFun_Expression_binary() {
	fset := token.NewFileSet()
	tree := &ast.BinaryExpr{
		X: &ast.Ident{
			Name: "val",
			Obj:  &ast.Object{Kind: ast.Var, Name: "val"},
		},
		Op: token.ADD,
		Y:  &ast.BasicLit{Kind: token.INT, Value: "1"},
	}
	result, err := translate.NewFun(fset).Expression(tree)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Print(result)
	}
	// Output:
	// val + 1
}

func ExampleFun_Statement_return() {
	fset := token.NewFileSet()
	tree := &ast.ReturnStmt{
		Results: []ast.Expr{
			&ast.BasicLit{Kind: token.INT, Value: "42"},
		},
	}
	result, err := translate.NewFun(fset).Statement(tree)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Print(result)
	}
	// Output:
	// 42
}

func ExampleFun_Statement_tuple() {
	fset := token.NewFileSet()
	expr, err := parser.ParseExpr("func() {return 'a', 9.99}")
	returnExpr := expr.(*ast.FuncLit).Body.List[0]
	result, err := translate.NewFun(fset).Statement(returnExpr)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Print(result)
	}
	// Output:
	// ('a', 9.99)
}

func ExampleFun_Expression_brokenLiteral_testError() {
	fset := token.NewFileSet()
	tree := &ast.BasicLit{
		Kind:  token.BREAK,
		Value: "SNAKE!",
	}
	_, err := translate.NewFun(fset).Expression(tree)
	fmt.Print(err)
	// Output:
	// unexpected literal type:
	//      0  *ast.BasicLit {
	//      1  .  ValuePos: -
	//      2  .  Kind: break
	//      3  .  Value: "SNAKE!"
	//      4  }
}
<|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package common

import (
	"errors"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/webx-top/com"
	"github.com/webx-top/echo"
)

// Ok 操作成功
func Ok(v string) Successor {
	return NewOk(v)
}

// Err 获取错误信息
func Err(ctx echo.Context, err error) (ret interface{}) {
	if err == nil {
		flash := ctx.Flash()
		if flash != nil {
			if errMsg, ok := flash.(string); ok {
				ret = errors.New(errMsg)
			} else {
				ret = flash
			}
		}
	} else {
		ret = ProcessError(ctx, err)
	}
	return
}

// SendOk 记录成功信息
func SendOk(ctx echo.Context, msg string) {
	if ctx.IsAjax() || ctx.Format() != `html` {
		ctx.Data().SetInfo(msg, 1)
		return
	}
	ctx.Session().AddFlash(Ok(msg))
}

// SendFail 记录失败信息
func SendFail(ctx echo.Context, msg string) {
	if ctx.IsAjax() || ctx.Format() != `html` {
		ctx.Data().SetInfo(msg, 0)
		return
	}
	ctx.Session().AddFlash(msg)
}

// SendErr 记录错误信息 (SendFail的别名)
func SendErr(ctx echo.Context, err error) {
	err = ProcessError(ctx, err)
	SendFail(ctx, err.Error())
}

type ConfigFromDB interface {
	ConfigFromDB() echo.H
}

// GetLocalIP 获取本机网卡IP
func GetLocalIP() (ipv4 string, err error) {
	var (
		addrs   []net.Addr
		addr    net.Addr
		ipNet   *net.IPNet // IP地址
		isIPNet bool
	)
	ipv4 = `127.0.0.1`
	// 获取所有网卡
	if addrs, err = net.InterfaceAddrs(); err != nil {
		return
	}
	// 取第一个非lo的网卡IP
	for _, addr = range addrs {
		// 这个网络地址是IP地址: ipv4, ipv6
		if ipNet, isIPNet = addr.(*net.IPNet); isIPNet && !ipNet.IP.IsLoopback() {
			// 跳过IPV6
			if ipNet.IP.To4() != nil {
				ipv4 = ipNet.IP.String() // 192.168.1.1
				return
			}
		}
	}
	return
}

var notWordRegexp = regexp.MustCompile(`[^\w]+`)

// LookPath 获取二进制可执行文件路径
func LookPath(bin string, otherPaths ...string) (string, error) {
	envVarName := `NGING_` + notWordRegexp.ReplaceAllString(strings.ToUpper(bin), `_`) + `_PATH`
	envVarValue := os.Getenv(envVarName)
	if len(envVarValue) > 0 {
		if com.IsFile(envVarValue) {
			return envVarValue, nil
		}
		envVarValue = filepath.Join(envVarValue, bin)
		if com.IsFile(envVarValue) {
			return envVarValue, nil
		}
	}
	findPath, err := exec.LookPath(bin)
	if err == nil {
		return findPath, err
	}
	if !errors.Is(err, exec.ErrNotFound) {
		return findPath, err
	}
	for _, binPath := range otherPaths {
		binPath = filepath.Join(binPath, bin)
		if com.IsFile(binPath) {
			return binPath, nil
		}
	}
	return findPath, err
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package common

import (
	"errors"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/webx-top/com"
	"github.com/webx-top/echo"
)

// Ok 操作成功
func Ok(v string) Successor {
	return NewOk(v)
}

// Err 获取错误信息
func Err(ctx echo.Context, err error) (ret interface{}) {
	if err == nil {
		flash := ctx.Flash()
		if flash != nil {
			if errMsg, ok := flash.(string); ok {
				ret = errors.New(errMsg)
			} else {
				ret = flash
			}
		}
	} else {
		ret = ProcessError(ctx, err)
	}
	return
}

// SendOk 记录成功信息
func SendOk(ctx echo.Context, msg string) {
	if ctx.IsAjax() || ctx.Format() != echo.ContentTypeHTML {
		ctx.Data().SetInfo(msg, 1)
		return
	}
	ctx.Session().AddFlash(Ok(msg))
}

// SendFail 记录失败信息
func SendFail(ctx echo.Context, msg string) {
	if ctx.IsAjax() || ctx.Format() != echo.ContentTypeHTML {
		ctx.Data().SetInfo(msg, 0)
		return
	}
	ctx.Session().AddFlash(msg)
}

// SendErr 记录错误信息 (SendFail的别名)
func SendErr(ctx echo.Context, err error) {
	err = ProcessError(ctx, err)
	SendFail(ctx, err.Error())
}

type ConfigFromDB interface {
	ConfigFromDB() echo.H
}

// GetLocalIP 获取本机网卡IP
func GetLocalIP() (ipv4 string, err error) {
	var (
		addrs   []net.Addr
		addr    net.Addr
		ipNet   *net.IPNet // IP地址
		isIPNet bool
	)
	ipv4 = `127.0.0.1`
	// 获取所有网卡
	if addrs, err = net.InterfaceAddrs(); err != nil {
		return
	}
	// 取第一个非lo的网卡IP
	for _, addr = range addrs {
		// 这个网络地址是IP地址: ipv4, ipv6
		if ipNet, isIPNet = addr.(*net.IPNet); isIPNet && !ipNet.IP.IsLoopback() {
			// 跳过IPV6
			if ipNet.IP.To4() != nil {
				ipv4 = ipNet.IP.String() // 192.168.1.1
				return
			}
		}
	}
	return
}

var notWordRegexp = regexp.MustCompile(`[^\w]+`)

// LookPath 获取二进制可执行文件路径
func LookPath(bin string, otherPaths ...string) (string, error) {
	envVarName := `NGING_` + notWordRegexp.ReplaceAllString(strings.ToUpper(bin), `_`) + `_PATH`
	envVarValue := os.Getenv(envVarName)
	if len(envVarValue) > 0 {
		if com.IsFile(envVarValue) {
			return envVarValue, nil
		}
		envVarValue = filepath.Join(envVarValue, bin)
		if com.IsFile(envVarValue) {
			return envVarValue, nil
		}
	}
	findPath, err := exec.LookPath(bin)
	if err == nil {
		return findPath, err
	}
	if !errors.Is(err, exec.ErrNotFound) {
		return findPath, err
	}
	for _, binPath := range otherPaths {
		binPath = filepath.Join(binPath, bin)
		if com.IsFile(binPath) {
			return binPath, nil
		}
	}
	return findPath, err
}
<|endoftext|>