<commit_before>package install

import (
	"errors"
	"fmt"
	"net"
	"os"
	"time"
)

// ValidatePlan runs validation against the installation plan to ensure
// that the plan contains valid user input. Returns true, nil if the validation
// is successful. Otherwise, returns false and a collection of validation errors.
func ValidatePlan(p *Plan) (bool, []error) {
	v := newValidator()
	v.validate(p)
	return v.valid()
}

type validatable interface {
	validate() (bool, []error)
}

type validator struct {
	errs []error
}

func newValidator() *validator {
	return &validator{
		errs: []error{},
	}
}

func (v *validator) addError(err ...error) {
	v.errs = append(v.errs, err...)
}

func (v *validator) validate(obj validatable) {
	if ok, err := obj.validate(); !ok {
		v.addError(err...)
	}
}

func (v *validator) validateWithErrPrefix(prefix string, obj validatable) {
	if ok, err := obj.validate(); !ok {
		newErrs := make([]error, len(err), len(err))
		for i, err := range err {
			newErrs[i] = fmt.Errorf("%s: %v", prefix, err)
		}
		v.addError(newErrs...)
	}
}

func (v *validator) valid() (bool, []error) {
	if len(v.errs) > 0 {
		return false, v.errs
	}
	return true, nil
}

func (p *Plan) validate() (bool, []error) {
	v := newValidator()

	v.validate(&p.Cluster)
	v.validateWithErrPrefix("Etcd nodes", &p.Etcd)
	v.validateWithErrPrefix("Master nodes", &p.Master)
	v.validateWithErrPrefix("Worker nodes", &p.Worker)

	return v.valid()
}

func (c *Cluster) validate() (bool, []error) {
	v := newValidator()
	if c.Name == "" {
		v.addError(errors.New("Cluster name cannot be empty"))
	}
	if c.AdminPassword == "" {
		v.addError(errors.New("Admin password cannot be empty"))
	}
	v.validate(&c.Networking)
	v.validate(&c.Certificates)
	v.validate(&c.SSH)

	return v.valid()
}

func (n *NetworkConfig) validate() (bool, []error) {
	v := newValidator()
	if n.Type == "" {
		v.addError(errors.New("Networking type cannot be empty"))
	}
	if n.Type != "bridged" && n.Type != "overlay" {
		v.addError(fmt.Errorf("Invalid networking type %q was provided", n.Type))
	}
	if n.PodCIDRBlock == "" {
		v.addError(errors.New("Pod CIDR block cannot be empty"))
	}
	if _, _, err := net.ParseCIDR(n.PodCIDRBlock); n.PodCIDRBlock != "" && err != nil {
		v.addError(fmt.Errorf("Invalid Pod CIDR block provided: %v", err))
	}

	if n.ServiceCIDRBlock == "" {
		v.addError(errors.New("Service CIDR block cannot be empty"))
	}
	if _, _, err := net.ParseCIDR(n.ServiceCIDRBlock); n.ServiceCIDRBlock != "" && err != nil {
		v.addError(fmt.Errorf("Invalid Service CIDR block provided: %v", err))
	}
	return v.valid()
}

func (c *CertsConfig) validate() (bool, []error) {
	v := newValidator()
	if _, err := time.ParseDuration(c.Expiry); err != nil {
		v.addError(fmt.Errorf("Invalid certificate expiry %q provided: %v", c.Expiry, err))
	}

	if c.LocationCity == "" {
		v.addError(errors.New("Certificate location_city field is required"))
	}
	if c.LocationState == "" {
		v.addError(errors.New("Certificate location_state field is required"))
	}
	if c.LocationCountry == "" {
		v.addError(errors.New("Certificate location_country field is required"))
	}
	return v.valid()
}

func (s *SSHConfig) validate() (bool, []error) {
	v := newValidator()
	if s.User == "" {
		v.addError(errors.New("SSH user field is required"))
	}
	if s.Key == "" {
		v.addError(errors.New("SSH key field is required"))
	}
	if _, err := os.Stat(s.Key); os.IsNotExist(err) {
		v.addError(fmt.Errorf("SSH Key file was not found at %q", s.Key))
	}
	if s.Port < 1 || s.Port > 65535 {
		v.addError(fmt.Errorf("SSH port %d is invalid. Port must be in the range 1-65535", s.Port))
	}
	return v.valid()
}

func (ng *NodeGroup) validate() (bool, []error) {
	v := newValidator()
	if ng == nil || len(ng.Nodes) <= 0 {
		v.addError(fmt.Errorf("Atleast one node is required"))
	}
	if ng.ExpectedCount <= 0 {
		v.addError(fmt.Errorf("Node count must be greater than 0"))
	}
	if len(ng.Nodes) != ng.ExpectedCount && (len(ng.Nodes) > 0 && ng.ExpectedCount > 0) {
		v.addError(fmt.Errorf("Expected node count (%d) does not match the number of nodes provided (%d)", ng.ExpectedCount, len(ng.Nodes)))
	}
	for i, n := range ng.Nodes {
		v.validateWithErrPrefix(fmt.Sprintf("Node #%d", i+1), &n)
	}
	return v.valid()
}

func (mng *MasterNodeGroup) validate() (bool, []error) {
	v := newValidator()
	v.validate(&mng.NodeGroup)

	if mng.LoadBalancedFQDN == "" {
		v.addError(fmt.Errorf("Load balanced FQDN is required"))
	}

	if mng.LoadBalancedShortName == "" {
		v.addError(fmt.Errorf("Load balanced shortname is required"))
	}

	return v.valid()
}

func (n *Node) validate() (bool, []error) {
	v := newValidator()
	if n.Host == "" {
		v.addError(fmt.Errorf("Node host field is required"))
	}
	if n.IP == "" {
		v.addError(fmt.Errorf("Node IP field is required"))
	}
	if ip := net.ParseIP(n.IP); ip == nil {
		v.addError(fmt.Errorf("Invalid IP provided"))
	}
	return v.valid()
}
<commit_msg>Fixed typo<commit_after>package install

import (
	"errors"
	"fmt"
	"net"
	"os"
	"time"
)

// ValidatePlan runs validation against the installation plan to ensure
// that the plan contains valid user input. Returns true, nil if the validation
// is successful. Otherwise, returns false and a collection of validation errors.
func ValidatePlan(p *Plan) (bool, []error) {
	v := newValidator()
	v.validate(p)
	return v.valid()
}

type validatable interface {
	validate() (bool, []error)
}

type validator struct {
	errs []error
}

func newValidator() *validator {
	return &validator{
		errs: []error{},
	}
}

func (v *validator) addError(err ...error) {
	v.errs = append(v.errs, err...)
}

func (v *validator) validate(obj validatable) {
	if ok, err := obj.validate(); !ok {
		v.addError(err...)
	}
}

func (v *validator) validateWithErrPrefix(prefix string, obj validatable) {
	if ok, err := obj.validate(); !ok {
		newErrs := make([]error, len(err), len(err))
		for i, err := range err {
			newErrs[i] = fmt.Errorf("%s: %v", prefix, err)
		}
		v.addError(newErrs...)
	}
}

func (v *validator) valid() (bool, []error) {
	if len(v.errs) > 0 {
		return false, v.errs
	}
	return true, nil
}

func (p *Plan) validate() (bool, []error) {
	v := newValidator()

	v.validate(&p.Cluster)
	v.validateWithErrPrefix("Etcd nodes", &p.Etcd)
	v.validateWithErrPrefix("Master nodes", &p.Master)
	v.validateWithErrPrefix("Worker nodes", &p.Worker)

	return v.valid()
}

func (c *Cluster) validate() (bool, []error) {
	v := newValidator()
	if c.Name == "" {
		v.addError(errors.New("Cluster name cannot be empty"))
	}
	if c.AdminPassword == "" {
		v.addError(errors.New("Admin password cannot be empty"))
	}
	v.validate(&c.Networking)
	v.validate(&c.Certificates)
	v.validate(&c.SSH)

	return v.valid()
}

func (n *NetworkConfig) validate() (bool, []error) {
	v := newValidator()
	if n.Type == "" {
		v.addError(errors.New("Networking type cannot be empty"))
	}
	if n.Type != "bridged" && n.Type != "overlay" {
		v.addError(fmt.Errorf("Invalid networking type %q was provided", n.Type))
	}
	if n.PodCIDRBlock == "" {
		v.addError(errors.New("Pod CIDR block cannot be empty"))
	}
	if _, _, err := net.ParseCIDR(n.PodCIDRBlock); n.PodCIDRBlock != "" && err != nil {
		v.addError(fmt.Errorf("Invalid Pod CIDR block provided: %v", err))
	}

	if n.ServiceCIDRBlock == "" {
		v.addError(errors.New("Service CIDR block cannot be empty"))
	}
	if _, _, err := net.ParseCIDR(n.ServiceCIDRBlock); n.ServiceCIDRBlock != "" && err != nil {
		v.addError(fmt.Errorf("Invalid Service CIDR block provided: %v", err))
	}
	return v.valid()
}

func (c *CertsConfig) validate() (bool, []error) {
	v := newValidator()
	if _, err := time.ParseDuration(c.Expiry); err != nil {
		v.addError(fmt.Errorf("Invalid certificate expiry %q provided: %v", c.Expiry, err))
	}

	if c.LocationCity == "" {
		v.addError(errors.New("Certificate location_city field is required"))
	}
	if c.LocationState == "" {
		v.addError(errors.New("Certificate location_state field is required"))
	}
	if c.LocationCountry == "" {
		v.addError(errors.New("Certificate location_country field is required"))
	}
	return v.valid()
}

func (s *SSHConfig) validate() (bool, []error) {
	v := newValidator()
	if s.User == "" {
		v.addError(errors.New("SSH user field is required"))
	}
	if s.Key == "" {
		v.addError(errors.New("SSH key field is required"))
	}
	if _, err := os.Stat(s.Key); os.IsNotExist(err) {
		v.addError(fmt.Errorf("SSH Key file was not found at %q", s.Key))
	}
	if s.Port < 1 || s.Port > 65535 {
		v.addError(fmt.Errorf("SSH port %d is invalid. Port must be in the range 1-65535", s.Port))
	}
	return v.valid()
}

func (ng *NodeGroup) validate() (bool, []error) {
	v := newValidator()
	if ng == nil || len(ng.Nodes) <= 0 {
		v.addError(fmt.Errorf("At least one node is required"))
	}
	if ng.ExpectedCount <= 0 {
		v.addError(fmt.Errorf("Node count must be greater than 0"))
	}
	if len(ng.Nodes) != ng.ExpectedCount && (len(ng.Nodes) > 0 && ng.ExpectedCount > 0) {
		v.addError(fmt.Errorf("Expected node count (%d) does not match the number of nodes provided (%d)", ng.ExpectedCount, len(ng.Nodes)))
	}
	for i, n := range ng.Nodes {
		v.validateWithErrPrefix(fmt.Sprintf("Node #%d", i+1), &n)
	}
	return v.valid()
}

func (mng *MasterNodeGroup) validate() (bool, []error) {
	v := newValidator()
	v.validate(&mng.NodeGroup)

	if mng.LoadBalancedFQDN == "" {
		v.addError(fmt.Errorf("Load balanced FQDN is required"))
	}

	if mng.LoadBalancedShortName == "" {
		v.addError(fmt.Errorf("Load balanced shortname is required"))
	}

	return v.valid()
}

func (n *Node) validate() (bool, []error) {
	v := newValidator()
	if n.Host == "" {
		v.addError(fmt.Errorf("Node host field is required"))
	}
	if n.IP == "" {
		v.addError(fmt.Errorf("Node IP field is required"))
	}
	if ip := net.ParseIP(n.IP); ip == nil {
		v.addError(fmt.Errorf("Invalid IP provided"))
	}
	return v.valid()
}
<|endoftext|>


<commit_before>package command

import (
	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/metrics"
	metricsLinux "github.com/mackerelio/mackerel-agent/metrics/linux"
	"github.com/mackerelio/mackerel-agent/spec"
	specLinux "github.com/mackerelio/mackerel-agent/spec/linux"
)

func specGenerators() []spec.Generator {
	instanceGenerator, _ := specLinux.NewInstanceGenerator("")
	return []spec.Generator{
		&specLinux.KernelGenerator{},
		&specLinux.CPUGenerator{},
		&specLinux.MemoryGenerator{},
		&specLinux.BlockDeviceGenerator{},
		&specLinux.FilesystemGenerator{},
		instanceGenerator,
	}
}

func interfaceGenerator() spec.Generator {
	return &specLinux.InterfaceGenerator{}
}

func metricsGenerators(conf *config.Config) []metrics.Generator {
	generators := []metrics.Generator{
		&metricsLinux.Loadavg5Generator{},
		&metricsLinux.CPUUsageGenerator{Interval: 60},
		&metricsLinux.MemoryGenerator{},
		&metricsLinux.UptimeGenerator{},
		&metricsLinux.InterfaceGenerator{Interval: 60},
		&metricsLinux.DiskGenerator{Interval: 60},
		&metricsLinux.FilesystemGenerator{},
	}

	return generators
}

func pluginGenerators(conf *config.Config) []metrics.PluginGenerator {
	generators := []metrics.PluginGenerator{}

	for _, pluginConfig := range conf.Plugin["metrics"] {
		generators = append(generators, metrics.NewPluginGenerator(pluginConfig))
	}

	return generators
}
<commit_msg>deal with an error of NewInstanceGenerator<commit_after>package command

import (
	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/metrics"
	metricsLinux "github.com/mackerelio/mackerel-agent/metrics/linux"
	"github.com/mackerelio/mackerel-agent/spec"
	specLinux "github.com/mackerelio/mackerel-agent/spec/linux"
)

func specGenerators() []spec.Generator {
	specs := []spec.Generator{
		&specLinux.KernelGenerator{},
		&specLinux.CPUGenerator{},
		&specLinux.MemoryGenerator{},
		&specLinux.BlockDeviceGenerator{},
		&specLinux.FilesystemGenerator{},
	}
	instanceGenerator, err := specLinux.NewInstanceGenerator("")
	if err != nil {
		logger.Errorf("Failed to create instanceGenerator: %s", err.Error())
	} else {
		append(specs, instanceGenerator)
	}
	return specs
}

func interfaceGenerator() spec.Generator {
	return &specLinux.InterfaceGenerator{}
}

func metricsGenerators(conf *config.Config) []metrics.Generator {
	generators := []metrics.Generator{
		&metricsLinux.Loadavg5Generator{},
		&metricsLinux.CPUUsageGenerator{Interval: 60},
		&metricsLinux.MemoryGenerator{},
		&metricsLinux.UptimeGenerator{},
		&metricsLinux.InterfaceGenerator{Interval: 60},
		&metricsLinux.DiskGenerator{Interval: 60},
		&metricsLinux.FilesystemGenerator{},
	}

	return generators
}

func pluginGenerators(conf *config.Config) []metrics.PluginGenerator {
	generators := []metrics.PluginGenerator{}

	for _, pluginConfig := range conf.Plugin["metrics"] {
		generators = append(generators, metrics.NewPluginGenerator(pluginConfig))
	}

	return generators
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://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 commands

import (
	"os"
	"strconv"

	"github.com/GoogleCloudPlatform/kf/pkg/kf/commands/config"
	"github.com/GoogleCloudPlatform/kf/pkg/kf/commands/doctor"
	pkgdoctor "github.com/GoogleCloudPlatform/kf/pkg/kf/doctor"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
)

// NewKfCommand creates the root kf command.
func NewKfCommand() *cobra.Command {
	p := &config.KfParams{
		Output: os.Stdout,
	}

	var rootCmd = &cobra.Command{
		Use:   "kf",
		Short: "kf is like cf for Knative",
		Long: `kf is like cf for Knative

kf supports the following sub-commands:

Apps:
  kf push
  kf delete <app>
  kf apps

Services:
  kf marketplace
  kf create-service
  kf delete-service
  kf service <instance-name>
  kf services
	kf bindings
	kf bind-service <app> <instance-name>
	kf unbind-service <app> <instance-name>

You can get more info by adding the --help flag to any sub-command.
`,
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Usage()
		},
	}

	rootCmd.PersistentFlags().StringVar(&p.KubeCfgFile, "kubeconfig", "", "kubectl config file (default is $HOME/.kube/config)")
	rootCmd.PersistentFlags().StringVar(&p.Namespace, "namespace", "default", "kubernetes namespace")
	rootCmd.PersistentFlags().BoolVarP(&p.Verbose, "verbose", "v", false, "make the operation more talkative")
	rootCmd.PersistentFlags().Bool(builtInLong, false, "do not use remote override commands from CRD")

	commands := map[string]*cobra.Command{
		// App interaction
		"push":   InjectPush(p),
		"delete": InjectDelete(p),
		"apps":   InjectApps(p),
		"proxy":  InjectProxy(p),

		// Environment Variables
		"env":       InjectEnv(p),
		"set-env":   InjectSetEnv(p),
		"unset-env": InjectUnsetEnv(p),

		// Services
		"create-service": InjectCreateService(p),
		"delete-service": InjectDeleteService(p),
		"service":        InjectGetService(p),
		"services":       InjectListServices(p),
		"marketplace":    InjectMarketplace(p),

		// Service Bindings
		"bind-service":  InjectBindingService(p),
		"bindings":      InjectListBindings(p),
		"ubind-service": InjectUnbindService(p),
		"vcap-services": InjectVcapServices(p),

		// Buildpacks
		"buildpacks":        InjectBuildpacks(p),
		"upload-buildpacks": InjectUploadBuildpacks(p),

		// DoctorTests are run in the order they're defined in this list.
		// Tests will stop as soon as one of these top-level tests fails so they
		// should be ordered in a logical way e.g. testing apps should come after
		// testing the cluster because if the cluster isn't working then all the
		// app tests will fail.
		"doctor": doctor.NewDoctorCommand(p, []doctor.DoctorTest{
			{Name: "cluster", Test: pkgdoctor.NewClusterDiagnostic(config.GetKubernetes(p))},
		}),
	}

	if !builtIn(rootCmd.PersistentFlags()) {
		// Override the base commands with ones from the CRD.
		overrides, err := InjectOverrider(p).FetchCommandOverrides()
		if err != nil {
			// nop
			// Proceed without any overrides
		}

		for k, v := range overrides {
			commands[k] = v
		}
	}

	for _, cmd := range commands {
		rootCmd.AddCommand(cmd)
	}

	return rootCmd
}

const (
	builtInLong = "built-in"
)

// builtIn reads a FlagSet and looks for the "built-in" flag and returns its
// value if found. It is necessary to parse the flag ahead of time as it
// determines which commands are loaded and therefore needs to be parsed
// earlier than pflags normally does.
func builtIn(flags *pflag.FlagSet) bool {
	var result bool

	flags.ParseAll(os.Args, func(flag *pflag.Flag, value string) error {
		if flag.Name == builtInLong {
			result, _ = strconv.ParseBool(value)
		}
		return nil
	})
	return result
}
<commit_msg>ensure --built-in flag is adhered to (#53)<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://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 commands

import (
	"os"

	"github.com/GoogleCloudPlatform/kf/pkg/kf/commands/config"
	"github.com/GoogleCloudPlatform/kf/pkg/kf/commands/doctor"
	pkgdoctor "github.com/GoogleCloudPlatform/kf/pkg/kf/doctor"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
)

// NewKfCommand creates the root kf command.
func NewKfCommand() *cobra.Command {
	p := &config.KfParams{
		Output: os.Stdout,
	}

	var rootCmd = &cobra.Command{
		Use:   "kf",
		Short: "kf is like cf for Knative",
		Long: `kf is like cf for Knative

kf supports the following sub-commands:

Apps:
  kf push
  kf delete <app>
  kf apps

Services:
  kf marketplace
  kf create-service
  kf delete-service
  kf service <instance-name>
  kf services
	kf bindings
	kf bind-service <app> <instance-name>
	kf unbind-service <app> <instance-name>

You can get more info by adding the --help flag to any sub-command.
`,
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Usage()
		},
	}

	rootCmd.PersistentFlags().StringVar(&p.KubeCfgFile, "kubeconfig", "", "kubectl config file (default is $HOME/.kube/config)")
	rootCmd.PersistentFlags().StringVar(&p.Namespace, "namespace", "default", "kubernetes namespace")
	rootCmd.PersistentFlags().BoolVarP(&p.Verbose, "verbose", "v", false, "make the operation more talkative")
	rootCmd.PersistentFlags().Bool(builtInLong, false, "do not use remote override commands from CRD")

	commands := map[string]*cobra.Command{
		// App interaction
		"push":   InjectPush(p),
		"delete": InjectDelete(p),
		"apps":   InjectApps(p),
		"proxy":  InjectProxy(p),

		// Environment Variables
		"env":       InjectEnv(p),
		"set-env":   InjectSetEnv(p),
		"unset-env": InjectUnsetEnv(p),

		// Services
		"create-service": InjectCreateService(p),
		"delete-service": InjectDeleteService(p),
		"service":        InjectGetService(p),
		"services":       InjectListServices(p),
		"marketplace":    InjectMarketplace(p),

		// Service Bindings
		"bind-service":  InjectBindingService(p),
		"bindings":      InjectListBindings(p),
		"ubind-service": InjectUnbindService(p),
		"vcap-services": InjectVcapServices(p),

		// Buildpacks
		"buildpacks":        InjectBuildpacks(p),
		"upload-buildpacks": InjectUploadBuildpacks(p),

		// DoctorTests are run in the order they're defined in this list.
		// Tests will stop as soon as one of these top-level tests fails so they
		// should be ordered in a logical way e.g. testing apps should come after
		// testing the cluster because if the cluster isn't working then all the
		// app tests will fail.
		"doctor": doctor.NewDoctorCommand(p, []doctor.DoctorTest{
			{Name: "cluster", Test: pkgdoctor.NewClusterDiagnostic(config.GetKubernetes(p))},
		}),
	}

	if !builtIn() {
		// Override the base commands with ones from the CRD.
		overrides, err := InjectOverrider(p).FetchCommandOverrides()
		if err != nil {
			// nop
			// Proceed without any overrides
		}

		for k, v := range overrides {
			commands[k] = v
		}
	}

	for _, cmd := range commands {
		rootCmd.AddCommand(cmd)
	}

	return rootCmd
}

const (
	builtInLong = "built-in"
)

// builtIn reads a FlagSet and looks for the "built-in" flag and returns its
// value if found. It is necessary to parse the flag ahead of time as it
// determines which commands are loaded and therefore needs to be parsed
// earlier than pflags normally does.
func builtIn() bool {
	flags := pflag.NewFlagSet("built-in", pflag.ContinueOnError)
	result := flags.Bool(builtInLong, false, "")

	// We are only configured to look for --built-in. So when we encounter
	// other flags, we want to keep going.
	flags.ParseErrorsWhitelist.UnknownFlags = true
	flags.Parse(os.Args)
	return *result
}
<|endoftext|>


<commit_before>package fs

import (
	"log"
	"syscall"
	"strings"

	git "github.com/libgit2/git2go"

	"github.com/hanwen/go-fuse/fuse/nodefs"
	"github.com/hanwen/go-fuse/fuse"
)

type multiGitFS struct {
	nodefs.FileSystem

	fsConn *nodefs.FileSystemConnector
	root    nodefs.Node
}

func NewMultiGitFS() *multiGitFS {
	return &multiGitFS{
		FileSystem: nodefs.NewDefaultFileSystem(),
		root: nodefs.NewDefaultNode(),
	}
}

func (m *multiGitFS) String() string {
	return "multigitfs"
}

func (m *multiGitFS) Root() nodefs.Node {
	return m.root
}

func (m *multiGitFS) OnMount(fsConn *nodefs.FileSystemConnector) {
	m.fsConn = fsConn
	config := m.root.Inode().New(true, m.newConfigNode(m.root))

	m.root.Inode().AddChild("config", config)
}

type configNode struct {
	fs *multiGitFS

	nodefs.Node

	// non-config node corresponding to this one.
	corresponding nodefs.Node
}

func (fs *multiGitFS) newConfigNode(corresponding nodefs.Node) *configNode {
	return &configNode{
		fs: fs,
		Node: nodefs.NewDefaultNode(),
		corresponding: corresponding,
	}
}

type gitConfigNode struct {
	nodefs.Node

	repo string
	treeish string
}

func newGitConfigNode(repo, tree string) *gitConfigNode {
	return &gitConfigNode{
		Node: nodefs.NewDefaultNode(),
		repo: repo,
		treeish: tree,
	}
}

func (n *gitConfigNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = syscall.S_IFLNK
	return fuse.OK
}

func (n *gitConfigNode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
	return []byte(n.repo + ":" + n.treeish), fuse.OK
}

func (n *configNode) Symlink(name string, content string, context *fuse.Context) (newNode nodefs.Node, code fuse.Status) {
	components := strings.Split(content, ":")
	if len(components) != 2 {
		return nil, fuse.Status(syscall.EINVAL)
	}

	repo, err := git.OpenRepository(components[0])
	if err != nil {
		log.Printf("OpenRepository(%q): %v", components[0], err)
		return nil, fuse.ENOENT
	}

	fs, err := NewTreeFS(repo, components[1])
	if err != nil {
		log.Printf("NewTreeFS(%q): %v", components[1], err)
		return nil, fuse.ENOENT
	}

	log.Println("mounting", components)
	// TODO - inherit options.
	if code := n.fs.fsConn.Mount(n.corresponding.Inode(), name, fs, nil); !code.Ok() {
		return nil, code
	}

	linkNode := newGitConfigNode(components[0], components[1])
	ch := n.Inode().New(false, linkNode)
	n.Inode().AddChild(name, ch)
	return linkNode, fuse.OK
}
<commit_msg>Set long timeouts in GitMultiFS<commit_after>package fs

import (
	"log"
	"time"
	"syscall"
	"strings"

	git "github.com/libgit2/git2go"

	"github.com/hanwen/go-fuse/fuse/nodefs"
	"github.com/hanwen/go-fuse/fuse"
)

type multiGitFS struct {
	nodefs.FileSystem

	fsConn *nodefs.FileSystemConnector
	root    nodefs.Node
}

func NewMultiGitFS() *multiGitFS {
	return &multiGitFS{
		FileSystem: nodefs.NewDefaultFileSystem(),
		root: nodefs.NewDefaultNode(),
	}
}

func (m *multiGitFS) String() string {
	return "multigitfs"
}

func (m *multiGitFS) Root() nodefs.Node {
	return m.root
}

func (m *multiGitFS) OnMount(fsConn *nodefs.FileSystemConnector) {
	m.fsConn = fsConn
	config := m.root.Inode().New(true, m.newConfigNode(m.root))

	m.root.Inode().AddChild("config", config)
}

type configNode struct {
	fs *multiGitFS

	nodefs.Node

	// non-config node corresponding to this one.
	corresponding nodefs.Node
}

func (fs *multiGitFS) newConfigNode(corresponding nodefs.Node) *configNode {
	return &configNode{
		fs: fs,
		Node: nodefs.NewDefaultNode(),
		corresponding: corresponding,
	}
}

type gitConfigNode struct {
	nodefs.Node

	repo string
	treeish string
}

func newGitConfigNode(repo, tree string) *gitConfigNode {
	return &gitConfigNode{
		Node: nodefs.NewDefaultNode(),
		repo: repo,
		treeish: tree,
	}
}

func (n *gitConfigNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = syscall.S_IFLNK
	return fuse.OK
}

func (n *gitConfigNode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
	return []byte(n.repo + ":" + n.treeish), fuse.OK
}

func (n *configNode) Symlink(name string, content string, context *fuse.Context) (newNode nodefs.Node, code fuse.Status) {
	components := strings.Split(content, ":")
	if len(components) != 2 {
		return nil, fuse.Status(syscall.EINVAL)
	}

	repo, err := git.OpenRepository(components[0])
	if err != nil {
		log.Printf("OpenRepository(%q): %v", components[0], err)
		return nil, fuse.ENOENT
	}

	fs, err := NewTreeFS(repo, components[1])
	if err != nil {
		log.Printf("NewTreeFS(%q): %v", components[1], err)
		return nil, fuse.ENOENT
	}

	log.Println("mounting", components)
	opts := &nodefs.Options{
		EntryTimeout: time.Hour,
		NegativeTimeout: time.Hour,
		AttrTimeout: time.Hour,
		PortableInodes: true,
	}
	if code := n.fs.fsConn.Mount(n.corresponding.Inode(), name, fs, opts); !code.Ok() {
		return nil, code
	}

	linkNode := newGitConfigNode(components[0], components[1])
	ch := n.Inode().New(false, linkNode)
	n.Inode().AddChild(name, ch)
	return linkNode, fuse.OK
}
<|endoftext|>


<commit_before>package log_test

import (
	"net/http"
	"net/http/httptest"

	"github.com/vardius/go-api-boilerplate/pkg/log"
)

func ExampleLogger_LogRequest() {
	logger := log.New("debug")
	h := logger.LogRequest(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/", nil)

	h.ServeHTTP(w, req)
}
<commit_msg>Rename example<commit_after>package log_test

import (
	"net/http"
	"net/http/httptest"

	"github.com/vardius/go-api-boilerplate/pkg/log"
)

func ExampleLogger_LogRequest() {
	l := log.New("development")
	h := l.LogRequest(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/", nil)

	h.ServeHTTP(w, req)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 ipvs

import (
	"k8s.io/apimachinery/pkg/util/sets"
	utilversion "k8s.io/apimachinery/pkg/util/version"
	utilipset "k8s.io/kubernetes/pkg/util/ipset"

	"fmt"
	"k8s.io/klog"
)

const (
	// MinIPSetCheckVersion is the min ipset version we need.  IPv6 is supported in ipset 6.x
	MinIPSetCheckVersion = "6.0"

	kubeLoopBackIPSetComment = "Kubernetes endpoints dst ip:port, source ip for solving hairpin purpose"
	kubeLoopBackIPSet        = "KUBE-LOOP-BACK"

	kubeClusterIPSetComment = "Kubernetes service cluster ip + port for masquerade purpose"
	kubeClusterIPSet        = "KUBE-CLUSTER-IP"

	kubeExternalIPSetComment = "Kubernetes service external ip + port for masquerade and filter purpose"
	kubeExternalIPSet        = "KUBE-EXTERNAL-IP"

	kubeLoadBalancerSetComment = "Kubernetes service lb portal"
	kubeLoadBalancerSet        = "KUBE-LOAD-BALANCER"

	kubeLoadBalancerLocalSetComment = "Kubernetes service load balancer ip + port with externalTrafficPolicy=local"
	kubeLoadBalancerLocalSet        = "KUBE-LOAD-BALANCER-LOCAL"

	kubeLoadbalancerFWSetComment = "Kubernetes service load balancer ip + port for load balancer with sourceRange"
	kubeLoadbalancerFWSet        = "KUBE-LOAD-BALANCER-FW"

	kubeLoadBalancerSourceIPSetComment = "Kubernetes service load balancer ip + port + source IP for packet filter purpose"
	kubeLoadBalancerSourceIPSet        = "KUBE-LOAD-BALANCER-SOURCE-IP"

	kubeLoadBalancerSourceCIDRSetComment = "Kubernetes service load balancer ip + port + source cidr for packet filter purpose"
	kubeLoadBalancerSourceCIDRSet        = "KUBE-LOAD-BALANCER-SOURCE-CIDR"

	kubeNodePortSetTCPComment = "Kubernetes nodeport TCP port for masquerade purpose"
	kubeNodePortSetTCP        = "KUBE-NODE-PORT-TCP"

	kubeNodePortLocalSetTCPComment = "Kubernetes nodeport TCP port with externalTrafficPolicy=local"
	kubeNodePortLocalSetTCP        = "KUBE-NODE-PORT-LOCAL-TCP"

	kubeNodePortSetUDPComment = "Kubernetes nodeport UDP port for masquerade purpose"
	kubeNodePortSetUDP        = "KUBE-NODE-PORT-UDP"

	kubeNodePortLocalSetUDPComment = "Kubernetes nodeport UDP port with externalTrafficPolicy=local"
	kubeNodePortLocalSetUDP        = "KUBE-NODE-PORT-LOCAL-UDP"

	kubeNodePortSetSCTPComment = "Kubernetes nodeport SCTP port for masquerade purpose"
	kubeNodePortSetSCTP        = "KUBE-NODE-PORT-SCTP"

	kubeNodePortLocalSetSCTPComment = "Kubernetes nodeport SCTP port with externalTrafficPolicy=local"
	kubeNodePortLocalSetSCTP        = "KUBE-NODE-PORT-LOCAL-SCTP"
)

// IPSetVersioner can query the current ipset version.
type IPSetVersioner interface {
	// returns "X.Y"
	GetVersion() (string, error)
}

// IPSet wraps util/ipset which is used by IPVS proxier.
type IPSet struct {
	utilipset.IPSet
	// activeEntries is the current active entries of the ipset.
	activeEntries sets.String
	// handle is the util ipset interface handle.
	handle utilipset.Interface
}

// NewIPSet initialize a new IPSet struct
func NewIPSet(handle utilipset.Interface, name string, setType utilipset.Type, isIPv6 bool, comment string) *IPSet {
	hashFamily := utilipset.ProtocolFamilyIPV4
	if isIPv6 {
		hashFamily = utilipset.ProtocolFamilyIPV6
	}
	set := &IPSet{
		IPSet: utilipset.IPSet{
			Name:       name,
			SetType:    setType,
			HashFamily: hashFamily,
			Comment:    comment,
		},
		activeEntries: sets.NewString(),
		handle:        handle,
	}
	return set
}

func (set *IPSet) validateEntry(entry *utilipset.Entry) bool {
	return entry.Validate(&set.IPSet)
}

func (set *IPSet) isEmpty() bool {
	return len(set.activeEntries.UnsortedList()) == 0
}

func (set *IPSet) getComment() string {
	return fmt.Sprintf("\"%s\"", set.Comment)
}

func (set *IPSet) resetEntries() {
	set.activeEntries = sets.NewString()
}

func (set *IPSet) syncIPSetEntries() {
	appliedEntries, err := set.handle.ListEntries(set.Name)
	if err != nil {
		klog.Errorf("Failed to list ip set entries, error: %v", err)
		return
	}

	// currentIPSetEntries represents Endpoints watched from API Server.
	currentIPSetEntries := sets.NewString()
	for _, appliedEntry := range appliedEntries {
		currentIPSetEntries.Insert(appliedEntry)
	}

	if !set.activeEntries.Equal(currentIPSetEntries) {
		// Clean legacy entries
		for _, entry := range currentIPSetEntries.Difference(set.activeEntries).List() {
			if err := set.handle.DelEntry(entry, set.Name); err != nil {
				if !utilipset.IsNotFoundError(err) {
					klog.Errorf("Failed to delete ip set entry: %s from ip set: %s, error: %v", entry, set.Name, err)
				}
			} else {
				klog.V(3).Infof("Successfully delete legacy ip set entry: %s from ip set: %s", entry, set.Name)
			}
		}
		// Create active entries
		for _, entry := range set.activeEntries.Difference(currentIPSetEntries).List() {
			if err := set.handle.AddEntry(entry, &set.IPSet, true); err != nil {
				klog.Errorf("Failed to add entry: %v to ip set: %s, error: %v", entry, set.Name, err)
			} else {
				klog.V(3).Infof("Successfully add entry: %v to ip set: %s", entry, set.Name)
			}
		}
	}
}

func ensureIPSet(set *IPSet) error {
	if err := set.handle.CreateSet(&set.IPSet, true); err != nil {
		klog.Errorf("Failed to make sure ip set: %v exist, error: %v", set, err)
		return err
	}
	return nil
}

// checkMinVersion checks if ipset current version satisfies required min version
func checkMinVersion(vstring string) bool {
	version, err := utilversion.ParseGeneric(vstring)
	if err != nil {
		klog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err)
		return false
	}

	minVersion, err := utilversion.ParseGeneric(MinIPSetCheckVersion)
	if err != nil {
		klog.Errorf("MinCheckVersion (%s) is not a valid version string: %v", MinIPSetCheckVersion, err)
		return false
	}
	return !version.LessThan(minVersion)
}
<commit_msg>create new ipset KUBE-NODE-PORT-SCTP-HASH and KUBE-NODE-PORT-LOCAL-SCTP-HASH for ipvs proxier<commit_after>/*
Copyright 2017 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 ipvs

import (
	"k8s.io/apimachinery/pkg/util/sets"
	utilversion "k8s.io/apimachinery/pkg/util/version"
	utilipset "k8s.io/kubernetes/pkg/util/ipset"

	"fmt"
	"k8s.io/klog"
)

const (
	// MinIPSetCheckVersion is the min ipset version we need.  IPv6 is supported in ipset 6.x
	MinIPSetCheckVersion = "6.0"

	kubeLoopBackIPSetComment = "Kubernetes endpoints dst ip:port, source ip for solving hairpin purpose"
	kubeLoopBackIPSet        = "KUBE-LOOP-BACK"

	kubeClusterIPSetComment = "Kubernetes service cluster ip + port for masquerade purpose"
	kubeClusterIPSet        = "KUBE-CLUSTER-IP"

	kubeExternalIPSetComment = "Kubernetes service external ip + port for masquerade and filter purpose"
	kubeExternalIPSet        = "KUBE-EXTERNAL-IP"

	kubeLoadBalancerSetComment = "Kubernetes service lb portal"
	kubeLoadBalancerSet        = "KUBE-LOAD-BALANCER"

	kubeLoadBalancerLocalSetComment = "Kubernetes service load balancer ip + port with externalTrafficPolicy=local"
	kubeLoadBalancerLocalSet        = "KUBE-LOAD-BALANCER-LOCAL"

	kubeLoadbalancerFWSetComment = "Kubernetes service load balancer ip + port for load balancer with sourceRange"
	kubeLoadbalancerFWSet        = "KUBE-LOAD-BALANCER-FW"

	kubeLoadBalancerSourceIPSetComment = "Kubernetes service load balancer ip + port + source IP for packet filter purpose"
	kubeLoadBalancerSourceIPSet        = "KUBE-LOAD-BALANCER-SOURCE-IP"

	kubeLoadBalancerSourceCIDRSetComment = "Kubernetes service load balancer ip + port + source cidr for packet filter purpose"
	kubeLoadBalancerSourceCIDRSet        = "KUBE-LOAD-BALANCER-SOURCE-CIDR"

	kubeNodePortSetTCPComment = "Kubernetes nodeport TCP port for masquerade purpose"
	kubeNodePortSetTCP        = "KUBE-NODE-PORT-TCP"

	kubeNodePortLocalSetTCPComment = "Kubernetes nodeport TCP port with externalTrafficPolicy=local"
	kubeNodePortLocalSetTCP        = "KUBE-NODE-PORT-LOCAL-TCP"

	kubeNodePortSetUDPComment = "Kubernetes nodeport UDP port for masquerade purpose"
	kubeNodePortSetUDP        = "KUBE-NODE-PORT-UDP"

	kubeNodePortLocalSetUDPComment = "Kubernetes nodeport UDP port with externalTrafficPolicy=local"
	kubeNodePortLocalSetUDP        = "KUBE-NODE-PORT-LOCAL-UDP"

	// This ipset is no longer active but still used in previous versions.
	// DO NOT create an ipset using this name
	legacyKubeNodePortSetSCTPComment = "Kubernetes nodeport SCTP port for masquerade purpose"
	legacyKubeNodePortSetSCTP        = "KUBE-NODE-PORT-SCTP"

	// This ipset is no longer active but still used in previous versions.
	// DO NOT create an ipset using this name
	legacyKubeNodePortLocalSetSCTPComment = "Kubernetes nodeport SCTP port with externalTrafficPolicy=local"
	legacyKubeNodePortLocalSetSCTP        = "KUBE-NODE-PORT-LOCAL-SCTP"

	kubeNodePortSetSCTPComment = "Kubernetes nodeport SCTP port for masquerade purpose with type 'hash ip:port'"
	kubeNodePortSetSCTP        = "KUBE-NODE-PORT-SCTP-HASH"

	kubeNodePortLocalSetSCTPComment = "Kubernetes nodeport SCTP port with externalTrafficPolicy=local with type 'hash ip:port'"
	kubeNodePortLocalSetSCTP        = "KUBE-NODE-PORT-LOCAL-SCTP-HASH"
)

// IPSetVersioner can query the current ipset version.
type IPSetVersioner interface {
	// returns "X.Y"
	GetVersion() (string, error)
}

// IPSet wraps util/ipset which is used by IPVS proxier.
type IPSet struct {
	utilipset.IPSet
	// activeEntries is the current active entries of the ipset.
	activeEntries sets.String
	// handle is the util ipset interface handle.
	handle utilipset.Interface
}

// NewIPSet initialize a new IPSet struct
func NewIPSet(handle utilipset.Interface, name string, setType utilipset.Type, isIPv6 bool, comment string) *IPSet {
	hashFamily := utilipset.ProtocolFamilyIPV4
	if isIPv6 {
		hashFamily = utilipset.ProtocolFamilyIPV6
	}
	set := &IPSet{
		IPSet: utilipset.IPSet{
			Name:       name,
			SetType:    setType,
			HashFamily: hashFamily,
			Comment:    comment,
		},
		activeEntries: sets.NewString(),
		handle:        handle,
	}
	return set
}

func (set *IPSet) validateEntry(entry *utilipset.Entry) bool {
	return entry.Validate(&set.IPSet)
}

func (set *IPSet) isEmpty() bool {
	return len(set.activeEntries.UnsortedList()) == 0
}

func (set *IPSet) getComment() string {
	return fmt.Sprintf("\"%s\"", set.Comment)
}

func (set *IPSet) resetEntries() {
	set.activeEntries = sets.NewString()
}

func (set *IPSet) syncIPSetEntries() {
	appliedEntries, err := set.handle.ListEntries(set.Name)
	if err != nil {
		klog.Errorf("Failed to list ip set entries, error: %v", err)
		return
	}

	// currentIPSetEntries represents Endpoints watched from API Server.
	currentIPSetEntries := sets.NewString()
	for _, appliedEntry := range appliedEntries {
		currentIPSetEntries.Insert(appliedEntry)
	}

	if !set.activeEntries.Equal(currentIPSetEntries) {
		// Clean legacy entries
		for _, entry := range currentIPSetEntries.Difference(set.activeEntries).List() {
			if err := set.handle.DelEntry(entry, set.Name); err != nil {
				if !utilipset.IsNotFoundError(err) {
					klog.Errorf("Failed to delete ip set entry: %s from ip set: %s, error: %v", entry, set.Name, err)
				}
			} else {
				klog.V(3).Infof("Successfully delete legacy ip set entry: %s from ip set: %s", entry, set.Name)
			}
		}
		// Create active entries
		for _, entry := range set.activeEntries.Difference(currentIPSetEntries).List() {
			if err := set.handle.AddEntry(entry, &set.IPSet, true); err != nil {
				klog.Errorf("Failed to add entry: %v to ip set: %s, error: %v", entry, set.Name, err)
			} else {
				klog.V(3).Infof("Successfully add entry: %v to ip set: %s", entry, set.Name)
			}
		}
	}
}

func ensureIPSet(set *IPSet) error {
	if err := set.handle.CreateSet(&set.IPSet, true); err != nil {
		klog.Errorf("Failed to make sure ip set: %v exist, error: %v", set, err)
		return err
	}
	return nil
}

// checkMinVersion checks if ipset current version satisfies required min version
func checkMinVersion(vstring string) bool {
	version, err := utilversion.ParseGeneric(vstring)
	if err != nil {
		klog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err)
		return false
	}

	minVersion, err := utilversion.ParseGeneric(MinIPSetCheckVersion)
	if err != nil {
		klog.Errorf("MinCheckVersion (%s) is not a valid version string: %v", MinIPSetCheckVersion, err)
		return false
	}
	return !version.LessThan(minVersion)
}
<|endoftext|>


<commit_before>// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package repro

import (
	"math/rand"
	"testing"
	"time"

	"github.com/google/syzkaller/prog"
)

func initTest(t *testing.T) (*rand.Rand, int) {
	iters := 1000
	if testing.Short() {
		iters = 100
	}
	seed := int64(time.Now().UnixNano())
	rs := rand.NewSource(seed)
	t.Logf("seed=%v", seed)
	return rand.New(rs), iters
}

func TestBisect(t *testing.T) {
	rd, iters := initTest(t)
	for n := 0; n < iters; n++ {
		var progs []*prog.LogEntry
		numTotal := rd.Intn(300)
		numGuilty := 0
		for i := 0; i < numTotal; i++ {
			var prog prog.LogEntry
			if rd.Intn(30) == 0 {
				prog.Proc = 42
				numGuilty += 1
			}
			progs = append(progs, &prog)
		}
		if numGuilty == 0 {
			var prog prog.LogEntry
			prog.Proc = 42
			progs = append(progs, &prog)
			numGuilty += 1
		}
		progs, _ = bisectProgs(progs, "test", func(p []*prog.LogEntry) (bool, error) {
			guilty := 0
			for _, prog := range p {
				if prog.Proc == 42 {
					guilty += 1
				}
			}
			return guilty == numGuilty, nil
		})
		if len(progs) != numGuilty {
			t.Fatalf("bisect test failed: wrong number of guilty progs: got: %v, want: %v", len(progs), numGuilty)
		}
		for _, prog := range progs {
			if prog.Proc != 42 {
				t.Fatalf("bisect test failed: wrong program is guilty: progs: %v", progs)
			}
		}
	}
}
<commit_msg>repro: fix tests (#266)<commit_after>// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package repro

import (
	"math/rand"
	"testing"
	"time"

	"github.com/google/syzkaller/prog"
)

func initTest(t *testing.T) (*rand.Rand, int) {
	iters := 1000
	if testing.Short() {
		iters = 100
	}
	seed := int64(time.Now().UnixNano())
	rs := rand.NewSource(seed)
	t.Logf("seed=%v", seed)
	return rand.New(rs), iters
}

func TestBisect(t *testing.T) {
	ctx := &context{}

	rd, iters := initTest(t)
	for n := 0; n < iters; n++ {
		var progs []*prog.LogEntry
		numTotal := rd.Intn(300)
		numGuilty := 0
		for i := 0; i < numTotal; i++ {
			var prog prog.LogEntry
			if rd.Intn(30) == 0 {
				prog.Proc = 42
				numGuilty += 1
			}
			progs = append(progs, &prog)
		}
		if numGuilty == 0 {
			var prog prog.LogEntry
			prog.Proc = 42
			progs = append(progs, &prog)
			numGuilty += 1
		}
		progs, _ = ctx.bisectProgs(progs, func(p []*prog.LogEntry) (bool, error) {
			guilty := 0
			for _, prog := range p {
				if prog.Proc == 42 {
					guilty += 1
				}
			}
			return guilty == numGuilty, nil
		})
		if len(progs) != numGuilty {
			t.Fatalf("bisect test failed: wrong number of guilty progs: got: %v, want: %v", len(progs), numGuilty)
		}
		for _, prog := range progs {
			if prog.Proc != 42 {
				t.Fatalf("bisect test failed: wrong program is guilty: progs: %v", progs)
			}
		}
	}
}
<|endoftext|>


<commit_before>package sessions

import (
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/crypto"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/utils"
	"github.com/cozy/echo"
)

// SessionCookieName is name of the cookie created by cozy
const SessionCookieName = "cozysessid"

// SessionMaxAge is the maximum duration of the session in seconds
const SessionMaxAge = 30 * 24 * time.Hour

var (
	// ErrNoCookie is returned by GetSession if there is no cookie
	ErrNoCookie = errors.New("No session cookie")
	// ErrExpired is returned when the session has expired
	ErrExpired = errors.New("Session expired")
	// ErrInvalidID is returned by GetSession if the cookie contains wrong ID
	ErrInvalidID = errors.New("Session cookie has wrong ID")
)

// A Session is an instance opened in a browser
type Session struct {
	Instance  *instance.Instance `json:"-"`
	DocID     string             `json:"_id,omitempty"`
	DocRev    string             `json:"_rev,omitempty"`
	CreatedAt time.Time          `json:"created_at"`
	LastSeen  time.Time          `json:"last_seen"`
}

// DocType implements couchdb.Doc
func (s *Session) DocType() string { return consts.Sessions }

// ID implements couchdb.Doc
func (s *Session) ID() string { return s.DocID }

// SetID implements couchdb.Doc
func (s *Session) SetID(v string) { s.DocID = v }

// Rev implements couchdb.Doc
func (s *Session) Rev() string { return s.DocRev }

// SetRev implements couchdb.Doc
func (s *Session) SetRev(v string) { s.DocRev = v }

// Clone implements couchdb.Doc
func (s *Session) Clone() couchdb.Doc {
	cloned := *s
	if cloned.Instance != nil {
		tmp := *s.Instance
		cloned.Instance = &tmp
	}
	return &cloned
}

// ensure Session implements couchdb.Doc
var _ couchdb.Doc = (*Session)(nil)

// OlderThan checks if a session last seen is older than t from now
func (s *Session) OlderThan(t time.Duration) bool {
	return time.Now().After(s.LastSeen.Add(t))
}

// New creates a session in couchdb for the given instance
func New(i *instance.Instance) (*Session, error) {
	now := time.Now()
	s := &Session{
		Instance:  i,
		LastSeen:  now,
		CreatedAt: now,
	}
	if err := couchdb.CreateDoc(i, s); err != nil {
		return nil, err
	}
	getCache().Set(i, s.DocID, s)
	return s, nil
}

// Get fetches the session
func Get(i *instance.Instance, sessionID string) (*Session, error) {
	updateCache := false

	s := getCache().Get(i, sessionID)
	if s == nil {
		s = &Session{}
		err := couchdb.GetDoc(i, consts.Sessions, sessionID, s)
		if couchdb.IsNotFoundError(err) {
			return nil, ErrInvalidID
		}
		if err != nil {
			return nil, err
		}
		updateCache = true
	}
	s.Instance = i

	// If the session is older than the session max age, it has expired and
	// should be deleted.
	if s.OlderThan(SessionMaxAge) {
		err := couchdb.DeleteDoc(i, s)
		if err != nil {
			i.Logger().Warn("[session] Failed to delete expired session:", err)
		}
		getCache().Revoke(i, s.DocID)
		return nil, ErrExpired
	}

	// In order to avoid too many updates of the session document, we have an
	// update period of one day for the `last_seen` date, which is a good enough
	// granularity.
	if s.OlderThan(24 * time.Hour) {
		lastSeen := s.LastSeen
		s.LastSeen = time.Now()
		err := couchdb.UpdateDoc(i, s)
		if err != nil {
			i.Logger().Warn("[session] Failed to update session last seen:", err)
			s.LastSeen = lastSeen
			updateCache = false
		} else {
			updateCache = true
		}
	}

	if updateCache {
		getCache().Set(i, s.DocID, s)
	}

	return s, nil
}

// FromCookie retrieves the session from a echo.Context cookies.
func FromCookie(c echo.Context, i *instance.Instance) (*Session, error) {
	cookie, err := c.Cookie(SessionCookieName)
	if err != nil || cookie.Value == "" {
		return nil, ErrNoCookie
	}

	sessionID, err := crypto.DecodeAuthMessage(cookieSessionMACConfig, i.SessionSecret,
		[]byte(cookie.Value), nil)
	if err != nil {
		return nil, err
	}

	return Get(i, string(sessionID))
}

// FromAppCookie retrives the session from an application submain cookie.
func FromAppCookie(c echo.Context, i *instance.Instance, slug string) (*Session, error) {
	if config.GetConfig().Subdomains == config.FlatSubdomains {
		cookie, err := c.Cookie(SessionCookieName)
		if err != nil || cookie.Value == "" {
			return nil, ErrNoCookie
		}

		sessionID, err := crypto.DecodeAuthMessage(cookieAppMACConfig, i.SessionSecret,
			[]byte(cookie.Value), []byte(slug))
		if err != nil {
			return nil, err
		}

		return Get(i, string(sessionID))
	}
	return FromCookie(c, i)
}

// GetAll returns all the active sessions
func GetAll(inst *instance.Instance) ([]*Session, error) {
	var sessions []*Session
	if err := couchdb.GetAllDocs(inst, consts.Sessions, nil, &sessions); err != nil {
		return nil, err
	}
	return sessions, nil
}

// Delete is a function to delete the session in couchdb,
// and returns a cookie with a negative MaxAge to clear it
func (s *Session) Delete(i *instance.Instance) *http.Cookie {
	err := couchdb.DeleteDoc(i, s)
	getCache().Revoke(i, s.DocID)
	if err != nil {
		i.Logger().Error("[session] Failed to delete session:", err)
	}
	return &http.Cookie{
		Name:   SessionCookieName,
		Value:  "",
		MaxAge: -1,
		Path:   "/",
		Domain: utils.StripPort("." + i.Domain),
	}
}

// ToCookie returns an http.Cookie for this Session
func (s *Session) ToCookie() (*http.Cookie, error) {
	encoded, err := crypto.EncodeAuthMessage(cookieSessionMACConfig, s.Instance.SessionSecret, []byte(s.ID()), nil)
	if err != nil {
		return nil, err
	}

	return &http.Cookie{
		Name:     SessionCookieName,
		Value:    string(encoded),
		MaxAge:   10 * 365 * 24 * 3600, // 10 years
		Path:     "/",
		Domain:   utils.StripPort("." + s.Instance.Domain),
		Secure:   !s.Instance.Dev,
		HttpOnly: true,
	}, nil
}

// ToAppCookie returns an http.Cookie for this Session on an app subdomain
func (s *Session) ToAppCookie(domain, slug string) (*http.Cookie, error) {
	encoded, err := crypto.EncodeAuthMessage(cookieAppMACConfig, s.Instance.SessionSecret, []byte(s.ID()), []byte(slug))
	if err != nil {
		return nil, err
	}

	return &http.Cookie{
		Name:     SessionCookieName,
		Value:    string(encoded),
		MaxAge:   0, // "session cookie", expiring when the browser is closed
		Path:     "/",
		Domain:   utils.StripPort(domain),
		Secure:   !s.Instance.Dev,
		HttpOnly: true,
	}, nil
}

// DeleteOthers will remove all sessions except the one given in parameter.
func DeleteOthers(i *instance.Instance, selfSessionID string) error {
	var sessions []*Session
	err := couchdb.ForeachDocs(i, consts.Sessions, func(_ string, data json.RawMessage) error {
		var s Session
		if err := json.Unmarshal(data, &s); err != nil {
			return err
		}
		sessions = append(sessions, &s)
		return nil
	})
	if err != nil {
		return err
	}
	for _, s := range sessions {
		if s.ID() != selfSessionID {
			s.Delete(i)
		}
	}
	return nil
}

// cookieMACConfig returns the options to authenticate the session cookie.
//
// We rely on a MACed cookie value, without additional encryption of the
// message since it should not contain critical private informations and is
// protected by HTTPs (secure cookie).
//
// About MaxLength, for a session of size 100 bytes
//
//       8 bytes time
//   +  32 bytes HMAC-SHA256
//   + 100 bytes session
//   + base64 encoding (4*n/3)
//   < 200 bytes
//
// 256 bytes should be sufficient enough to support any type of session.
//
var cookieAppMACConfig = crypto.MACConfig{
	Name:   SessionCookieName,
	MaxAge: SessionMaxAge,
	MaxLen: 256,
}

var cookieSessionMACConfig = crypto.MACConfig{
	Name:   SessionCookieName,
	MaxLen: 256,
}
<commit_msg>Use contextual domain name for cookies domain<commit_after>package sessions

import (
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/crypto"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/utils"
	"github.com/cozy/echo"
)

// SessionCookieName is name of the cookie created by cozy
const SessionCookieName = "cozysessid"

// SessionMaxAge is the maximum duration of the session in seconds
const SessionMaxAge = 30 * 24 * time.Hour

var (
	// ErrNoCookie is returned by GetSession if there is no cookie
	ErrNoCookie = errors.New("No session cookie")
	// ErrExpired is returned when the session has expired
	ErrExpired = errors.New("Session expired")
	// ErrInvalidID is returned by GetSession if the cookie contains wrong ID
	ErrInvalidID = errors.New("Session cookie has wrong ID")
)

// A Session is an instance opened in a browser
type Session struct {
	Instance  *instance.Instance `json:"-"`
	DocID     string             `json:"_id,omitempty"`
	DocRev    string             `json:"_rev,omitempty"`
	CreatedAt time.Time          `json:"created_at"`
	LastSeen  time.Time          `json:"last_seen"`
}

// DocType implements couchdb.Doc
func (s *Session) DocType() string { return consts.Sessions }

// ID implements couchdb.Doc
func (s *Session) ID() string { return s.DocID }

// SetID implements couchdb.Doc
func (s *Session) SetID(v string) { s.DocID = v }

// Rev implements couchdb.Doc
func (s *Session) Rev() string { return s.DocRev }

// SetRev implements couchdb.Doc
func (s *Session) SetRev(v string) { s.DocRev = v }

// Clone implements couchdb.Doc
func (s *Session) Clone() couchdb.Doc {
	cloned := *s
	if cloned.Instance != nil {
		tmp := *s.Instance
		cloned.Instance = &tmp
	}
	return &cloned
}

// ensure Session implements couchdb.Doc
var _ couchdb.Doc = (*Session)(nil)

// OlderThan checks if a session last seen is older than t from now
func (s *Session) OlderThan(t time.Duration) bool {
	return time.Now().After(s.LastSeen.Add(t))
}

// New creates a session in couchdb for the given instance
func New(i *instance.Instance) (*Session, error) {
	now := time.Now()
	s := &Session{
		Instance:  i,
		LastSeen:  now,
		CreatedAt: now,
	}
	if err := couchdb.CreateDoc(i, s); err != nil {
		return nil, err
	}
	getCache().Set(i, s.DocID, s)
	return s, nil
}

// Get fetches the session
func Get(i *instance.Instance, sessionID string) (*Session, error) {
	updateCache := false

	s := getCache().Get(i, sessionID)
	if s == nil {
		s = &Session{}
		err := couchdb.GetDoc(i, consts.Sessions, sessionID, s)
		if couchdb.IsNotFoundError(err) {
			return nil, ErrInvalidID
		}
		if err != nil {
			return nil, err
		}
		updateCache = true
	}
	s.Instance = i

	// If the session is older than the session max age, it has expired and
	// should be deleted.
	if s.OlderThan(SessionMaxAge) {
		err := couchdb.DeleteDoc(i, s)
		if err != nil {
			i.Logger().Warn("[session] Failed to delete expired session:", err)
		}
		getCache().Revoke(i, s.DocID)
		return nil, ErrExpired
	}

	// In order to avoid too many updates of the session document, we have an
	// update period of one day for the `last_seen` date, which is a good enough
	// granularity.
	if s.OlderThan(24 * time.Hour) {
		lastSeen := s.LastSeen
		s.LastSeen = time.Now()
		err := couchdb.UpdateDoc(i, s)
		if err != nil {
			i.Logger().Warn("[session] Failed to update session last seen:", err)
			s.LastSeen = lastSeen
			updateCache = false
		} else {
			updateCache = true
		}
	}

	if updateCache {
		getCache().Set(i, s.DocID, s)
	}

	return s, nil
}

// FromCookie retrieves the session from a echo.Context cookies.
func FromCookie(c echo.Context, i *instance.Instance) (*Session, error) {
	cookie, err := c.Cookie(SessionCookieName)
	if err != nil || cookie.Value == "" {
		return nil, ErrNoCookie
	}

	sessionID, err := crypto.DecodeAuthMessage(cookieSessionMACConfig, i.SessionSecret,
		[]byte(cookie.Value), nil)
	if err != nil {
		return nil, err
	}

	return Get(i, string(sessionID))
}

// FromAppCookie retrives the session from an application submain cookie.
func FromAppCookie(c echo.Context, i *instance.Instance, slug string) (*Session, error) {
	if config.GetConfig().Subdomains == config.FlatSubdomains {
		cookie, err := c.Cookie(SessionCookieName)
		if err != nil || cookie.Value == "" {
			return nil, ErrNoCookie
		}

		sessionID, err := crypto.DecodeAuthMessage(cookieAppMACConfig, i.SessionSecret,
			[]byte(cookie.Value), []byte(slug))
		if err != nil {
			return nil, err
		}

		return Get(i, string(sessionID))
	}
	return FromCookie(c, i)
}

// GetAll returns all the active sessions
func GetAll(inst *instance.Instance) ([]*Session, error) {
	var sessions []*Session
	if err := couchdb.GetAllDocs(inst, consts.Sessions, nil, &sessions); err != nil {
		return nil, err
	}
	return sessions, nil
}

// Delete is a function to delete the session in couchdb,
// and returns a cookie with a negative MaxAge to clear it
func (s *Session) Delete(i *instance.Instance) *http.Cookie {
	err := couchdb.DeleteDoc(i, s)
	getCache().Revoke(i, s.DocID)
	if err != nil {
		i.Logger().Error("[session] Failed to delete session:", err)
	}
	return &http.Cookie{
		Name:   SessionCookieName,
		Value:  "",
		MaxAge: -1,
		Path:   "/",
		Domain: utils.StripPort("." + i.ContextualDomain()),
	}
}

// ToCookie returns an http.Cookie for this Session
func (s *Session) ToCookie() (*http.Cookie, error) {
	encoded, err := crypto.EncodeAuthMessage(cookieSessionMACConfig, s.Instance.SessionSecret, []byte(s.ID()), nil)
	if err != nil {
		return nil, err
	}

	return &http.Cookie{
		Name:     SessionCookieName,
		Value:    string(encoded),
		MaxAge:   10 * 365 * 24 * 3600, // 10 years
		Path:     "/",
		Domain:   utils.StripPort("." + s.Instance.ContextualDomain()),
		Secure:   !s.Instance.Dev,
		HttpOnly: true,
	}, nil
}

// ToAppCookie returns an http.Cookie for this Session on an app subdomain
func (s *Session) ToAppCookie(domain, slug string) (*http.Cookie, error) {
	encoded, err := crypto.EncodeAuthMessage(cookieAppMACConfig, s.Instance.SessionSecret, []byte(s.ID()), []byte(slug))
	if err != nil {
		return nil, err
	}

	return &http.Cookie{
		Name:     SessionCookieName,
		Value:    string(encoded),
		MaxAge:   0, // "session cookie", expiring when the browser is closed
		Path:     "/",
		Domain:   utils.StripPort(domain),
		Secure:   !s.Instance.Dev,
		HttpOnly: true,
	}, nil
}

// DeleteOthers will remove all sessions except the one given in parameter.
func DeleteOthers(i *instance.Instance, selfSessionID string) error {
	var sessions []*Session
	err := couchdb.ForeachDocs(i, consts.Sessions, func(_ string, data json.RawMessage) error {
		var s Session
		if err := json.Unmarshal(data, &s); err != nil {
			return err
		}
		sessions = append(sessions, &s)
		return nil
	})
	if err != nil {
		return err
	}
	for _, s := range sessions {
		if s.ID() != selfSessionID {
			s.Delete(i)
		}
	}
	return nil
}

// cookieMACConfig returns the options to authenticate the session cookie.
//
// We rely on a MACed cookie value, without additional encryption of the
// message since it should not contain critical private informations and is
// protected by HTTPs (secure cookie).
//
// About MaxLength, for a session of size 100 bytes
//
//       8 bytes time
//   +  32 bytes HMAC-SHA256
//   + 100 bytes session
//   + base64 encoding (4*n/3)
//   < 200 bytes
//
// 256 bytes should be sufficient enough to support any type of session.
//
var cookieAppMACConfig = crypto.MACConfig{
	Name:   SessionCookieName,
	MaxAge: SessionMaxAge,
	MaxLen: 256,
}

var cookieSessionMACConfig = crypto.MACConfig{
	Name:   SessionCookieName,
	MaxLen: 256,
}
<|endoftext|>


<commit_before>package slinga

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"google.golang.org/grpc/grpclog"
	yaml "gopkg.in/yaml.v2"
	"k8s.io/helm/pkg/helm"
	"k8s.io/helm/pkg/helm/portforwarder"
	"k8s.io/helm/pkg/kube"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	internalversioncore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/labels"
	"strings"

	"errors"
)

// HelmCodeExecutor is an executor that uses Helm for deployment of apps on kubernetes
type HelmCodeExecutor struct {
	Code     *Code
	Cluster  *Cluster
	Key      string
	Metadata map[string]string
	Params   interface{}
}

// NewHelmCodeExecutor constructs HelmCodeExecutor from given *Code
func NewHelmCodeExecutor(code *Code, key string, codeMetadata map[string]string, codeParams interface{}, clusters map[string]*Cluster) (CodeExecutor, error) {
	// First of all, redirect Helm/grpc logging to our own debug stream
	// We don't want these messages to be printed to Stdout/Stderr
	grpclog.SetLogger(debug)

	if paramsMap, ok := codeParams.(map[interface{}]interface{}); ok {
		// todo: should we check key existence first?
		if clusterName, ok := paramsMap["cluster"].(string); !ok {
			return nil, errors.New("Cluster param should be defined")
		} else if cluster, ok := clusters[clusterName]; ok {
			exec := HelmCodeExecutor{Code: code, Cluster: cluster, Key: key, Metadata: codeMetadata, Params: codeParams}
			err := exec.setupTillerConnection()
			if err != nil {
				return nil, err
			}
			return exec, nil
		} else {
			return nil, errors.New("Specified cluster is undefined")
		}
	}
	return nil, errors.New("Can't parse codeParams")
}

func (exec *HelmCodeExecutor) newKubeClient() (*restclient.Config, *internalclientset.Clientset, error) {
	kubeContext := exec.Cluster.Metadata.KubeContext
	config, err := kube.GetConfig(kubeContext).ClientConfig()
	if err != nil {
		return nil, nil, fmt.Errorf("Could not get kubernetes config for context '%s': %s", kubeContext, err)
	}
	client, err := internalclientset.NewForConfig(config)
	if err != nil {
		return nil, nil, fmt.Errorf("Could not get kubernetes client: %s", err)
	}
	return config, client, nil
}

func (exec *HelmCodeExecutor) setupTillerConnection() error {
	if exec.Cluster.Metadata.tillerHost != "" {
		return nil
	}

	config, client, err := exec.newKubeClient()
	if err != nil {
		return err
	}

	tillerNamespace := exec.Cluster.Metadata.TillerNamespace
	if tillerNamespace == "" {
		tillerNamespace = "kube-system"
	}
	tunnel, err := portforwarder.New(tillerNamespace, client, config)
	if err != nil {
		return err
	}

	exec.Cluster.Metadata.tillerHost = fmt.Sprintf("localhost:%d", tunnel.Local)

	debug.WithFields(log.Fields{
		"port": tunnel.Local,
	}).Info("Created k8s tunnel using local port")

	return nil
}

func (exec *HelmCodeExecutor) newHelmClient() *helm.Client {
	return helm.NewClient(helm.Host(exec.Cluster.Metadata.tillerHost))
}

func findHelmRelease(helmClient *helm.Client, name string) (bool, error) {
	resp, err := helmClient.ListReleases()
	if err != nil {
		return false, nil
	}

	for _, rel := range resp.Releases {
		if rel.Name == name {
			return true, nil
		}
	}

	return false, nil
}

func releaseName(key string) string {
	return strings.ToLower(EscapeName(key))
}

// Install for HelmCodeExecutor runs "helm install" for the corresponding helm chart
func (exec HelmCodeExecutor) Install() error {
	releaseName := releaseName(exec.Key)

	chartName, ok := exec.Metadata["chartName"]
	if !ok {
		return errors.New("Chart name is undefined")
	}

	helmClient := exec.newHelmClient()

	exists, err := findHelmRelease(helmClient, releaseName)
	if err != nil {
		debug.WithFields(log.Fields{
			"releaseName": releaseName,
			"error":       err,
		}).Fatal("Error while looking for release")
	}

	if exists {
		// If a release already exists, let's just go ahead and update it
		debug.WithFields(log.Fields{
			"releaseName": releaseName,
		}).Info("Found that release already exists. Calling an update on it")
		return exec.Update()

		/*
			debug.WithFields(log.Fields{
				"releaseName": releaseName,
			}).Fatal("Release already exists")
		*/
	}

	chartPath := GetAptomiPolicyDir() + "/charts/" + chartName + ".tgz"

	vals, err := yaml.Marshal(exec.Params)
	if err != nil {
		return err
	}

	debug.WithFields(log.Fields{
		"release": releaseName,
		"chart":   chartName,
		"path":    chartPath,
		"params":  string(vals),
	}).Info("Installing Helm release")

	_, err = helmClient.InstallRelease(chartPath, "demo", helm.ReleaseName(releaseName), helm.ValueOverrides(vals), helm.InstallReuseName(true))
	if err != nil {
		return err
	}
	return nil
}

// Update for HelmCodeExecutor runs "helm update" for the corresponding helm chart
func (exec HelmCodeExecutor) Update() error {
	releaseName := releaseName(exec.Key)

	chartName := exec.Metadata["chartName"]

	helmClient := exec.newHelmClient()

	chartPath := GetAptomiPolicyDir() + "/charts/" + chartName + ".tgz"

	vals, err := yaml.Marshal(exec.Params)
	if err != nil {
		return err
	}

	debug.WithFields(log.Fields{
		"release": releaseName,
		"chart":   chartName,
		"path":    chartPath,
		"params":  string(vals),
	}).Info("Updating Helm release")

	_, err = helmClient.UpdateRelease(releaseName, chartPath, helm.UpdateValueOverrides(vals))
	if err != nil {
		return err
	}

	return nil
}

// Destroy for HelmCodeExecutor runs "helm delete" for the corresponding helm chart
func (exec HelmCodeExecutor) Destroy() error {
	releaseName := releaseName(exec.Key)

	helmClient := exec.newHelmClient()

	debug.WithFields(log.Fields{
		"release": releaseName,
	}).Info("Deleting Helm release")

	if _, err := helmClient.DeleteRelease(releaseName, helm.DeletePurge(true)); err != nil {
		return err
	}

	return nil
}

// Endpoints returns map from port type to url for all services of the current chart
func (exec HelmCodeExecutor) Endpoints() (map[string]string, error) {
	_, clientset, err := exec.newKubeClient()
	if err != nil {
		return nil, err
	}

	client := clientset.Core()

	releaseName := releaseName(exec.Key)
	chartName := exec.Metadata["chartName"]

	selector := labels.Set{"release": releaseName, "chart": chartName}.AsSelector()
	options := api.ListOptions{LabelSelector: selector}
	services, err := client.Services(exec.Cluster.Metadata.Namespace).List(options)
	if err != nil {
		return nil, err
	}
	endpoints := make(map[string]string)

	kubeHost, err := exec.getKubeExternalAddress(client)
	if err != nil {
		return nil, err
	}

	for _, service := range services.Items {
		if service.Spec.Type == "NodePort" {
			for _, port := range service.Spec.Ports {
				sURL := fmt.Sprintf("%s:%d", kubeHost, port.NodePort)

				// todo(slukjanov): could we somehow detect real schema? I think no :(
				if port.Name == "webui" || port.Name == "ui" || port.Name == "rest" {
					sURL = "http://" + sURL
				}

				endpoints[port.Name] = sURL
			}
		}
	}
	return endpoints, nil

	return nil, nil
}

func (exec HelmCodeExecutor) getKubeExternalAddress(client internalversioncore.CoreInterface) (string, error) {
	if exec.Cluster.Metadata.kubeExternalAddress != "" {
		return exec.Cluster.Metadata.kubeExternalAddress, nil
	}

	nodes, err := client.Nodes().List(api.ListOptions{})
	if err != nil {
		return "", err
	}
	if len(nodes.Items) == 0 {
		return "", errors.New("K8s nodes list if empty, fail")
	}

	returnFirst := func(addrType api.NodeAddressType) string {
		for _, node := range nodes.Items {
			for _, addr := range node.Status.Addresses {
				if addr.Type == addrType {
					return addr.Address
				}
			}
		}
		return ""
	}

	addr := returnFirst(api.NodeExternalIP)
	if addr == "" {
		addr = returnFirst(api.NodeLegacyHostIP)
	}
	if addr == "" {
		addr = returnFirst(api.NodeInternalIP)
	}
	if addr == "" {
		return "", errors.New("Couldn't find external IP for cluster")
	}

	exec.Cluster.Metadata.kubeExternalAddress = addr
	return addr, nil
}
<commit_msg>Remove unreacheable code<commit_after>package slinga

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"google.golang.org/grpc/grpclog"
	yaml "gopkg.in/yaml.v2"
	"k8s.io/helm/pkg/helm"
	"k8s.io/helm/pkg/helm/portforwarder"
	"k8s.io/helm/pkg/kube"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	internalversioncore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/labels"
	"strings"

	"errors"
)

// HelmCodeExecutor is an executor that uses Helm for deployment of apps on kubernetes
type HelmCodeExecutor struct {
	Code     *Code
	Cluster  *Cluster
	Key      string
	Metadata map[string]string
	Params   interface{}
}

// NewHelmCodeExecutor constructs HelmCodeExecutor from given *Code
func NewHelmCodeExecutor(code *Code, key string, codeMetadata map[string]string, codeParams interface{}, clusters map[string]*Cluster) (CodeExecutor, error) {
	// First of all, redirect Helm/grpc logging to our own debug stream
	// We don't want these messages to be printed to Stdout/Stderr
	grpclog.SetLogger(debug)

	if paramsMap, ok := codeParams.(map[interface{}]interface{}); ok {
		// todo: should we check key existence first?
		if clusterName, ok := paramsMap["cluster"].(string); !ok {
			return nil, errors.New("Cluster param should be defined")
		} else if cluster, ok := clusters[clusterName]; ok {
			exec := HelmCodeExecutor{Code: code, Cluster: cluster, Key: key, Metadata: codeMetadata, Params: codeParams}
			err := exec.setupTillerConnection()
			if err != nil {
				return nil, err
			}
			return exec, nil
		} else {
			return nil, errors.New("Specified cluster is undefined")
		}
	}
	return nil, errors.New("Can't parse codeParams")
}

func (exec *HelmCodeExecutor) newKubeClient() (*restclient.Config, *internalclientset.Clientset, error) {
	kubeContext := exec.Cluster.Metadata.KubeContext
	config, err := kube.GetConfig(kubeContext).ClientConfig()
	if err != nil {
		return nil, nil, fmt.Errorf("Could not get kubernetes config for context '%s': %s", kubeContext, err)
	}
	client, err := internalclientset.NewForConfig(config)
	if err != nil {
		return nil, nil, fmt.Errorf("Could not get kubernetes client: %s", err)
	}
	return config, client, nil
}

func (exec *HelmCodeExecutor) setupTillerConnection() error {
	if exec.Cluster.Metadata.tillerHost != "" {
		return nil
	}

	config, client, err := exec.newKubeClient()
	if err != nil {
		return err
	}

	tillerNamespace := exec.Cluster.Metadata.TillerNamespace
	if tillerNamespace == "" {
		tillerNamespace = "kube-system"
	}
	tunnel, err := portforwarder.New(tillerNamespace, client, config)
	if err != nil {
		return err
	}

	exec.Cluster.Metadata.tillerHost = fmt.Sprintf("localhost:%d", tunnel.Local)

	debug.WithFields(log.Fields{
		"port": tunnel.Local,
	}).Info("Created k8s tunnel using local port")

	return nil
}

func (exec *HelmCodeExecutor) newHelmClient() *helm.Client {
	return helm.NewClient(helm.Host(exec.Cluster.Metadata.tillerHost))
}

func findHelmRelease(helmClient *helm.Client, name string) (bool, error) {
	resp, err := helmClient.ListReleases()
	if err != nil {
		return false, nil
	}

	for _, rel := range resp.Releases {
		if rel.Name == name {
			return true, nil
		}
	}

	return false, nil
}

func releaseName(key string) string {
	return strings.ToLower(EscapeName(key))
}

// Install for HelmCodeExecutor runs "helm install" for the corresponding helm chart
func (exec HelmCodeExecutor) Install() error {
	releaseName := releaseName(exec.Key)

	chartName, ok := exec.Metadata["chartName"]
	if !ok {
		return errors.New("Chart name is undefined")
	}

	helmClient := exec.newHelmClient()

	exists, err := findHelmRelease(helmClient, releaseName)
	if err != nil {
		debug.WithFields(log.Fields{
			"releaseName": releaseName,
			"error":       err,
		}).Fatal("Error while looking for release")
	}

	if exists {
		// If a release already exists, let's just go ahead and update it
		debug.WithFields(log.Fields{
			"releaseName": releaseName,
		}).Info("Found that release already exists. Calling an update on it")
		return exec.Update()

		/*
			debug.WithFields(log.Fields{
				"releaseName": releaseName,
			}).Fatal("Release already exists")
		*/
	}

	chartPath := GetAptomiPolicyDir() + "/charts/" + chartName + ".tgz"

	vals, err := yaml.Marshal(exec.Params)
	if err != nil {
		return err
	}

	debug.WithFields(log.Fields{
		"release": releaseName,
		"chart":   chartName,
		"path":    chartPath,
		"params":  string(vals),
	}).Info("Installing Helm release")

	_, err = helmClient.InstallRelease(chartPath, "demo", helm.ReleaseName(releaseName), helm.ValueOverrides(vals), helm.InstallReuseName(true))
	if err != nil {
		return err
	}
	return nil
}

// Update for HelmCodeExecutor runs "helm update" for the corresponding helm chart
func (exec HelmCodeExecutor) Update() error {
	releaseName := releaseName(exec.Key)

	chartName := exec.Metadata["chartName"]

	helmClient := exec.newHelmClient()

	chartPath := GetAptomiPolicyDir() + "/charts/" + chartName + ".tgz"

	vals, err := yaml.Marshal(exec.Params)
	if err != nil {
		return err
	}

	debug.WithFields(log.Fields{
		"release": releaseName,
		"chart":   chartName,
		"path":    chartPath,
		"params":  string(vals),
	}).Info("Updating Helm release")

	_, err = helmClient.UpdateRelease(releaseName, chartPath, helm.UpdateValueOverrides(vals))
	if err != nil {
		return err
	}

	return nil
}

// Destroy for HelmCodeExecutor runs "helm delete" for the corresponding helm chart
func (exec HelmCodeExecutor) Destroy() error {
	releaseName := releaseName(exec.Key)

	helmClient := exec.newHelmClient()

	debug.WithFields(log.Fields{
		"release": releaseName,
	}).Info("Deleting Helm release")

	if _, err := helmClient.DeleteRelease(releaseName, helm.DeletePurge(true)); err != nil {
		return err
	}

	return nil
}

// Endpoints returns map from port type to url for all services of the current chart
func (exec HelmCodeExecutor) Endpoints() (map[string]string, error) {
	_, clientset, err := exec.newKubeClient()
	if err != nil {
		return nil, err
	}

	client := clientset.Core()

	releaseName := releaseName(exec.Key)
	chartName := exec.Metadata["chartName"]

	selector := labels.Set{"release": releaseName, "chart": chartName}.AsSelector()
	options := api.ListOptions{LabelSelector: selector}
	services, err := client.Services(exec.Cluster.Metadata.Namespace).List(options)
	if err != nil {
		return nil, err
	}
	endpoints := make(map[string]string)

	kubeHost, err := exec.getKubeExternalAddress(client)
	if err != nil {
		return nil, err
	}

	for _, service := range services.Items {
		if service.Spec.Type == "NodePort" {
			for _, port := range service.Spec.Ports {
				sURL := fmt.Sprintf("%s:%d", kubeHost, port.NodePort)

				// todo(slukjanov): could we somehow detect real schema? I think no :(
				if port.Name == "webui" || port.Name == "ui" || port.Name == "rest" {
					sURL = "http://" + sURL
				}

				endpoints[port.Name] = sURL
			}
		}
	}
	return endpoints, nil
}

func (exec HelmCodeExecutor) getKubeExternalAddress(client internalversioncore.CoreInterface) (string, error) {
	if exec.Cluster.Metadata.kubeExternalAddress != "" {
		return exec.Cluster.Metadata.kubeExternalAddress, nil
	}

	nodes, err := client.Nodes().List(api.ListOptions{})
	if err != nil {
		return "", err
	}
	if len(nodes.Items) == 0 {
		return "", errors.New("K8s nodes list if empty, fail")
	}

	returnFirst := func(addrType api.NodeAddressType) string {
		for _, node := range nodes.Items {
			for _, addr := range node.Status.Addresses {
				if addr.Type == addrType {
					return addr.Address
				}
			}
		}
		return ""
	}

	addr := returnFirst(api.NodeExternalIP)
	if addr == "" {
		addr = returnFirst(api.NodeLegacyHostIP)
	}
	if addr == "" {
		addr = returnFirst(api.NodeInternalIP)
	}
	if addr == "" {
		return "", errors.New("Couldn't find external IP for cluster")
	}

	exec.Cluster.Metadata.kubeExternalAddress = addr
	return addr, nil
}
<|endoftext|>


<commit_before>package tarlog

import (
	"io"
	"sync"

	"github.com/sirupsen/logrus"
	"github.com/vbatts/tar-split/archive/tar"
)

type tarLogger struct {
	writer     *io.PipeWriter
	closeMutex *sync.Mutex
	stateMutex *sync.Mutex
	closed     bool
}

// NewLogger returns a writer that, when a tar archive is written to it, calls
// `logger` for each file header it encounters in the archive.
func NewLogger(logger func(*tar.Header)) (io.WriteCloser, error) {
	reader, writer := io.Pipe()
	t := &tarLogger{
		writer:     writer,
		closeMutex: new(sync.Mutex),
		stateMutex: new(sync.Mutex),
		closed:     false,
	}
	tr := tar.NewReader(reader)
	tr.RawAccounting = true
	t.closeMutex.Lock()
	go func() {
		hdr, err := tr.Next()
		for err == nil {
			logger(hdr)
			hdr, err = tr.Next()

		}
		// Make sure to avoid writes after the reader has been closed.
		t.stateMutex.Lock()
		t.closed = true
		if err := reader.Close(); err != nil {
			logrus.Errorf("error closing tarlogger reader: %v", err)
		}
		t.stateMutex.Unlock()
		// Unblock the Close().
		t.closeMutex.Unlock()
	}()
	return t, nil
}

func (t *tarLogger) Write(b []byte) (int, error) {
	t.stateMutex.Lock()
	if t.closed {
		// We cannot use os.Pipe() as this alters the tar's digest. Using
		// io.Pipe() requires this workaround as it does not allow for writes
		// after close.
		t.stateMutex.Unlock()
		return len(b), nil
	}
	t.stateMutex.Unlock()
	return t.writer.Write(b)
}

func (t *tarLogger) Close() error {
	err := t.writer.Close()
	// Wait for the reader to finish.
	t.closeMutex.Lock()
	return err
}
<commit_msg>tarlogger: disable raw accouting<commit_after>package tarlog

import (
	"io"
	"sync"

	"github.com/sirupsen/logrus"
	"github.com/vbatts/tar-split/archive/tar"
)

type tarLogger struct {
	writer     *io.PipeWriter
	closeMutex *sync.Mutex
	stateMutex *sync.Mutex
	closed     bool
}

// NewLogger returns a writer that, when a tar archive is written to it, calls
// `logger` for each file header it encounters in the archive.
func NewLogger(logger func(*tar.Header)) (io.WriteCloser, error) {
	reader, writer := io.Pipe()
	t := &tarLogger{
		writer:     writer,
		closeMutex: new(sync.Mutex),
		stateMutex: new(sync.Mutex),
		closed:     false,
	}
	tr := tar.NewReader(reader)
	t.closeMutex.Lock()
	go func() {
		hdr, err := tr.Next()
		for err == nil {
			logger(hdr)
			hdr, err = tr.Next()

		}
		// Make sure to avoid writes after the reader has been closed.
		t.stateMutex.Lock()
		t.closed = true
		if err := reader.Close(); err != nil {
			logrus.Errorf("error closing tarlogger reader: %v", err)
		}
		t.stateMutex.Unlock()
		// Unblock the Close().
		t.closeMutex.Unlock()
	}()
	return t, nil
}

func (t *tarLogger) Write(b []byte) (int, error) {
	t.stateMutex.Lock()
	if t.closed {
		// We cannot use os.Pipe() as this alters the tar's digest. Using
		// io.Pipe() requires this workaround as it does not allow for writes
		// after close.
		t.stateMutex.Unlock()
		return len(b), nil
	}
	t.stateMutex.Unlock()
	return t.writer.Write(b)
}

func (t *tarLogger) Close() error {
	err := t.writer.Close()
	// Wait for the reader to finish.
	t.closeMutex.Lock()
	return err
}
<|endoftext|>


<commit_before>package xlsx

import (
	"encoding/xml"
	"github.com/plandem/ooxml"
	"github.com/plandem/xlsx/format"
	"github.com/plandem/xlsx/internal/ml"
	"github.com/plandem/xlsx/options"
	"github.com/plandem/xlsx/types"
)

type sheetReadStream struct {
	*sheetInfo
	stream     *ooxml.StreamFileReader
	rowReader  ooxml.StreamReaderIterator
	mergedRows map[int]*Row
	currentRow *ml.Row
}

var _ Sheet = (*sheetReadStream)(nil)

func (s *sheetReadStream) Cell(colIndex, rowIndex int) *Cell {
	var data *ml.Cell

	colIndex, rowIndex, _ = s.mergedCells.Resolve(colIndex, rowIndex)
	row := s.Row(rowIndex)
	data = row.ml.Cells[colIndex]

	if data == nil {
		data = &ml.Cell{
			Ref: types.CellRefFromIndexes(colIndex, rowIndex),
		}
	}

	return &Cell{ml: data, sheet: s.sheetInfo}
}

func (s *sheetReadStream) CellByRef(cellRef types.CellRef) *Cell {
	cid, rid := cellRef.ToIndexes()
	return s.Cell(cid, rid)
}

func (s *sheetReadStream) Row(index int) *Row {
	if s.rowReader == nil {
		return nil
	}

	//if index from cached merged rows, then use it
	if row, ok := s.mergedRows[index]; ok {
		return row
	}

	//skip rows till required index
	indexRef := index + 1
	var data *ml.Row

	for {
		if s.currentRow != nil {
			if indexRef == s.currentRow.Ref {
				data = s.currentRow
				break
			}

			if indexRef < s.currentRow.Ref {
				break
			}
		}

		if !s.rowReader(s.nextRow) {
			break
		}
	}

	//looks like there is no any data anymore, return empty row
	if data == nil {
		data = s.emptyDataRow(indexRef)
	}

	return &Row{
		data,
		newRange(s, 0, len(data.Cells)-1, index, index),
	}
}

func (s *sheetReadStream) nextRow(decoder *xml.Decoder, start *xml.StartElement) bool {
	if start != nil && start.Name.Local == "row" {
		row := &ml.Row{}
		_ = decoder.DecodeElement(row, start)

		//expand row dimension to required width
		width, _ := s.Dimension()
		cells := make([]*ml.Cell, width)
		for _, c := range row.Cells {
			//add cell info
			if !isCellEmpty(c) {
				iCellCol, _ := c.Ref.ToIndexes()
				cells[iCellCol] = c
			}
		}

		row.Cells = cells
		s.currentRow = row
		return true
	}

	s.currentRow = nil
	return false
}

func (s *sheetReadStream) Rows() RowIterator {
	return newRowIterator(s)
}

//Close frees allocated by sheet resources
func (s *sheetReadStream) Close() {
	_ = s.stream.Close()
}

func (s *sheetReadStream) emptyDataRow(indexRef int) *ml.Row {
	width, _ := s.Dimension()
	return &ml.Row{
		Ref:   indexRef,
		Cells: make([]*ml.Cell, width),
	}
}

//afterOpen loads worksheet data and initializes it if required
func (s *sheetReadStream) afterOpen() {

	multiPhase := (s.sheetMode & SheetModeMultiPhase) != 0
	conditionalsInited := false

	if s.currentRow == nil {
		s.stream = s.file.ReadStream()

		//phase1
		for next, hasNext := s.stream.StartIterator(nil); hasNext; {
			hasNext = next(func(decoder *xml.Decoder, start *xml.StartElement) bool {
				switch start.Name.Local {
				case "dimension":
					s.ml.Dimension = &ml.SheetDimension{}
					_ = decoder.DecodeElement(s.ml.Dimension, start)
				case "hyperlinks":
					s.hyperlinks = newHyperlinks(s.sheetInfo)
				case "conditionalFormatting":
					if !conditionalsInited {
						//N.B.: conditionalFormatting is not nested, so we have to use flag to init once only
						s.conditionals = newConditionals(s.sheetInfo)
						s.conditionals.initIfRequired()
						conditionalsInited = true
					}
				case "mergeCells":
					s.mergedCells = newMergedCells(s.sheetInfo)
				case "mergeCell":
					cell := &ml.MergeCell{}
					_ = decoder.DecodeElement(cell, start)
					s.ml.MergeCells.Items = append(s.ml.MergeCells.Items, cell)
				case "row":
					if multiPhase {
						//skip row data, because 'mergeCell' is going after row data
						return true
					}

					//first row found, so stop pre-loading phase
					s.rowReader, _ = s.stream.StartIterator(start)
					return false
				}

				return true
			})
		}

		// multi phased?
		if multiPhase {
			//skip is func to skip any info till first row
			skip := func() {
				//close previous opened stream
				_ = s.stream.Close()

				//re-open stream again and cache skip any info till first row
				s.stream = s.file.ReadStream()
				for next, hasNext := s.stream.StartIterator(nil); hasNext; {
					hasNext = next(func(decoder *xml.Decoder, start *xml.StartElement) bool {
						if start.Name.Local == "row" {
							//first row found, so stop pre-loading phase
							s.rowReader, _ = s.stream.StartIterator(start)
							return false
						}

						return true
					})
				}
			}

			//phase2 - reset pointer to rows
			skip()

			//cache merged rows
			s.mergedRows = make(map[int]*Row)
			for rows := s.Rows(); rows.HasNext(); {
				rIdx, row := rows.Next()

				for _, mc := range s.ml.MergeCells.Items {
					if rIdx >= mc.Bounds.FromRow && rIdx <= mc.Bounds.ToRow {
						s.mergedRows[rIdx] = row
						break
					}
				}
			}

			//phase3 - reset pointer to rows and clear current row info
			skip()
			s.currentRow = nil
		}
	}
}

//not allowed methods for stream reading mode
func (s *sheetReadStream) Col(index int) *Col {
	panic(errorNotSupported)
}

func (s *sheetReadStream) Cols() ColIterator {
	panic(errorNotSupported)
}

func (s *sheetReadStream) InsertCol(index int) *Col {
	panic(errorNotSupported)
}

func (s *sheetReadStream) InsertRow(index int) *Row {
	panic(errorNotSupported)
}

func (s *sheetReadStream) DeleteRow(index int) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) DeleteCol(index int) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) SetDimension(cols, rows int) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) SetActive() {
	panic(errorNotSupported)
}

func (s *sheetReadStream) Set(o *options.SheetOptions) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) SetName(name string) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) MergeRows(fromIndex, toIndex int) error {
	panic(errorNotSupported)
}

func (s *sheetReadStream) MergeCols(fromIndex, toIndex int) error {
	panic(errorNotSupported)
}

func (s *sheetReadStream) SplitRows(fromIndex, toIndex int) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) SplitCols(fromIndex, toIndex int) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) AddConditional(conditional *format.ConditionalFormat, refs ...types.Ref) error {
	panic(errorNotSupported)
}

func (s *sheetReadStream) DeleteConditional(refs ...types.Ref) {
	panic(errorNotSupported)
}
<commit_msg>fix for stream read and cell with styles<commit_after>package xlsx

import (
	"encoding/xml"
	"github.com/plandem/ooxml"
	"github.com/plandem/xlsx/format"
	"github.com/plandem/xlsx/internal/ml"
	"github.com/plandem/xlsx/options"
	"github.com/plandem/xlsx/types"
)

type sheetReadStream struct {
	*sheetInfo
	stream     *ooxml.StreamFileReader
	rowReader  ooxml.StreamReaderIterator
	mergedRows map[int]*Row
	currentRow *ml.Row
}

var _ Sheet = (*sheetReadStream)(nil)

func (s *sheetReadStream) Cell(colIndex, rowIndex int) *Cell {
	var data *ml.Cell

	colIndex, rowIndex, _ = s.mergedCells.Resolve(colIndex, rowIndex)
	row := s.Row(rowIndex)
	data = row.ml.Cells[colIndex]

	if data == nil {
		data = &ml.Cell{
			Ref: types.CellRefFromIndexes(colIndex, rowIndex),
		}
	}

	return &Cell{ml: data, sheet: s.sheetInfo}
}

func (s *sheetReadStream) CellByRef(cellRef types.CellRef) *Cell {
	cid, rid := cellRef.ToIndexes()
	return s.Cell(cid, rid)
}

func (s *sheetReadStream) Row(index int) *Row {
	if s.rowReader == nil {
		return nil
	}

	//if index from cached merged rows, then use it
	if row, ok := s.mergedRows[index]; ok {
		return row
	}

	//skip rows till required index
	indexRef := index + 1
	var data *ml.Row

	for {
		if s.currentRow != nil {
			if indexRef == s.currentRow.Ref {
				data = s.currentRow
				break
			}

			if indexRef < s.currentRow.Ref {
				break
			}
		}

		if !s.rowReader(s.nextRow) {
			break
		}
	}

	//looks like there is no any data anymore, return empty row
	if data == nil {
		data = s.emptyDataRow(indexRef)
	}

	return &Row{
		data,
		newRange(s, 0, len(data.Cells)-1, index, index),
	}
}

func (s *sheetReadStream) nextRow(decoder *xml.Decoder, start *xml.StartElement) bool {
	if start != nil && start.Name.Local == "row" {
		row := &ml.Row{}
		_ = decoder.DecodeElement(row, start)

		//expand row dimension to required width
		width, _ := s.Dimension()
		cells := make([]*ml.Cell, width)
		for _, c := range row.Cells {
			//add cell info
			if !isCellEmpty(c) {
				iCellCol, _ := c.Ref.ToIndexes()
				cells[iCellCol] = c
			}
		}

		row.Cells = cells
		s.currentRow = row
		return true
	}

	s.currentRow = nil
	return false
}

func (s *sheetReadStream) Rows() RowIterator {
	return newRowIterator(s)
}

//Close frees allocated by sheet resources
func (s *sheetReadStream) Close() {
	_ = s.stream.Close()
}

func (s *sheetReadStream) emptyDataRow(indexRef int) *ml.Row {
	width, _ := s.Dimension()
	return &ml.Row{
		Ref:   indexRef,
		Cells: make([]*ml.Cell, width),
	}
}

//afterOpen loads worksheet data and initializes it if required
func (s *sheetReadStream) afterOpen() {
	//adds a styles for types
	s.workbook.doc.styleSheet.addTypedStylesIfRequired()

	multiPhase := (s.sheetMode & SheetModeMultiPhase) != 0
	conditionalsInited := false

	if s.currentRow == nil {
		s.stream = s.file.ReadStream()

		//phase1
		for next, hasNext := s.stream.StartIterator(nil); hasNext; {
			hasNext = next(func(decoder *xml.Decoder, start *xml.StartElement) bool {
				switch start.Name.Local {
				case "dimension":
					s.ml.Dimension = &ml.SheetDimension{}
					_ = decoder.DecodeElement(s.ml.Dimension, start)
				case "hyperlinks":
					s.hyperlinks = newHyperlinks(s.sheetInfo)
				case "conditionalFormatting":
					if !conditionalsInited {
						//N.B.: conditionalFormatting is not nested, so we have to use flag to init once only
						s.conditionals = newConditionals(s.sheetInfo)
						s.conditionals.initIfRequired()
						conditionalsInited = true
					}
				case "mergeCells":
					s.mergedCells = newMergedCells(s.sheetInfo)
				case "mergeCell":
					cell := &ml.MergeCell{}
					_ = decoder.DecodeElement(cell, start)
					s.ml.MergeCells.Items = append(s.ml.MergeCells.Items, cell)
				case "row":
					if multiPhase {
						//skip row data, because 'mergeCell' is going after row data
						return true
					}

					//first row found, so stop pre-loading phase
					s.rowReader, _ = s.stream.StartIterator(start)
					return false
				}

				return true
			})
		}

		// multi phased?
		if multiPhase {
			//skip is func to skip any info till first row
			skip := func() {
				//close previous opened stream
				_ = s.stream.Close()

				//re-open stream again and cache skip any info till first row
				s.stream = s.file.ReadStream()
				for next, hasNext := s.stream.StartIterator(nil); hasNext; {
					hasNext = next(func(decoder *xml.Decoder, start *xml.StartElement) bool {
						if start.Name.Local == "row" {
							//first row found, so stop pre-loading phase
							s.rowReader, _ = s.stream.StartIterator(start)
							return false
						}

						return true
					})
				}
			}

			//phase2 - reset pointer to rows
			skip()

			//cache merged rows
			s.mergedRows = make(map[int]*Row)
			for rows := s.Rows(); rows.HasNext(); {
				rIdx, row := rows.Next()

				for _, mc := range s.ml.MergeCells.Items {
					if rIdx >= mc.Bounds.FromRow && rIdx <= mc.Bounds.ToRow {
						s.mergedRows[rIdx] = row
						break
					}
				}
			}

			//phase3 - reset pointer to rows and clear current row info
			skip()
			s.currentRow = nil
		}
	}
}

//not allowed methods for stream reading mode
func (s *sheetReadStream) Col(index int) *Col {
	panic(errorNotSupported)
}

func (s *sheetReadStream) Cols() ColIterator {
	panic(errorNotSupported)
}

func (s *sheetReadStream) InsertCol(index int) *Col {
	panic(errorNotSupported)
}

func (s *sheetReadStream) InsertRow(index int) *Row {
	panic(errorNotSupported)
}

func (s *sheetReadStream) DeleteRow(index int) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) DeleteCol(index int) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) SetDimension(cols, rows int) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) SetActive() {
	panic(errorNotSupported)
}

func (s *sheetReadStream) Set(o *options.SheetOptions) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) SetName(name string) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) MergeRows(fromIndex, toIndex int) error {
	panic(errorNotSupported)
}

func (s *sheetReadStream) MergeCols(fromIndex, toIndex int) error {
	panic(errorNotSupported)
}

func (s *sheetReadStream) SplitRows(fromIndex, toIndex int) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) SplitCols(fromIndex, toIndex int) {
	panic(errorNotSupported)
}

func (s *sheetReadStream) AddConditional(conditional *format.ConditionalFormat, refs ...types.Ref) error {
	panic(errorNotSupported)
}

func (s *sheetReadStream) DeleteConditional(refs ...types.Ref) {
	panic(errorNotSupported)
}
<|endoftext|>