<commit_before>/*
Copyright 2019 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 flowcontrol

import (
	"context"
	"strconv"
	"time"

	"k8s.io/apiserver/pkg/server/mux"
	fq "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing"
	"k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/eventclock"
	fqs "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/queueset"
	"k8s.io/apiserver/pkg/util/flowcontrol/metrics"
	fcrequest "k8s.io/apiserver/pkg/util/flowcontrol/request"
	kubeinformers "k8s.io/client-go/informers"
	"k8s.io/klog/v2"
	"k8s.io/utils/clock"

	flowcontrol "k8s.io/api/flowcontrol/v1beta2"
	flowcontrolclient "k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta2"
)

// ConfigConsumerAsFieldManager is how the config consuminng
// controller appears in an ObjectMeta ManagedFieldsEntry.Manager
const ConfigConsumerAsFieldManager = "api-priority-and-fairness-config-consumer-v1"

// Interface defines how the API Priority and Fairness filter interacts with the underlying system.
type Interface interface {
	// Handle takes care of queuing and dispatching a request
	// characterized by the given digest.  The given `noteFn` will be
	// invoked with the results of request classification.
	// The given `workEstimator` is called, if at all, after noteFn.
	// `workEstimator` will be invoked only when the request
	//  is classified as non 'exempt'.
	// 'workEstimator', when invoked, must return the
	// work parameters for the request.
	// If the request is queued then `queueNoteFn` will be called twice,
	// first with `true` and then with `false`; otherwise
	// `queueNoteFn` will not be called at all.  If Handle decides
	// that the request should be executed then `execute()` will be
	// invoked once to execute the request; otherwise `execute()` will
	// not be invoked.
	// Handle() should never return while execute() is running, even if
	// ctx is cancelled or times out.
	Handle(ctx context.Context,
		requestDigest RequestDigest,
		noteFn func(fs *flowcontrol.FlowSchema, pl *flowcontrol.PriorityLevelConfiguration, flowDistinguisher string),
		workEstimator func() fcrequest.WorkEstimate,
		queueNoteFn fq.QueueNoteFn,
		execFn func(),
	)

	// MaintainObservations is a helper for maintaining statistics.
	MaintainObservations(stopCh <-chan struct{})

	// Run monitors config objects from the main apiservers and causes
	// any needed changes to local behavior.  This method ceases
	// activity and returns after the given channel is closed.
	Run(stopCh <-chan struct{}) error

	// Install installs debugging endpoints to the web-server.
	Install(c *mux.PathRecorderMux)

	// WatchTracker provides the WatchTracker interface.
	WatchTracker
}

// This request filter implements https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/1040-priority-and-fairness/README.md

// New creates a new instance to implement API priority and fairness
func New(
	informerFactory kubeinformers.SharedInformerFactory,
	flowcontrolClient flowcontrolclient.FlowcontrolV1beta2Interface,
	serverConcurrencyLimit int,
	requestWaitLimit time.Duration,
) Interface {
	clk := eventclock.Real{}
	return NewTestable(TestableConfig{
		Name:                   "Controller",
		Clock:                  clk,
		AsFieldManager:         ConfigConsumerAsFieldManager,
		FoundToDangling:        func(found bool) bool { return !found },
		InformerFactory:        informerFactory,
		FlowcontrolClient:      flowcontrolClient,
		ServerConcurrencyLimit: serverConcurrencyLimit,
		RequestWaitLimit:       requestWaitLimit,
		ReqsObsPairGenerator:   metrics.PriorityLevelConcurrencyObserverPairGenerator,
		ExecSeatsObsGenerator:  metrics.PriorityLevelExecutionSeatsObserverGenerator,
		QueueSetFactory:        fqs.NewQueueSetFactory(clk),
	})
}

// TestableConfig carries the parameters to an implementation that is testable
type TestableConfig struct {
	// Name of the controller
	Name string

	// Clock to use in timing deliberate delays
	Clock clock.PassiveClock

	// AsFieldManager is the string to use in the metadata for
	// server-side apply.  Normally this is
	// `ConfigConsumerAsFieldManager`.  This is exposed as a parameter
	// so that a test of competing controllers can supply different
	// values.
	AsFieldManager string

	// FoundToDangling maps the boolean indicating whether a
	// FlowSchema's referenced PLC exists to the boolean indicating
	// that FlowSchema's status should indicate a dangling reference.
	// This is a parameter so that we can write tests of what happens
	// when servers disagree on that bit of Status.
	FoundToDangling func(bool) bool

	// InformerFactory to use in building the controller
	InformerFactory kubeinformers.SharedInformerFactory

	// FlowcontrolClient to use for manipulating config objects
	FlowcontrolClient flowcontrolclient.FlowcontrolV1beta2Interface

	// ServerConcurrencyLimit for the controller to enforce
	ServerConcurrencyLimit int

	// RequestWaitLimit configured on the server
	RequestWaitLimit time.Duration

	// ObsPairGenerator for metrics about requests
	ReqsObsPairGenerator metrics.RatioedChangeObserverPairGenerator

	// RatioedChangeObserverPairGenerator for metrics about seats occupied by all phases of execution
	ExecSeatsObsGenerator metrics.RatioedChangeObserverGenerator

	// QueueSetFactory for the queuing implementation
	QueueSetFactory fq.QueueSetFactory
}

// NewTestable is extra flexible to facilitate testing
func NewTestable(config TestableConfig) Interface {
	return newTestableController(config)
}

func (cfgCtlr *configController) Handle(ctx context.Context, requestDigest RequestDigest,
	noteFn func(fs *flowcontrol.FlowSchema, pl *flowcontrol.PriorityLevelConfiguration, flowDistinguisher string),
	workEstimator func() fcrequest.WorkEstimate,
	queueNoteFn fq.QueueNoteFn,
	execFn func()) {
	fs, pl, isExempt, req, startWaitingTime := cfgCtlr.startRequest(ctx, requestDigest, noteFn, workEstimator, queueNoteFn)
	queued := startWaitingTime != time.Time{}
	if req == nil {
		if queued {
			metrics.ObserveWaitingDuration(ctx, pl.Name, fs.Name, strconv.FormatBool(req != nil), time.Since(startWaitingTime))
		}
		klog.V(7).Infof("Handle(%#+v) => fsName=%q, distMethod=%#+v, plName=%q, isExempt=%v, reject", requestDigest, fs.Name, fs.Spec.DistinguisherMethod, pl.Name, isExempt)
		return
	}
	klog.V(7).Infof("Handle(%#+v) => fsName=%q, distMethod=%#+v, plName=%q, isExempt=%v, queued=%v", requestDigest, fs.Name, fs.Spec.DistinguisherMethod, pl.Name, isExempt, queued)
	var executed bool
	idle, panicking := true, true
	defer func() {
		klog.V(7).Infof("Handle(%#+v) => fsName=%q, distMethod=%#+v, plName=%q, isExempt=%v, queued=%v, Finish() => panicking=%v idle=%v",
			requestDigest, fs.Name, fs.Spec.DistinguisherMethod, pl.Name, isExempt, queued, panicking, idle)
		if idle {
			cfgCtlr.maybeReap(pl.Name)
		}
	}()
	idle = req.Finish(func() {
		if queued {
			metrics.ObserveWaitingDuration(ctx, pl.Name, fs.Name, strconv.FormatBool(req != nil), time.Since(startWaitingTime))
		}
		metrics.AddDispatch(ctx, pl.Name, fs.Name)
		executed = true
		startExecutionTime := time.Now()
		defer func() {
			metrics.ObserveExecutionDuration(ctx, pl.Name, fs.Name, time.Since(startExecutionTime))
		}()
		execFn()
	})
	if queued && !executed {
		metrics.ObserveWaitingDuration(ctx, pl.Name, fs.Name, strconv.FormatBool(req != nil), time.Since(startWaitingTime))
	}
	panicking = false
}
<commit_msg>For each call, log apf_execution_time<commit_after>/*
Copyright 2019 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 flowcontrol

import (
	"context"
	"strconv"
	"time"

	"k8s.io/apiserver/pkg/server/httplog"
	"k8s.io/apiserver/pkg/server/mux"
	fq "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing"
	"k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/eventclock"
	fqs "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/queueset"
	"k8s.io/apiserver/pkg/util/flowcontrol/metrics"
	fcrequest "k8s.io/apiserver/pkg/util/flowcontrol/request"
	kubeinformers "k8s.io/client-go/informers"
	"k8s.io/klog/v2"
	"k8s.io/utils/clock"

	flowcontrol "k8s.io/api/flowcontrol/v1beta2"
	flowcontrolclient "k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta2"
)

// ConfigConsumerAsFieldManager is how the config consuminng
// controller appears in an ObjectMeta ManagedFieldsEntry.Manager
const ConfigConsumerAsFieldManager = "api-priority-and-fairness-config-consumer-v1"

// Interface defines how the API Priority and Fairness filter interacts with the underlying system.
type Interface interface {
	// Handle takes care of queuing and dispatching a request
	// characterized by the given digest.  The given `noteFn` will be
	// invoked with the results of request classification.
	// The given `workEstimator` is called, if at all, after noteFn.
	// `workEstimator` will be invoked only when the request
	//  is classified as non 'exempt'.
	// 'workEstimator', when invoked, must return the
	// work parameters for the request.
	// If the request is queued then `queueNoteFn` will be called twice,
	// first with `true` and then with `false`; otherwise
	// `queueNoteFn` will not be called at all.  If Handle decides
	// that the request should be executed then `execute()` will be
	// invoked once to execute the request; otherwise `execute()` will
	// not be invoked.
	// Handle() should never return while execute() is running, even if
	// ctx is cancelled or times out.
	Handle(ctx context.Context,
		requestDigest RequestDigest,
		noteFn func(fs *flowcontrol.FlowSchema, pl *flowcontrol.PriorityLevelConfiguration, flowDistinguisher string),
		workEstimator func() fcrequest.WorkEstimate,
		queueNoteFn fq.QueueNoteFn,
		execFn func(),
	)

	// MaintainObservations is a helper for maintaining statistics.
	MaintainObservations(stopCh <-chan struct{})

	// Run monitors config objects from the main apiservers and causes
	// any needed changes to local behavior.  This method ceases
	// activity and returns after the given channel is closed.
	Run(stopCh <-chan struct{}) error

	// Install installs debugging endpoints to the web-server.
	Install(c *mux.PathRecorderMux)

	// WatchTracker provides the WatchTracker interface.
	WatchTracker
}

// This request filter implements https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/1040-priority-and-fairness/README.md

// New creates a new instance to implement API priority and fairness
func New(
	informerFactory kubeinformers.SharedInformerFactory,
	flowcontrolClient flowcontrolclient.FlowcontrolV1beta2Interface,
	serverConcurrencyLimit int,
	requestWaitLimit time.Duration,
) Interface {
	clk := eventclock.Real{}
	return NewTestable(TestableConfig{
		Name:                   "Controller",
		Clock:                  clk,
		AsFieldManager:         ConfigConsumerAsFieldManager,
		FoundToDangling:        func(found bool) bool { return !found },
		InformerFactory:        informerFactory,
		FlowcontrolClient:      flowcontrolClient,
		ServerConcurrencyLimit: serverConcurrencyLimit,
		RequestWaitLimit:       requestWaitLimit,
		ReqsObsPairGenerator:   metrics.PriorityLevelConcurrencyObserverPairGenerator,
		ExecSeatsObsGenerator:  metrics.PriorityLevelExecutionSeatsObserverGenerator,
		QueueSetFactory:        fqs.NewQueueSetFactory(clk),
	})
}

// TestableConfig carries the parameters to an implementation that is testable
type TestableConfig struct {
	// Name of the controller
	Name string

	// Clock to use in timing deliberate delays
	Clock clock.PassiveClock

	// AsFieldManager is the string to use in the metadata for
	// server-side apply.  Normally this is
	// `ConfigConsumerAsFieldManager`.  This is exposed as a parameter
	// so that a test of competing controllers can supply different
	// values.
	AsFieldManager string

	// FoundToDangling maps the boolean indicating whether a
	// FlowSchema's referenced PLC exists to the boolean indicating
	// that FlowSchema's status should indicate a dangling reference.
	// This is a parameter so that we can write tests of what happens
	// when servers disagree on that bit of Status.
	FoundToDangling func(bool) bool

	// InformerFactory to use in building the controller
	InformerFactory kubeinformers.SharedInformerFactory

	// FlowcontrolClient to use for manipulating config objects
	FlowcontrolClient flowcontrolclient.FlowcontrolV1beta2Interface

	// ServerConcurrencyLimit for the controller to enforce
	ServerConcurrencyLimit int

	// RequestWaitLimit configured on the server
	RequestWaitLimit time.Duration

	// ObsPairGenerator for metrics about requests
	ReqsObsPairGenerator metrics.RatioedChangeObserverPairGenerator

	// RatioedChangeObserverPairGenerator for metrics about seats occupied by all phases of execution
	ExecSeatsObsGenerator metrics.RatioedChangeObserverGenerator

	// QueueSetFactory for the queuing implementation
	QueueSetFactory fq.QueueSetFactory
}

// NewTestable is extra flexible to facilitate testing
func NewTestable(config TestableConfig) Interface {
	return newTestableController(config)
}

func (cfgCtlr *configController) Handle(ctx context.Context, requestDigest RequestDigest,
	noteFn func(fs *flowcontrol.FlowSchema, pl *flowcontrol.PriorityLevelConfiguration, flowDistinguisher string),
	workEstimator func() fcrequest.WorkEstimate,
	queueNoteFn fq.QueueNoteFn,
	execFn func()) {
	fs, pl, isExempt, req, startWaitingTime := cfgCtlr.startRequest(ctx, requestDigest, noteFn, workEstimator, queueNoteFn)
	queued := startWaitingTime != time.Time{}
	if req == nil {
		if queued {
			metrics.ObserveWaitingDuration(ctx, pl.Name, fs.Name, strconv.FormatBool(req != nil), time.Since(startWaitingTime))
		}
		klog.V(7).Infof("Handle(%#+v) => fsName=%q, distMethod=%#+v, plName=%q, isExempt=%v, reject", requestDigest, fs.Name, fs.Spec.DistinguisherMethod, pl.Name, isExempt)
		return
	}
	klog.V(7).Infof("Handle(%#+v) => fsName=%q, distMethod=%#+v, plName=%q, isExempt=%v, queued=%v", requestDigest, fs.Name, fs.Spec.DistinguisherMethod, pl.Name, isExempt, queued)
	var executed bool
	idle, panicking := true, true
	defer func() {
		klog.V(7).Infof("Handle(%#+v) => fsName=%q, distMethod=%#+v, plName=%q, isExempt=%v, queued=%v, Finish() => panicking=%v idle=%v",
			requestDigest, fs.Name, fs.Spec.DistinguisherMethod, pl.Name, isExempt, queued, panicking, idle)
		if idle {
			cfgCtlr.maybeReap(pl.Name)
		}
	}()
	idle = req.Finish(func() {
		if queued {
			metrics.ObserveWaitingDuration(ctx, pl.Name, fs.Name, strconv.FormatBool(req != nil), time.Since(startWaitingTime))
		}
		metrics.AddDispatch(ctx, pl.Name, fs.Name)
		executed = true
		startExecutionTime := time.Now()
		defer func() {
			executionTime := time.Since(startExecutionTime)
			httplog.AddKeyValue(ctx, "apf_execution_time", executionTime)
			metrics.ObserveExecutionDuration(ctx, pl.Name, fs.Name, executionTime)
		}()
		execFn()
	})
	if queued && !executed {
		metrics.ObserveWaitingDuration(ctx, pl.Name, fs.Name, strconv.FormatBool(req != nil), time.Since(startWaitingTime))
	}
	panicking = false
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"io"
	"sort"
	"text/tabwriter"
	"time"

	oldcmds "github.com/ipfs/go-ipfs/commands"

	cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds"
	cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
)

const (
	verboseOptionName = "v"
)

var ActiveReqsCmd = &cmds.Command{
	Helptext: cmdkit.HelpText{
		Tagline: "List commands run on this IPFS node.",
		ShortDescription: `
Lists running and recently run commands.
`,
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		ctx := env.(*oldcmds.Context)
		return cmds.EmitOnce(res, ctx.ReqLog.Report())
	},
	Options: []cmdkit.Option{
		cmdkit.BoolOption("verbose", verboseOptionName, "Print extra information."),
	},
	Subcommands: map[string]*cmds.Command{
		"clear":    clearInactiveCmd,
		"set-time": setRequestClearCmd,
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *[]*cmds.ReqLogEntry) error {
			verbose, _ := req.Options[verboseOptionName].(bool)

			tw := tabwriter.NewWriter(w, 4, 4, 2, ' ', 0)
			if verbose {
				fmt.Fprint(tw, "ID\t")
			}
			fmt.Fprint(tw, "Command\t")
			if verbose {
				fmt.Fprint(tw, "Arguments\tOptions\t")
			}
			fmt.Fprintln(tw, "Active\tStartTime\tRunTime")

			for _, req := range *out {
				if verbose {
					fmt.Fprintf(tw, "%d\t", req.ID)
				}
				fmt.Fprintf(tw, "%s\t", req.Command)
				if verbose {
					fmt.Fprintf(tw, "%v\t[", req.Args)
					var keys []string
					for k := range req.Options {
						keys = append(keys, k)
					}
					sort.Strings(keys)

					for _, k := range keys {
						fmt.Fprintf(tw, "%s=%v,", k, req.Options[k])
					}
					fmt.Fprintf(tw, "]\t")
				}

				var live time.Duration
				if req.Active {
					live = time.Since(req.StartTime)
				} else {
					live = req.EndTime.Sub(req.StartTime)
				}
				t := req.StartTime.Format(time.Stamp)
				fmt.Fprintf(tw, "%t\t%s\t%s\n", req.Active, t, live)
			}
			tw.Flush()

			return nil
		}),
	},
	Type: []*cmds.ReqLogEntry{},
}

var clearInactiveCmd = &cmds.Command{
	Helptext: cmdkit.HelpText{
		Tagline: "Clear inactive requests from the log.",
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		ctx := env.(*oldcmds.Context)
		ctx.ReqLog.ClearInactive()
		return nil
	},
}

var setRequestClearCmd = &cmds.Command{
	Helptext: cmdkit.HelpText{
		Tagline: "Set how long to keep inactive requests in the log.",
	},
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("time", true, false, "Time to keep inactive requests in log."),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		tval, err := time.ParseDuration(req.Arguments[0])
		if err != nil {
			return err
		}
		ctx := env.(*oldcmds.Context)
		ctx.ReqLog.SetKeepTime(tval)

		return nil
	},
}
<commit_msg>commands: fix verbose flag for the active commands command<commit_after>package commands

import (
	"fmt"
	"io"
	"sort"
	"text/tabwriter"
	"time"

	oldcmds "github.com/ipfs/go-ipfs/commands"

	cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds"
	cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
)

const (
	verboseOptionName = "verbose"
)

var ActiveReqsCmd = &cmds.Command{
	Helptext: cmdkit.HelpText{
		Tagline: "List commands run on this IPFS node.",
		ShortDescription: `
Lists running and recently run commands.
`,
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		ctx := env.(*oldcmds.Context)
		return cmds.EmitOnce(res, ctx.ReqLog.Report())
	},
	Options: []cmdkit.Option{
		cmdkit.BoolOption(verboseOptionName, "v", "Print extra information."),
	},
	Subcommands: map[string]*cmds.Command{
		"clear":    clearInactiveCmd,
		"set-time": setRequestClearCmd,
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *[]*cmds.ReqLogEntry) error {
			verbose, _ := req.Options[verboseOptionName].(bool)

			tw := tabwriter.NewWriter(w, 4, 4, 2, ' ', 0)
			if verbose {
				fmt.Fprint(tw, "ID\t")
			}
			fmt.Fprint(tw, "Command\t")
			if verbose {
				fmt.Fprint(tw, "Arguments\tOptions\t")
			}
			fmt.Fprintln(tw, "Active\tStartTime\tRunTime")

			for _, req := range *out {
				if verbose {
					fmt.Fprintf(tw, "%d\t", req.ID)
				}
				fmt.Fprintf(tw, "%s\t", req.Command)
				if verbose {
					fmt.Fprintf(tw, "%v\t[", req.Args)
					var keys []string
					for k := range req.Options {
						keys = append(keys, k)
					}
					sort.Strings(keys)

					for _, k := range keys {
						fmt.Fprintf(tw, "%s=%v,", k, req.Options[k])
					}
					fmt.Fprintf(tw, "]\t")
				}

				var live time.Duration
				if req.Active {
					live = time.Since(req.StartTime)
				} else {
					live = req.EndTime.Sub(req.StartTime)
				}
				t := req.StartTime.Format(time.Stamp)
				fmt.Fprintf(tw, "%t\t%s\t%s\n", req.Active, t, live)
			}
			tw.Flush()

			return nil
		}),
	},
	Type: []*cmds.ReqLogEntry{},
}

var clearInactiveCmd = &cmds.Command{
	Helptext: cmdkit.HelpText{
		Tagline: "Clear inactive requests from the log.",
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		ctx := env.(*oldcmds.Context)
		ctx.ReqLog.ClearInactive()
		return nil
	},
}

var setRequestClearCmd = &cmds.Command{
	Helptext: cmdkit.HelpText{
		Tagline: "Set how long to keep inactive requests in the log.",
	},
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("time", true, false, "Time to keep inactive requests in log."),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		tval, err := time.ParseDuration(req.Arguments[0])
		if err != nil {
			return err
		}
		ctx := env.(*oldcmds.Context)
		ctx.ReqLog.SetKeepTime(tval)

		return nil
	},
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"
	"log"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/config/configschema"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/tfdiags"
)

// Schemas is a container for various kinds of schema that Terraform needs
// during processing.
type Schemas struct {
	providers    map[string]*ProviderSchema
	provisioners map[string]*configschema.Block
}

// ProviderSchema returns the entire ProviderSchema object that was produced
// by the plugin for the given provider, or nil if no such schema is available.
//
// It's usually better to go use the more precise methods offered by type
// Schemas to handle this detail automatically.
func (ss *Schemas) ProviderSchema(typeName string) *ProviderSchema {
	if ss.providers == nil {
		return nil
	}
	return ss.providers[typeName]
}

// ProviderConfig returns the schema for the provider configuration of the
// given provider type, or nil if no such schema is available.
func (ss *Schemas) ProviderConfig(typeName string) *configschema.Block {
	ps := ss.ProviderSchema(typeName)
	if ps == nil {
		return nil
	}
	return ps.Provider
}

// ResourceTypeConfig returns the schema for the configuration of a given
// resource type belonging to a given provider type, or nil of no such
// schema is available.
//
// In many cases the provider type is inferrable from the resource type name,
// but this is not always true because users can override the provider for
// a resource using the "provider" meta-argument. Therefore it's important to
// always pass the correct provider name, even though it many cases it feels
// redundant.
func (ss *Schemas) ResourceTypeConfig(providerType string, resourceType string) *configschema.Block {
	ps := ss.ProviderSchema(providerType)
	if ps == nil || ps.ResourceTypes == nil {
		return nil
	}

	return ps.ResourceTypes[resourceType]
}

// DataSourceConfig returns the schema for the configuration of a given
// data source belonging to a given provider type, or nil of no such
// schema is available.
//
// In many cases the provider type is inferrable from the data source name,
// but this is not always true because users can override the provider for
// a resource using the "provider" meta-argument. Therefore it's important to
// always pass the correct provider name, even though it many cases it feels
// redundant.
func (ss *Schemas) DataSourceConfig(providerType string, dataSource string) *configschema.Block {
	ps := ss.ProviderSchema(providerType)
	if ps == nil || ps.DataSources == nil {
		return nil
	}

	return ps.DataSources[dataSource]
}

// ProvisionerConfig returns the schema for the configuration of a given
// provisioner, or nil of no such schema is available.
func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block {
	return ss.provisioners[name]
}

// LoadSchemas searches the given configuration and state (either of which may
// be nil) for constructs that have an associated schema, requests the
// necessary schemas from the given component factory (which may _not_ be nil),
// and returns a single object representing all of the necessary schemas.
//
// If an error is returned, it may be a wrapped tfdiags.Diagnostics describing
// errors across multiple separate objects. Errors here will usually indicate
// either misbehavior on the part of one of the providers or of the provider
// protocol itself. When returned with errors, the returned schemas object is
// still valid but may be incomplete.
func LoadSchemas(config *configs.Config, state *State, components contextComponentFactory) (*Schemas, error) {
	schemas := &Schemas{
		providers:    map[string]*ProviderSchema{},
		provisioners: map[string]*configschema.Block{},
	}
	var diags tfdiags.Diagnostics

	newDiags := loadProviderSchemas(schemas.providers, config, state, components)
	diags = diags.Append(newDiags)
	newDiags = loadProvisionerSchemas(schemas.provisioners, config, components)
	diags = diags.Append(newDiags)

	return schemas, diags.Err()
}

func loadProviderSchemas(schemas map[string]*ProviderSchema, config *configs.Config, state *State, components contextComponentFactory) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics

	ensure := func(typeName string) {
		if _, exists := schemas[typeName]; exists {
			return
		}

		log.Printf("[TRACE] LoadSchemas: retrieving schema for provider type %q", typeName)
		provider, err := components.ResourceProvider(typeName, "early/"+typeName)
		if err != nil {
			// We'll put a stub in the map so we won't re-attempt this on
			// future calls.
			schemas[typeName] = &ProviderSchema{}
			diags = diags.Append(
				fmt.Errorf("Failed to instantiate provider %q to obtain schema: %s", typeName, err),
			)
			return
		}
		defer func() {
			if closer, ok := provider.(ResourceProviderCloser); ok {
				closer.Close()
			}
		}()

		// FIXME: The provider interface is currently awkward in that it
		// requires us to tell the provider which resources types and data
		// sources we need. In future this will change to just return
		// everything available, but for now we'll fake that by fetching all
		// of the available names and then requesting them.
		resourceTypes := provider.Resources()
		dataSources := provider.DataSources()
		resourceTypeNames := make([]string, len(resourceTypes))
		for i, o := range resourceTypes {
			resourceTypeNames[i] = o.Name
		}
		dataSourceNames := make([]string, len(dataSources))
		for i, o := range dataSources {
			dataSourceNames[i] = o.Name
		}

		schema, err := provider.GetSchema(&ProviderSchemaRequest{
			ResourceTypes: resourceTypeNames,
			DataSources:   dataSourceNames,
		})
		if err != nil {
			// We'll put a stub in the map so we won't re-attempt this on
			// future calls.
			schemas[typeName] = &ProviderSchema{}
			diags = diags.Append(
				fmt.Errorf("Failed to retrieve schema from provider %q: %s", typeName, err),
			)
			return
		}

		schemas[typeName] = schema
	}

	if config != nil {
		for _, pc := range config.Module.ProviderConfigs {
			ensure(pc.Name)
		}
		for _, rc := range config.Module.ManagedResources {
			providerAddr := rc.ProviderConfigAddr()
			ensure(providerAddr.Type)
		}
		for _, rc := range config.Module.DataResources {
			providerAddr := rc.ProviderConfigAddr()
			ensure(providerAddr.Type)
		}

		// Must also visit our child modules, recursively.
		for _, cc := range config.Children {
			childDiags := loadProviderSchemas(schemas, cc, nil, components)
			diags = diags.Append(childDiags)
		}
	}

	if state != nil {
		for _, ms := range state.Modules {
			for rsKey, rs := range ms.Resources {
				providerAddrStr := rs.Provider
				providerAddr, addrDiags := addrs.ParseAbsProviderConfigStr(providerAddrStr)
				if addrDiags.HasErrors() {
					// Should happen only if someone has tampered manually with
					// the state, since we always write valid provider addrs.
					moduleAddrStr := normalizeModulePath(ms.Path).String()
					if moduleAddrStr == "" {
						moduleAddrStr = "the root module"
					}
					// For now this is a warning, since there are many existing
					// test fixtures that have invalid provider configurations.
					// There's a check deeper in Terraform that makes this a
					// failure when an empty/invalid provider string is present
					// in practice.
					log.Printf("[WARN] LoadSchemas: Resource %s in %s has invalid provider address %q in its state", rsKey, moduleAddrStr, providerAddrStr)
					diags = diags.Append(
						tfdiags.SimpleWarning(fmt.Sprintf("Resource %s in %s has invalid provider address %q in its state", rsKey, moduleAddrStr, providerAddrStr)),
					)
					continue
				}
				ensure(providerAddr.ProviderConfig.Type)
			}
		}
	}

	return diags
}

func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *configs.Config, components contextComponentFactory) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics

	ensure := func(name string) {
		if _, exists := schemas[name]; exists {
			return
		}

		log.Printf("[TRACE] LoadSchemas: retrieving schema for provisioner %q", name)
		provisioner, err := components.ResourceProvisioner(name, "early/"+name)
		if err != nil {
			// We'll put a stub in the map so we won't re-attempt this on
			// future calls.
			schemas[name] = &configschema.Block{}
			diags = diags.Append(
				fmt.Errorf("Failed to instantiate provisioner %q to obtain schema: %s", name, err),
			)
			return
		}
		defer func() {
			if closer, ok := provisioner.(ResourceProvisionerCloser); ok {
				closer.Close()
			}
		}()

		schema, err := provisioner.GetConfigSchema()
		if err != nil {
			// We'll put a stub in the map so we won't re-attempt this on
			// future calls.
			schemas[name] = &configschema.Block{}
			diags = diags.Append(
				fmt.Errorf("Failed to retrieve schema from provisioner %q: %s", name, err),
			)
			return
		}

		schemas[name] = schema
	}

	if config != nil {
		for _, rc := range config.Module.ManagedResources {
			for _, pc := range rc.Managed.Provisioners {
				ensure(pc.Type)
			}
		}
	}

	return diags
}

// ProviderSchema represents the schema for a provider's own configuration
// and the configuration for some or all of its resources and data sources.
//
// The completeness of this structure depends on how it was constructed.
// When constructed for a configuration, it will generally include only
// resource types and data sources used by that configuration.
type ProviderSchema struct {
	Provider      *configschema.Block
	ResourceTypes map[string]*configschema.Block
	DataSources   map[string]*configschema.Block
}

// ProviderSchemaRequest is used to describe to a ResourceProvider which
// aspects of schema are required, when calling the GetSchema method.
type ProviderSchemaRequest struct {
	ResourceTypes []string
	DataSources   []string
}
<commit_msg>core: LoadSchemas must detect provisioners in non-root modules<commit_after>package terraform

import (
	"fmt"
	"log"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/config/configschema"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/tfdiags"
)

// Schemas is a container for various kinds of schema that Terraform needs
// during processing.
type Schemas struct {
	providers    map[string]*ProviderSchema
	provisioners map[string]*configschema.Block
}

// ProviderSchema returns the entire ProviderSchema object that was produced
// by the plugin for the given provider, or nil if no such schema is available.
//
// It's usually better to go use the more precise methods offered by type
// Schemas to handle this detail automatically.
func (ss *Schemas) ProviderSchema(typeName string) *ProviderSchema {
	if ss.providers == nil {
		return nil
	}
	return ss.providers[typeName]
}

// ProviderConfig returns the schema for the provider configuration of the
// given provider type, or nil if no such schema is available.
func (ss *Schemas) ProviderConfig(typeName string) *configschema.Block {
	ps := ss.ProviderSchema(typeName)
	if ps == nil {
		return nil
	}
	return ps.Provider
}

// ResourceTypeConfig returns the schema for the configuration of a given
// resource type belonging to a given provider type, or nil of no such
// schema is available.
//
// In many cases the provider type is inferrable from the resource type name,
// but this is not always true because users can override the provider for
// a resource using the "provider" meta-argument. Therefore it's important to
// always pass the correct provider name, even though it many cases it feels
// redundant.
func (ss *Schemas) ResourceTypeConfig(providerType string, resourceType string) *configschema.Block {
	ps := ss.ProviderSchema(providerType)
	if ps == nil || ps.ResourceTypes == nil {
		return nil
	}

	return ps.ResourceTypes[resourceType]
}

// DataSourceConfig returns the schema for the configuration of a given
// data source belonging to a given provider type, or nil of no such
// schema is available.
//
// In many cases the provider type is inferrable from the data source name,
// but this is not always true because users can override the provider for
// a resource using the "provider" meta-argument. Therefore it's important to
// always pass the correct provider name, even though it many cases it feels
// redundant.
func (ss *Schemas) DataSourceConfig(providerType string, dataSource string) *configschema.Block {
	ps := ss.ProviderSchema(providerType)
	if ps == nil || ps.DataSources == nil {
		return nil
	}

	return ps.DataSources[dataSource]
}

// ProvisionerConfig returns the schema for the configuration of a given
// provisioner, or nil of no such schema is available.
func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block {
	return ss.provisioners[name]
}

// LoadSchemas searches the given configuration and state (either of which may
// be nil) for constructs that have an associated schema, requests the
// necessary schemas from the given component factory (which may _not_ be nil),
// and returns a single object representing all of the necessary schemas.
//
// If an error is returned, it may be a wrapped tfdiags.Diagnostics describing
// errors across multiple separate objects. Errors here will usually indicate
// either misbehavior on the part of one of the providers or of the provider
// protocol itself. When returned with errors, the returned schemas object is
// still valid but may be incomplete.
func LoadSchemas(config *configs.Config, state *State, components contextComponentFactory) (*Schemas, error) {
	schemas := &Schemas{
		providers:    map[string]*ProviderSchema{},
		provisioners: map[string]*configschema.Block{},
	}
	var diags tfdiags.Diagnostics

	newDiags := loadProviderSchemas(schemas.providers, config, state, components)
	diags = diags.Append(newDiags)
	newDiags = loadProvisionerSchemas(schemas.provisioners, config, components)
	diags = diags.Append(newDiags)

	return schemas, diags.Err()
}

func loadProviderSchemas(schemas map[string]*ProviderSchema, config *configs.Config, state *State, components contextComponentFactory) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics

	ensure := func(typeName string) {
		if _, exists := schemas[typeName]; exists {
			return
		}

		log.Printf("[TRACE] LoadSchemas: retrieving schema for provider type %q", typeName)
		provider, err := components.ResourceProvider(typeName, "early/"+typeName)
		if err != nil {
			// We'll put a stub in the map so we won't re-attempt this on
			// future calls.
			schemas[typeName] = &ProviderSchema{}
			diags = diags.Append(
				fmt.Errorf("Failed to instantiate provider %q to obtain schema: %s", typeName, err),
			)
			return
		}
		defer func() {
			if closer, ok := provider.(ResourceProviderCloser); ok {
				closer.Close()
			}
		}()

		// FIXME: The provider interface is currently awkward in that it
		// requires us to tell the provider which resources types and data
		// sources we need. In future this will change to just return
		// everything available, but for now we'll fake that by fetching all
		// of the available names and then requesting them.
		resourceTypes := provider.Resources()
		dataSources := provider.DataSources()
		resourceTypeNames := make([]string, len(resourceTypes))
		for i, o := range resourceTypes {
			resourceTypeNames[i] = o.Name
		}
		dataSourceNames := make([]string, len(dataSources))
		for i, o := range dataSources {
			dataSourceNames[i] = o.Name
		}

		schema, err := provider.GetSchema(&ProviderSchemaRequest{
			ResourceTypes: resourceTypeNames,
			DataSources:   dataSourceNames,
		})
		if err != nil {
			// We'll put a stub in the map so we won't re-attempt this on
			// future calls.
			schemas[typeName] = &ProviderSchema{}
			diags = diags.Append(
				fmt.Errorf("Failed to retrieve schema from provider %q: %s", typeName, err),
			)
			return
		}

		schemas[typeName] = schema
	}

	if config != nil {
		for _, pc := range config.Module.ProviderConfigs {
			ensure(pc.Name)
		}
		for _, rc := range config.Module.ManagedResources {
			providerAddr := rc.ProviderConfigAddr()
			ensure(providerAddr.Type)
		}
		for _, rc := range config.Module.DataResources {
			providerAddr := rc.ProviderConfigAddr()
			ensure(providerAddr.Type)
		}

		// Must also visit our child modules, recursively.
		for _, cc := range config.Children {
			childDiags := loadProviderSchemas(schemas, cc, nil, components)
			diags = diags.Append(childDiags)
		}
	}

	if state != nil {
		for _, ms := range state.Modules {
			for rsKey, rs := range ms.Resources {
				providerAddrStr := rs.Provider
				providerAddr, addrDiags := addrs.ParseAbsProviderConfigStr(providerAddrStr)
				if addrDiags.HasErrors() {
					// Should happen only if someone has tampered manually with
					// the state, since we always write valid provider addrs.
					moduleAddrStr := normalizeModulePath(ms.Path).String()
					if moduleAddrStr == "" {
						moduleAddrStr = "the root module"
					}
					// For now this is a warning, since there are many existing
					// test fixtures that have invalid provider configurations.
					// There's a check deeper in Terraform that makes this a
					// failure when an empty/invalid provider string is present
					// in practice.
					log.Printf("[WARN] LoadSchemas: Resource %s in %s has invalid provider address %q in its state", rsKey, moduleAddrStr, providerAddrStr)
					diags = diags.Append(
						tfdiags.SimpleWarning(fmt.Sprintf("Resource %s in %s has invalid provider address %q in its state", rsKey, moduleAddrStr, providerAddrStr)),
					)
					continue
				}
				ensure(providerAddr.ProviderConfig.Type)
			}
		}
	}

	return diags
}

func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *configs.Config, components contextComponentFactory) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics

	ensure := func(name string) {
		if _, exists := schemas[name]; exists {
			return
		}

		log.Printf("[TRACE] LoadSchemas: retrieving schema for provisioner %q", name)
		provisioner, err := components.ResourceProvisioner(name, "early/"+name)
		if err != nil {
			// We'll put a stub in the map so we won't re-attempt this on
			// future calls.
			schemas[name] = &configschema.Block{}
			diags = diags.Append(
				fmt.Errorf("Failed to instantiate provisioner %q to obtain schema: %s", name, err),
			)
			return
		}
		defer func() {
			if closer, ok := provisioner.(ResourceProvisionerCloser); ok {
				closer.Close()
			}
		}()

		schema, err := provisioner.GetConfigSchema()
		if err != nil {
			// We'll put a stub in the map so we won't re-attempt this on
			// future calls.
			schemas[name] = &configschema.Block{}
			diags = diags.Append(
				fmt.Errorf("Failed to retrieve schema from provisioner %q: %s", name, err),
			)
			return
		}

		schemas[name] = schema
	}

	if config != nil {
		for _, rc := range config.Module.ManagedResources {
			for _, pc := range rc.Managed.Provisioners {
				ensure(pc.Type)
			}
		}

		// Must also visit our child modules, recursively.
		for _, cc := range config.Children {
			childDiags := loadProvisionerSchemas(schemas, cc, components)
			diags = diags.Append(childDiags)
		}
	}

	return diags
}

// ProviderSchema represents the schema for a provider's own configuration
// and the configuration for some or all of its resources and data sources.
//
// The completeness of this structure depends on how it was constructed.
// When constructed for a configuration, it will generally include only
// resource types and data sources used by that configuration.
type ProviderSchema struct {
	Provider      *configschema.Block
	ResourceTypes map[string]*configschema.Block
	DataSources   map[string]*configschema.Block
}

// ProviderSchemaRequest is used to describe to a ResourceProvider which
// aspects of schema are required, when calling the GetSchema method.
type ProviderSchemaRequest struct {
	ResourceTypes []string
	DataSources   []string
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package revel

import (
	"reflect"
	"strings"
)

// Field represents a data field that may be collected in a web form.
type Field struct {
	Name       string
	Error      *ValidationError
	viewArgs   map[string]interface{}
	controller *Controller
}

func NewField(name string, viewArgs map[string]interface{}) *Field {
	err, _ := viewArgs["errors"].(map[string]*ValidationError)[name]
	controller, _ := viewArgs["_controller"].(*Controller)
	return &Field{
		Name:       name,
		Error:      err,
		viewArgs:   viewArgs,
		controller: controller,
	}
}

// ID returns an identifier suitable for use as an HTML id.
func (f *Field) ID() string {
	return strings.Replace(f.Name, ".", "_", -1)
}

// Flash returns the flashed value of this Field.
func (f *Field) Flash() string {
	v, _ := f.viewArgs["flash"].(map[string]string)[f.Name]
	return v
}

// FlashArray returns the flashed value of this Field as a list split on comma.
func (f *Field) FlashArray() []string {
	v := f.Flash()
	if v == "" {
		return []string{}
	}
	return strings.Split(v, ",")
}

func readNext(nextKey string) (string, string) {
	switch nextKey[0] {
	case '[':
		idx := strings.IndexRune(nextKey, ']')
		if idx < 0 {
			return nextKey[1:], ""
		} else {
			return nextKey[1:idx], nextKey[idx+1:]
		}
	case '.':
		nextKey = nextKey[1:]
		fallthrough
	default:
		idx := strings.IndexAny(nextKey, ".[")
		if idx < 0 {
			return nextKey, ""
		} else if nextKey[idx] == '.' {
			return nextKey[:idx], nextKey[idx+1:]
		} else {
			return nextKey[:idx], nextKey[idx:]
		}
	}
}

// Value returns the current value of this Field.
func (f *Field) Value() interface{} {
	var fieldName string

	var nextKey = f.Name
	var val interface{} = f.viewArgs
	for nextKey != "" {
		fieldName, nextKey = readNext(nextKey)

		rVal := reflect.ValueOf(val)
		kind := rVal.Kind()
		if kind == reflect.Map {
			rFieldName := reflect.ValueOf(fieldName)
			val = rVal.MapIndex(rFieldName).Interface()
			if val == nil {
				return nil
			}
			continue
		}

		if kind == reflect.Ptr {
			rVal = rVal.Elem()
		}
		rVal = rVal.FieldByName(fieldName)
		if !rVal.IsValid() {
			return nil
		}
		val = rVal.Interface()
	}

	return val
}

// ErrorClass returns ErrorCSSClass if this field has a validation error, else empty string.
func (f *Field) ErrorClass() string {
	if f.Error != nil {
		if errorClass, ok := f.viewArgs["ERROR_CLASS"]; ok {
			return errorClass.(string)
		}
		return ErrorCSSClass
	}
	return ""
}

// Get the short name and translate it
func (f *Field) ShortName() string {
	name := f.Name
	if i := strings.LastIndex(name, "."); i > 0 {
		name = name[i+1:]
	}
	return f.Translate(name)
}

// Translate the text
func (f *Field) Translate(text string, args ...interface{}) string {
	if f.controller != nil {
		text = f.controller.Message(text, args...)
	}
	return text
}
<commit_msg>fix panic<commit_after>// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package revel

import (
	"reflect"
	"strings"
)

// Field represents a data field that may be collected in a web form.
type Field struct {
	Name       string
	Error      *ValidationError
	viewArgs   map[string]interface{}
	controller *Controller
}

func NewField(name string, viewArgs map[string]interface{}) *Field {
	err, _ := viewArgs["errors"].(map[string]*ValidationError)[name]
	controller, _ := viewArgs["_controller"].(*Controller)
	return &Field{
		Name:       name,
		Error:      err,
		viewArgs:   viewArgs,
		controller: controller,
	}
}

// ID returns an identifier suitable for use as an HTML id.
func (f *Field) ID() string {
	return strings.Replace(f.Name, ".", "_", -1)
}

// Flash returns the flashed value of this Field.
func (f *Field) Flash() string {
	v, _ := f.viewArgs["flash"].(map[string]string)[f.Name]
	return v
}

// FlashArray returns the flashed value of this Field as a list split on comma.
func (f *Field) FlashArray() []string {
	v := f.Flash()
	if v == "" {
		return []string{}
	}
	return strings.Split(v, ",")
}

func readNext(nextKey string) (string, string) {
	switch nextKey[0] {
	case '[':
		idx := strings.IndexRune(nextKey, ']')
		if idx < 0 {
			return nextKey[1:], ""
		} else {
			return nextKey[1:idx], nextKey[idx+1:]
		}
	case '.':
		nextKey = nextKey[1:]
		fallthrough
	default:
		idx := strings.IndexAny(nextKey, ".[")
		if idx < 0 {
			return nextKey, ""
		} else if nextKey[idx] == '.' {
			return nextKey[:idx], nextKey[idx+1:]
		} else {
			return nextKey[:idx], nextKey[idx:]
		}
	}
}

// Value returns the current value of this Field.
func (f *Field) Value() interface{} {
	var fieldName string

	var nextKey = f.Name
	var val interface{} = f.viewArgs
	for nextKey != "" {
		fieldName, nextKey = readNext(nextKey)

		rVal := reflect.ValueOf(val)
		kind := rVal.Kind()
		if kind == reflect.Map {
			rFieldName := reflect.ValueOf(fieldName)
			rVal = rVal.MapIndex(rFieldName)
			if !rVal.IsValid() {
				return nil
			}

			val = rVal.Interface()
			if val == nil {
				return nil
			}
			continue
		}

		if kind == reflect.Ptr {
			rVal = rVal.Elem()
		}
		rVal = rVal.FieldByName(fieldName)
		if !rVal.IsValid() {
			return nil
		}
		val = rVal.Interface()
		if val == nil {
			return nil
		}
	}

	return val
}

// ErrorClass returns ErrorCSSClass if this field has a validation error, else empty string.
func (f *Field) ErrorClass() string {
	if f.Error != nil {
		if errorClass, ok := f.viewArgs["ERROR_CLASS"]; ok {
			return errorClass.(string)
		}
		return ErrorCSSClass
	}
	return ""
}

// Get the short name and translate it
func (f *Field) ShortName() string {
	name := f.Name
	if i := strings.LastIndex(name, "."); i > 0 {
		name = name[i+1:]
	}
	return f.Translate(name)
}

// Translate the text
func (f *Field) Translate(text string, args ...interface{}) string {
	if f.controller != nil {
		text = f.controller.Message(text, args...)
	}
	return text
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
)

var maxStorageString = flag.String("maxSize", "",
	"Approximate maximum amount of space to allocate")
var maxStorage int64

func hashFilename(base, hstr string) string {
	return base + "/" + hstr[:2] + "/" + hstr
}

type ReadSeekCloser interface {
	io.ReadSeeker
	io.Closer
}

func openBlob(hstr string) (ReadSeekCloser, error) {
	return os.Open(hashFilename(*root, hstr))
}

func removeObject(h string) error {
	err := maybeRemoveBlobOwnership(h)
	if err == nil {
		err = os.Remove(hashFilename(*root, h))
		log.Printf("Removed local copy of %v, result=%v",
			h, errorOrSuccess(err))
	}
	return err
}

func forceRemoveObject(h string) error {
	removeBlobOwnershipRecord(h, serverId)
	return os.Remove(hashFilename(*root, h))
}

func verifyObjectHash(h string) error {
	f, err := openBlob(h)
	if err != nil {
		return err
	}
	defer f.Close()

	sh := getHash()
	_, err = io.Copy(sh, f)
	if err != nil {
		return err
	}

	hstring := hex.EncodeToString(sh.Sum([]byte{}))
	if h != hstring {
		err = forceRemoveObject(h)
		log.Printf("Removed corrupt file from disk: %v (was %v), result=%v",
			h, hstring, errorOrSuccess(err))
		return fmt.Errorf("Hash from disk of %v was %v", h, hstring)
	}
	return nil
}

func shouldVerifyObject(h string) bool {
	b, err := getBlobOwnership(h)
	if err != nil {
		return true
	}
	// True if we haven't checked the object in 30 days.
	return b.Nodes[serverId].Add(30 * 24 * time.Hour).After(time.Now())
}

func verifyWorker(ch chan os.FileInfo) {
	nl, err := findAllNodes()
	if err != nil {
		log.Printf("Couldn't find node list during verify: %v", err)
		nl = NodeList{}
	}
	for info := range ch {
		var err error
		force := false
		if shouldVerifyObject(info.Name()) {
			err = verifyObjectHash(info.Name())
			force = true
		}
		if err == nil {
			recordBlobOwnership(info.Name(), info.Size(), force)
		} else {
			log.Printf("Invalid hash for object %v found at verification: %v",
				info.Name(), err)
			removeBlobOwnershipRecord(info.Name(), serverId)
			if len(nl) > 0 {
				salvageBlob(info.Name(), "", 1, nl)
			}
		}
	}
}

func quickVerifyWorker(ch chan os.FileInfo) {
	for info := range ch {
		recordBlobOwnership(info.Name(), info.Size(), false)
	}
}

func reconcileWith(wf func(chan os.FileInfo)) error {
	explen := getHash().Size() * 2

	vch := make(chan os.FileInfo)
	defer close(vch)

	for i := 0; i < *verifyWorkers; i++ {
		go wf(vch)
	}

	return filepath.Walk(*root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() && !strings.HasPrefix(info.Name(), "tmp") &&
			len(info.Name()) == explen {

			vch <- info

			return err
		}
		return nil
	})
}

func reconcile() error {
	return reconcileWith(verifyWorker)
}

func quickReconcile() error {
	return reconcileWith(quickVerifyWorker)
}
<commit_msg>Fully verification only when something is too old.<commit_after>package main

import (
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
)

var maxStorageString = flag.String("maxSize", "",
	"Approximate maximum amount of space to allocate")
var maxStorage int64

func hashFilename(base, hstr string) string {
	return base + "/" + hstr[:2] + "/" + hstr
}

type ReadSeekCloser interface {
	io.ReadSeeker
	io.Closer
}

func openBlob(hstr string) (ReadSeekCloser, error) {
	return os.Open(hashFilename(*root, hstr))
}

func removeObject(h string) error {
	err := maybeRemoveBlobOwnership(h)
	if err == nil {
		err = os.Remove(hashFilename(*root, h))
		log.Printf("Removed local copy of %v, result=%v",
			h, errorOrSuccess(err))
	}
	return err
}

func forceRemoveObject(h string) error {
	removeBlobOwnershipRecord(h, serverId)
	return os.Remove(hashFilename(*root, h))
}

func verifyObjectHash(h string) error {
	f, err := openBlob(h)
	if err != nil {
		return err
	}
	defer f.Close()

	sh := getHash()
	_, err = io.Copy(sh, f)
	if err != nil {
		return err
	}

	hstring := hex.EncodeToString(sh.Sum([]byte{}))
	if h != hstring {
		err = forceRemoveObject(h)
		log.Printf("Removed corrupt file from disk: %v (was %v), result=%v",
			h, hstring, errorOrSuccess(err))
		return fmt.Errorf("Hash from disk of %v was %v", h, hstring)
	}
	return nil
}

func shouldVerifyObject(h string) bool {
	b, err := getBlobOwnership(h)
	if err != nil {
		return true
	}
	// True if we haven't checked the object in 30 days.
	return b.Nodes[serverId].Add(30 * 24 * time.Hour).Before(time.Now())
}

func verifyWorker(ch chan os.FileInfo) {
	nl, err := findAllNodes()
	if err != nil {
		log.Printf("Couldn't find node list during verify: %v", err)
		nl = NodeList{}
	}
	for info := range ch {
		var err error
		force := false
		if shouldVerifyObject(info.Name()) {
			err = verifyObjectHash(info.Name())
			force = true
		}
		if err == nil {
			recordBlobOwnership(info.Name(), info.Size(), force)
		} else {
			log.Printf("Invalid hash for object %v found at verification: %v",
				info.Name(), err)
			removeBlobOwnershipRecord(info.Name(), serverId)
			if len(nl) > 0 {
				salvageBlob(info.Name(), "", 1, nl)
			}
		}
	}
}

func quickVerifyWorker(ch chan os.FileInfo) {
	for info := range ch {
		recordBlobOwnership(info.Name(), info.Size(), false)
	}
}

func reconcileWith(wf func(chan os.FileInfo)) error {
	explen := getHash().Size() * 2

	vch := make(chan os.FileInfo)
	defer close(vch)

	for i := 0; i < *verifyWorkers; i++ {
		go wf(vch)
	}

	return filepath.Walk(*root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() && !strings.HasPrefix(info.Name(), "tmp") &&
			len(info.Name()) == explen {

			vch <- info

			return err
		}
		return nil
	})
}

func reconcile() error {
	return reconcileWith(verifyWorker)
}

func quickReconcile() error {
	return reconcileWith(quickVerifyWorker)
}
<|endoftext|>


<commit_before>package box

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
)

// TODO(ttacon): reconcile this with Folder for one common struct?
type File struct {
	ID                string          `json:"id,omitempty"`
	FolderUploadEmail *AccessEmail    `json:"folder_upload_email,omitempty"`
	Parent            *Item           `json:"parent,omitempty"`
	ItemStatus        string          `json:"item_status"`
	ItemCollection    *ItemCollection `json:"item_collection"`
	Type              string          `json:"type"` // TODO(ttacon): enum
	Description       string          `json:"description"`
	Size              int             `json:"size"`
	CreateBy          *Item           `json:"created_by"`
	ModifiedBy        *Item           `json:"modified_by"`
	TrashedAt         *string         `json:"trashed_at"`          // TODO(ttacon): change to time.Time
	ContentModifiedAt *string         `json:"content_modified_at"` // TODO(ttacon): change to time.Time
	PurgedAt          *string         `json:"purged_at"`           // TODO(ttacon): change to time.Time, this field isn't documented but I keep getting it back...
	SharedLinkg       *string         `json:"shared_link"`
	SequenceId        string          `json:"sequence_id"`
	ETag              *string         `json:"etag"`
	Name              string          `json:"name"`
	CreatedAt         *string         `json:"created_at"` // TODO(ttacon): change to time.Time
	OwnedBy           *Item           `json:"owned_by"`
	ModifiedAt        *string         `json:"modified_at"`        // TODO(ttacon): change to time.Time
	ContentCreatedAt  *string         `json:"content_created_at"` // TODO(ttacon): change to time.Time
	PathCollection    *PathCollection `json:"path_collection"`    // TODO(ttacon): make sure this is the correct kind of struct(ure)
	SharedLink        *Link           `json:"shared_link"`

	SHA1 string `json:"sha1"`
}

type FileCollection struct {
	TotalCount int     `json:"total_count"`
	Entries    []*File `json:"entries"`
}

// Documentation: https://developer.box.com/docs/#files-get
func (c *Client) GetFile(fileId string) (*http.Response, *File, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s", fileId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *File
	resp, err := c.Do(req, data)
	return resp, data, err
}

// Documentation https://developer.box.com/docs/#files-upload-a-file
// TODO(ttacon): deal with handling SHA1 headers
func (c *Client) UploadFile(filePath, parentId string) (*http.Response, *FileCollection, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, nil, err
	}
	defer file.Close()

	fileContents, err := ioutil.ReadAll(file)
	if err != nil {
		return nil, nil, err
	}

	fi, err := file.Stat()
	if err != nil {
		return nil, nil, err
	}

	var (
		body   = &bytes.Buffer{}
		writer = multipart.NewWriter(body)
	)

	// write the file
	part, err := writer.CreateFormFile("file", fi.Name())
	if err != nil {
		return nil, nil, err
	}
	part.Write(fileContents)

	// write the other form fields we need
	err = writer.WriteField("filename", fi.Name())
	if err != nil {
		return nil, nil, err
	}

	err = writer.WriteField("parent_id", parentId)
	if err != nil {
		return nil, nil, err
	}

	// TODO(ttacon): add in content_created_at, content_modified_at

	err = writer.Close()
	if err != nil {
		return nil, nil, err
	}

	// TODO(ttacon): refactor to use Client.NewRequest/Do when it supports
	// io.Writer
	req, err := http.NewRequest(
		"POST",
		fmt.Sprintf("https://upload.box.com/api/2.0/files/content"),
		body,
	)
	req.Header.Add("Content-Type", writer.FormDataContentType())
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data FileCollection
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}

// Documentation: https://developers.box.com/docs/#files-delete-a-file
func (c *Client) DeleteFile(fileId string) (*http.Response, error) {
	req, err := c.NewRequest(
		"DELETE",
		fmt.Sprintf("/files/%s", fileId),
		nil,
	)
	if err != nil {
		return nil, err
	}

	return c.Do(req, nil)
}

// Documentation: https://developers.box.com/docs/#files-copy-a-file
func (c *Client) CopyFile(fileId, parent, name string) (*http.Response, *File, error) {
	var bodyData = map[string]interface{}{
		"parent": map[string]string{
			"id": parent,
		},
		"name": name,
	}

	req, err := http.NewRequest(
		"POST",
		fmt.Sprintf("/files/%s/copy", fileId),
		dataMap,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *File
	resp, err := c.Do(req, data)
	return resp, data, err
}

// NOTE: we return the http.Response as Box may return a 202 if there is not
// yet a download link, or a 302 with the link - this allows the user to
// decide what to do.
// Documentation: https://developers.box.com/docs/#files-download-a-file
func (c *Client) DownloadFile(fileId string) (*http.Response, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s/content", fileId),
		nil,
	)
	if err != nil {
		return nil, err
	}

	return c.Do(req, nil)
}

// Documentation: https://developers.box.com/docs/#files-view-versions-of-a-file
// TODO(ttacon): don't use file collection, make actual structs specific to file versions
func (c *Client) ViewVersionsOfFile(fileId string) (*http.Response, *FileCollection, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s/versions", fileId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *FileCollection
	resp, err := c.Do(req, data)
	return resp, data, err
}

// NOTE: we only return the response as there are many possible responses that we
// feel the user should have control over
// Documentation: https://developers.box.com/docs/#files-get-a-thumbnail-for-a-file
func (c *Client) GetThumbnail(fileId string) (*http.Response, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s/thumbnail.extension", fileId),
		nil,
	)
	if err != nil {
		return nil, err
	}

	return c.Do(req)
}

// Documentation: https://developers.box.com/docs/#files-create-a-shared-link-for-a-file
func (c *Client) CreateSharedLinkForFile(fileId, access, unsharedAt string, canDownload, canPreview bool) (*http.Response, *File, error) {
	var dataMap = make(map[string]interface{})
	if len(access) > 0 {
		dataMap["access"] = access
	}
	// TODO(ttacon): support unshared_at as time.Time
	// TODO(ttacon): validate access is open or company before add permissions
	if canDownload {
		dataMap["permissions"] = map[string]bool{
			"can_download": canDownload,
		}
	}
	if canPreview {
		if m, ok := dataMap["permissions"]; ok {
			mVal, _ := m.(map[string]bool)
			mVal["can_preview"] = canPreview
		} else {
			dataMap["permissions"] = map[string]bool{
				"can_preview": canPreview,
			}
		}
	}

	req, err := c.NewRequest(
		"PUT",
		fmt.Sprintf("/files/%s", fileId),
		dataMap,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *File
	resp, err := c.Do(req, data)
	return resp, data, err
}

// Documentation: https://developers.box.com/docs/#files-get-a-trashed-file
func (c *Client) GetTrashedFile(fileId string) (*http.Response, *File, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s/trash", fileId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *File
	resp, err := c.Do(req, data)
	return resp, data, err
}

// Documentation: https://developers.box.com/docs/#files-restore-a-trashed-item
func (c *Client) RestoreTrashedItem(fileId, name, parentId string) (*http.Response, *File, error) {
	var dataMap = make(map[string]interface{})
	if len(name) > 0 {
		dataMap["name"] = name
	}
	if len(parentId) > 0 {
		dataMap["parent"] = map[string]string{
			"id": parentId,
		}
	}

	req, err := c.NewRequest(
		"POST",
		fmt.Sprintf("/files/%s", fileId),
		dataMap,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *File
	resp, err := c.Do(req, data)
	return resp, data, err
}

// Documentation: https://developers.box.com/docs/#files-permanently-delete-a-trashed-file
func (c *Client) PermanentlyDeleteTrashedFile(fileId string) (*http.Response, error) {
	req, err := c.NewRequest(
		"DELETE",
		fmt.Sprintf("/files/%s/trash", fileId),
		nil,
	)
	if err != nil {
		return nil, err
	}

	return c.Do(req, nil)
}

// Documentation: https://developers.box.com/docs/#files-view-the-comments-on-a-file
func (c *Client) ViewCommentsOnFile(fileId string) (*http.Response, *CommentCollection, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s/comments", fileId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *CommentCollection
	resp, err := c.Do(req, data)
	return resp, data, err
}

// Documentation: https://developers.box.com/docs/#files-get-the-tasks-for-a-file
func (c *Client) GetTasksForFile(fileId string) (*http.Response, *TaskCollection, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s/tasks", fileId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *TaskCollection
	resp, err := c.Do(req, data)
	return resp, data, err
}
<commit_msg>refactor upload file a little<commit_after>package box

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
)

// TODO(ttacon): reconcile this with Folder for one common struct?
type File struct {
	ID                string          `json:"id,omitempty"`
	FolderUploadEmail *AccessEmail    `json:"folder_upload_email,omitempty"`
	Parent            *Item           `json:"parent,omitempty"`
	ItemStatus        string          `json:"item_status"`
	ItemCollection    *ItemCollection `json:"item_collection"`
	Type              string          `json:"type"` // TODO(ttacon): enum
	Description       string          `json:"description"`
	Size              int             `json:"size"`
	CreateBy          *Item           `json:"created_by"`
	ModifiedBy        *Item           `json:"modified_by"`
	TrashedAt         *string         `json:"trashed_at"`          // TODO(ttacon): change to time.Time
	ContentModifiedAt *string         `json:"content_modified_at"` // TODO(ttacon): change to time.Time
	PurgedAt          *string         `json:"purged_at"`           // TODO(ttacon): change to time.Time, this field isn't documented but I keep getting it back...
	SharedLinkg       *string         `json:"shared_link"`
	SequenceId        string          `json:"sequence_id"`
	ETag              *string         `json:"etag"`
	Name              string          `json:"name"`
	CreatedAt         *string         `json:"created_at"` // TODO(ttacon): change to time.Time
	OwnedBy           *Item           `json:"owned_by"`
	ModifiedAt        *string         `json:"modified_at"`        // TODO(ttacon): change to time.Time
	ContentCreatedAt  *string         `json:"content_created_at"` // TODO(ttacon): change to time.Time
	PathCollection    *PathCollection `json:"path_collection"`    // TODO(ttacon): make sure this is the correct kind of struct(ure)
	SharedLink        *Link           `json:"shared_link"`

	SHA1 string `json:"sha1"`
}

type FileCollection struct {
	TotalCount int     `json:"total_count"`
	Entries    []*File `json:"entries"`
}

// Documentation: https://developer.box.com/docs/#files-get
func (c *Client) GetFile(fileId string) (*http.Response, *File, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s", fileId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *File
	resp, err := c.Do(req, data)
	return resp, data, err
}

// Documentation https://developer.box.com/docs/#files-upload-a-file
// TODO(ttacon): deal with handling SHA1 headers
func (c *Client) UploadFile(filePath, parentId string) (*http.Response, *FileCollection, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, nil, err
	}
	defer file.Close()

	fileContents, err := ioutil.ReadAll(file)
	if err != nil {
		return nil, nil, err
	}

	fi, err := file.Stat()
	if err != nil {
		return nil, nil, err
	}

	var (
		body   = &bytes.Buffer{}
		writer = multipart.NewWriter(body)
	)

	// write the file
	part, err := writer.CreateFormFile("file", fi.Name())
	if err != nil {
		return nil, nil, err
	}
	part.Write(fileContents)

	// write the other form fields we need
	err = writer.WriteField("filename", fi.Name())
	if err != nil {
		return nil, nil, err
	}

	err = writer.WriteField("parent_id", parentId)
	if err != nil {
		return nil, nil, err
	}

	// TODO(ttacon): add in content_created_at, content_modified_at

	err = writer.Close()
	if err != nil {
		return nil, nil, err
	}

	// TODO(ttacon): refactor to use Client.NewRequest/Do when it supports
	// io.Writer
	req, err := http.NewRequest(
		"POST",
		fmt.Sprintf("https://upload.box.com/api/2.0/files/content"),
		body,
	)
	req.Header.Add("Content-Type", writer.FormDataContentType())
	if err != nil {
		return nil, nil, err
	}

	var data *FileCollection
	resp, err := c.Do(req, data)
	return resp, data, err
}

// Documentation: https://developers.box.com/docs/#files-delete-a-file
func (c *Client) DeleteFile(fileId string) (*http.Response, error) {
	req, err := c.NewRequest(
		"DELETE",
		fmt.Sprintf("/files/%s", fileId),
		nil,
	)
	if err != nil {
		return nil, err
	}

	return c.Do(req, nil)
}

// Documentation: https://developers.box.com/docs/#files-copy-a-file
func (c *Client) CopyFile(fileId, parent, name string) (*http.Response, *File, error) {
	var bodyData = map[string]interface{}{
		"parent": map[string]string{
			"id": parent,
		},
		"name": name,
	}

	req, err := http.NewRequest(
		"POST",
		fmt.Sprintf("/files/%s/copy", fileId),
		dataMap,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *File
	resp, err := c.Do(req, data)
	return resp, data, err
}

// NOTE: we return the http.Response as Box may return a 202 if there is not
// yet a download link, or a 302 with the link - this allows the user to
// decide what to do.
// Documentation: https://developers.box.com/docs/#files-download-a-file
func (c *Client) DownloadFile(fileId string) (*http.Response, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s/content", fileId),
		nil,
	)
	if err != nil {
		return nil, err
	}

	return c.Do(req, nil)
}

// Documentation: https://developers.box.com/docs/#files-view-versions-of-a-file
// TODO(ttacon): don't use file collection, make actual structs specific to file versions
func (c *Client) ViewVersionsOfFile(fileId string) (*http.Response, *FileCollection, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s/versions", fileId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *FileCollection
	resp, err := c.Do(req, data)
	return resp, data, err
}

// NOTE: we only return the response as there are many possible responses that we
// feel the user should have control over
// Documentation: https://developers.box.com/docs/#files-get-a-thumbnail-for-a-file
func (c *Client) GetThumbnail(fileId string) (*http.Response, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s/thumbnail.extension", fileId),
		nil,
	)
	if err != nil {
		return nil, err
	}

	return c.Do(req)
}

// Documentation: https://developers.box.com/docs/#files-create-a-shared-link-for-a-file
func (c *Client) CreateSharedLinkForFile(fileId, access, unsharedAt string, canDownload, canPreview bool) (*http.Response, *File, error) {
	var dataMap = make(map[string]interface{})
	if len(access) > 0 {
		dataMap["access"] = access
	}
	// TODO(ttacon): support unshared_at as time.Time
	// TODO(ttacon): validate access is open or company before add permissions
	if canDownload {
		dataMap["permissions"] = map[string]bool{
			"can_download": canDownload,
		}
	}
	if canPreview {
		if m, ok := dataMap["permissions"]; ok {
			mVal, _ := m.(map[string]bool)
			mVal["can_preview"] = canPreview
		} else {
			dataMap["permissions"] = map[string]bool{
				"can_preview": canPreview,
			}
		}
	}

	req, err := c.NewRequest(
		"PUT",
		fmt.Sprintf("/files/%s", fileId),
		dataMap,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *File
	resp, err := c.Do(req, data)
	return resp, data, err
}

// Documentation: https://developers.box.com/docs/#files-get-a-trashed-file
func (c *Client) GetTrashedFile(fileId string) (*http.Response, *File, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s/trash", fileId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *File
	resp, err := c.Do(req, data)
	return resp, data, err
}

// Documentation: https://developers.box.com/docs/#files-restore-a-trashed-item
func (c *Client) RestoreTrashedItem(fileId, name, parentId string) (*http.Response, *File, error) {
	var dataMap = make(map[string]interface{})
	if len(name) > 0 {
		dataMap["name"] = name
	}
	if len(parentId) > 0 {
		dataMap["parent"] = map[string]string{
			"id": parentId,
		}
	}

	req, err := c.NewRequest(
		"POST",
		fmt.Sprintf("/files/%s", fileId),
		dataMap,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *File
	resp, err := c.Do(req, data)
	return resp, data, err
}

// Documentation: https://developers.box.com/docs/#files-permanently-delete-a-trashed-file
func (c *Client) PermanentlyDeleteTrashedFile(fileId string) (*http.Response, error) {
	req, err := c.NewRequest(
		"DELETE",
		fmt.Sprintf("/files/%s/trash", fileId),
		nil,
	)
	if err != nil {
		return nil, err
	}

	return c.Do(req, nil)
}

// Documentation: https://developers.box.com/docs/#files-view-the-comments-on-a-file
func (c *Client) ViewCommentsOnFile(fileId string) (*http.Response, *CommentCollection, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s/comments", fileId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *CommentCollection
	resp, err := c.Do(req, data)
	return resp, data, err
}

// Documentation: https://developers.box.com/docs/#files-get-the-tasks-for-a-file
func (c *Client) GetTasksForFile(fileId string) (*http.Response, *TaskCollection, error) {
	req, err := c.NewRequest(
		"GET",
		fmt.Sprintf("/files/%s/tasks", fileId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	var data *TaskCollection
	resp, err := c.Do(req, data)
	return resp, data, err
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"github.com/paaff/Filly/files"
	"github.com/spf13/viper"
)

func main() {
	// Load configuration file
	viper.SetConfigName("fillyConf")
	viper.SetConfigType("json")
	viper.AddConfigPath("./config")
	vErr := viper.ReadInConfig()
	if vErr != nil {
		panic(fmt.Errorf("Fatal error config file: %s \n", vErr))
	} else {
		dirContent.ROOT_DIR = viper.GetString("root_dir")
	}

	// Create a simple file server
	fs := http.FileServer(http.Dir("./web/dist"))
	http.Handle("/", fs)

	// GetDir endpoint
	http.HandleFunc("/browse", browseHandler)

	err := http.ListenAndServe(":1337", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}

}

func browseHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		path := r.FormValue("path")
		// Browse from the POST form variable
		cont, status := dirContent.GetDirectoryContentInJSON(path)
		if cont != nil {
			w.Header().Set("Content-Type", "application/json")
			json.NewEncoder(w).Encode(cont)
		} else {
			// Error handling.
			errorHandler(w, r, status)
		}
	}
}

func errorHandler(w http.ResponseWriter, r *http.Request, status int) {
	w.WriteHeader(status)
	if status == 404 {
		fmt.Fprint(w, "404 - NOT FOUND")
	}

}
<commit_msg>NO ISSUES HERE MOVE A LONG<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"github.com/paaff/Filly/files"
	"github.com/spf13/viper"
)

func main() {
	// Load configuration file
	viper.SetConfigName("fillyconf")
	viper.SetConfigType("json")
	viper.AddConfigPath("./config")
	vErr := viper.ReadInConfig()
	if vErr != nil {
		panic(fmt.Errorf("Fatal error config file: %s \n", vErr))
	} else {
		dirContent.ROOT_DIR = viper.GetString("root_dir")
	}

	// Create a simple file server
	fs := http.FileServer(http.Dir("./web/dist"))
	http.Handle("/", fs)

	// GetDir endpoint
	http.HandleFunc("/browse", browseHandler)

	err := http.ListenAndServe(":1337", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}

}

func browseHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		path := r.FormValue("path")
		// Browse from the POST form variable
		cont, status := dirContent.GetDirectoryContentInJSON(path)
		if cont != nil {
			w.Header().Set("Content-Type", "application/json")
			json.NewEncoder(w).Encode(cont)
		} else {
			// Error handling.
			errorHandler(w, r, status)
		}
	}
}

func errorHandler(w http.ResponseWriter, r *http.Request, status int) {
	w.WriteHeader(status)
	if status == 404 {
		fmt.Fprint(w, "404 - NOT FOUND")
	}

}
<|endoftext|>


<commit_before>package openshift

import (
	"bytes"
	"fmt"
	"html/template"
	"net/http"
	"net/http/httputil"
	"reflect"
	"unsafe"

	"time"

	yaml "gopkg.in/yaml.v2"
)

const (
	fieldKind            = "kind"
	fieldAPIVersion      = "apiVersion"
	fieldObjects         = "objects"
	fieldItems           = "items"
	fieldMetadata        = "metadata"
	fieldNamespace       = "namespace"
	fieldName            = "name"
	fieldResourceVersion = "resourceVersion"

	valTemplate       = "Template"
	valProjectRequest = "ProjectRequest"
	valList           = "List"
)

var (
	endpoints = map[string]map[string]string{
		"POST": {
			"Project":                `/oapi/v1/projects`,
			"ProjectRequest":         `/oapi/v1/projectrequests`,
			"RoleBinding":            `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/rolebindings`,
			"RoleBindingRestriction": `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/rolebindingrestrictions`,
			"Route":                  `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/routes`,
			"DeploymentConfig":       `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/deploymentconfigs`,
			"PersistentVolumeClaim":  `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/persistentvolumeclaims`,
			"Service":                `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/services`,
			"Secret":                 `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/secrets`,
			"ServiceAccount":         `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/serviceaccounts`,
			"ConfigMap":              `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/configmaps`,
			"ResourceQuota":          `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/resourcequotas`,
			"LimitRange":             `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/limitranges`,
		},
		"PUT": {
			"Project":                `/oapi/v1/projects/{{ index . "metadata" "name"}}`,
			"RoleBinding":            `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/rolebindings/{{ index . "metadata" "name"}}`,
			"RoleBindingRestriction": `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/rolebindingrestrictions/{{ index . "metadata" "name"}}`,
			"Route":                  `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/routes/{{ index . "metadata" "name"}}`,
			"DeploymentConfig":       `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/deploymentconfigs/{{ index . "metadata" "name"}}`,
			"PersistentVolumeClaim":  `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/persistentvolumeclaims/{{ index . "metadata" "name"}}`,
			"Service":                `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/services/{{ index . "metadata" "name"}}`,
			"Secret":                 `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/secrets/{{ index . "metadata" "name"}}`,
			"ServiceAccount":         `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/serviceaccounts/{{ index . "metadata" "name"}}`,
			"ConfigMap":              `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/configmaps/{{ index . "metadata" "name"}}`,
			"ResourceQuota":          `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/resourcequotas/{{ index . "metadata" "name"}}`,
			"LimitRange":             `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/limitranges/{{ index . "metadata" "name"}}`,
		},
		"GET": {
			"Project":                `/oapi/v1/projects/{{ index . "metadata" "name"}}`,
			"RoleBinding":            `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/rolebindings/{{ index . "metadata" "name"}}`,
			"RoleBindingRestriction": `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/rolebindingrestrictions/{{ index . "metadata" "name"}}`,
			"Route":                  `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/routes/{{ index . "metadata" "name"}}`,
			"DeploymentConfig":       `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/deploymentconfigs/{{ index . "metadata" "name"}}`,
			"PersistentVolumeClaim":  `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/persistentvolumeclaims/{{ index . "metadata" "name"}}`,
			"Service":                `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/services/{{ index . "metadata" "name"}}`,
			"Secret":                 `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/secrets/{{ index . "metadata" "name"}}`,
			"ServiceAccount":         `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/serviceaccounts/{{ index . "metadata" "name"}}`,
			"ConfigMap":              `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/configmaps/{{ index . "metadata" "name"}}`,
			"ResourceQuota":          `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/resourcequotas/{{ index . "metadata" "name"}}`,
			"LimitRange":             `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/limitranges/{{ index . "metadata" "name"}}`,
		},
		/*
			"PUT": {
				"ResourceQuota": `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/resourcequotas/{{ index . "metadata" "name"}}`,
				"LimitRange":    `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/limitranges/{{ index . "metadata" "name"}}`,
			},
		*/
		"DELETE": {
			"ResourceQuota": `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/resourcequotas/{{ index . "metadata" "name"}}`,
		},
	}
)

// ApplyOptions contains options for connecting to the target API
type ApplyOptions struct {
	Config
	Overwrite bool
	Namespace string
}

func (a ApplyOptions) withNamespace(namespace string) ApplyOptions {
	return ApplyOptions{
		Config:    a.Config,
		Overwrite: a.Overwrite,
		Namespace: namespace,
	}
}

// Apply a given template structure to a target API
func Apply(source string, opts ApplyOptions) error {

	objects, err := parseObjects(source, opts.Namespace)
	if err != nil {
		return err
	}

	err = allKnownTypes(objects)
	if err != nil {
		return err
	}

	err = applyAll(objects, opts)
	if err != nil {
		return err
	}

	return nil
}

func applyAll(objects []map[interface{}]interface{}, opts ApplyOptions) error {
	for index, obj := range objects {
		_, err := apply(obj, "POST", opts)
		if err != nil {
			return err
		}
		if index == 0 {
			time.Sleep(time.Second * 2)
		}
	}
	return nil
}

func apply(object map[interface{}]interface{}, action string, opts ApplyOptions) (map[interface{}]interface{}, error) {
	body, err := yaml.Marshal(object)
	if err != nil {
		return nil, err
	}

	url, err := createURL(opts.MasterURL, action, object)
	if url == "" {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest(action, url, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/yaml")
	req.Header.Set("Content-Type", "application/yaml")
	req.Header.Set("Authorization", "Bearer "+opts.Token)

	// for debug only
	rb, _ := httputil.DumpRequest(req, true)
	if false {
		fmt.Println(string(rb))
	}

	client := http.DefaultClient
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	buf := new(bytes.Buffer)
	buf.ReadFrom(resp.Body)
	b := buf.Bytes()

	var respType map[interface{}]interface{}
	err = yaml.Unmarshal(b, &respType)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode == http.StatusConflict {
		/*
			if object[fieldKind] == valProjectRequest {
				return respType, nil
			}
			resp, err := apply(object, "GET", opts)
			if err != nil {
				return nil, err
			}
			fmt.Println(resp)
			updateResourceVersion(resp, object)
			fmt.Println(object)
		*/
		return apply(object, "PUT", opts)
	}
	/*
		if resp.StatusCode == http.StatusForbidden && opts.Overwrite {

		} else
	*/
	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Unknown response:\n%v\n%v", *(*string)(unsafe.Pointer(&b)), string(rb))
	}

	fmt.Printf("%v %v %v in %v\n", action, respType[fieldKind], getName(respType), opts.Namespace)
	return respType, nil
}

func updateResourceVersion(source, target map[interface{}]interface{}) {
	if sourceMeta, sourceMetaFound := source[fieldMetadata].(map[interface{}]interface{}); sourceMetaFound {
		if sourceVersion, sourceVersionFound := sourceMeta[fieldResourceVersion]; sourceVersionFound {
			if targetMeta, targetMetaFound := source[fieldMetadata].(map[interface{}]interface{}); targetMetaFound {
				fmt.Println("setting v", sourceVersion, reflect.TypeOf(sourceVersion).Kind())
				targetMeta[fieldResourceVersion] = sourceVersion
			}
		}
	}
}

func getName(obj map[interface{}]interface{}) string {
	if meta, metaFound := obj[fieldMetadata].(map[interface{}]interface{}); metaFound {
		if name, nameFound := meta[fieldName].(string); nameFound {
			return name
		}
	}
	return ""
}

func parseObjects(source string, namespace string) ([]map[interface{}]interface{}, error) {
	var template map[interface{}]interface{}

	err := yaml.Unmarshal([]byte(source), &template)
	if err != nil {
		return nil, err
	}

	if template[fieldKind] == valTemplate || template[fieldKind] == valList {
		var ts []interface{}
		if template[fieldKind] == valTemplate {
			ts = template[fieldObjects].([]interface{})
		} else if template[fieldKind] == valList {
			ts = template[fieldItems].([]interface{})
		}
		var objs []map[interface{}]interface{}
		for _, obj := range ts {
			objs = append(objs, obj.(map[interface{}]interface{}))
		}
		if namespace != "" {
			for _, obj := range objs {
				if val, ok := obj[fieldMetadata].(map[interface{}]interface{}); ok {
					if _, ok := val[fieldNamespace]; !ok {
						val[fieldNamespace] = namespace
					}
				}
			}
		}

		return objs, nil
	}
	return []map[interface{}]interface{}{template}, nil
}

// TODO: a bit off now that there are multiple Action methods
func allKnownTypes(objects []map[interface{}]interface{}) error {
	m := multiError{}
	for _, obj := range objects {
		if _, ok := endpoints["POST"][obj[fieldKind].(string)]; !ok {
			m.Errors = append(m.Errors, fmt.Errorf("Unknown type: %v", obj[fieldKind]))
		}
	}
	if len(m.Errors) > 0 {
		return m
	}
	return nil
}

func createURL(hostURL, action string, object map[interface{}]interface{}) (string, error) {
	urlTemplate, found := endpoints[action][object[fieldKind].(string)]
	if !found {
		return "", nil
	}
	target, err := template.New("url").Parse(urlTemplate)
	if err != nil {
		return "", err
	}
	var buf bytes.Buffer
	err = target.Execute(&buf, object)
	if err != nil {
		return "", err
	}
	str := buf.String()
	return hostURL + str, nil
}
<commit_msg>Set correct target when updating resourceVersion<commit_after>package openshift

import (
	"bytes"
	"fmt"
	"html/template"
	"net/http"
	"net/http/httputil"
	"reflect"
	"unsafe"

	"time"

	yaml "gopkg.in/yaml.v2"
)

const (
	fieldKind            = "kind"
	fieldAPIVersion      = "apiVersion"
	fieldObjects         = "objects"
	fieldItems           = "items"
	fieldMetadata        = "metadata"
	fieldNamespace       = "namespace"
	fieldName            = "name"
	fieldResourceVersion = "resourceVersion"

	valTemplate       = "Template"
	valProjectRequest = "ProjectRequest"
	valList           = "List"
)

var (
	endpoints = map[string]map[string]string{
		"POST": {
			"Project":                `/oapi/v1/projects`,
			"ProjectRequest":         `/oapi/v1/projectrequests`,
			"RoleBinding":            `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/rolebindings`,
			"RoleBindingRestriction": `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/rolebindingrestrictions`,
			"Route":                  `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/routes`,
			"DeploymentConfig":       `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/deploymentconfigs`,
			"PersistentVolumeClaim":  `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/persistentvolumeclaims`,
			"Service":                `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/services`,
			"Secret":                 `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/secrets`,
			"ServiceAccount":         `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/serviceaccounts`,
			"ConfigMap":              `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/configmaps`,
			"ResourceQuota":          `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/resourcequotas`,
			"LimitRange":             `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/limitranges`,
		},
		"PUT": {
			"Project":                `/oapi/v1/projects/{{ index . "metadata" "name"}}`,
			"RoleBinding":            `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/rolebindings/{{ index . "metadata" "name"}}`,
			"RoleBindingRestriction": `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/rolebindingrestrictions/{{ index . "metadata" "name"}}`,
			"Route":                  `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/routes/{{ index . "metadata" "name"}}`,
			"DeploymentConfig":       `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/deploymentconfigs/{{ index . "metadata" "name"}}`,
			"PersistentVolumeClaim":  `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/persistentvolumeclaims/{{ index . "metadata" "name"}}`,
			"Service":                `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/services/{{ index . "metadata" "name"}}`,
			"Secret":                 `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/secrets/{{ index . "metadata" "name"}}`,
			"ServiceAccount":         `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/serviceaccounts/{{ index . "metadata" "name"}}`,
			"ConfigMap":              `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/configmaps/{{ index . "metadata" "name"}}`,
			"ResourceQuota":          `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/resourcequotas/{{ index . "metadata" "name"}}`,
			"LimitRange":             `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/limitranges/{{ index . "metadata" "name"}}`,
		},
		"GET": {
			"Project":                `/oapi/v1/projects/{{ index . "metadata" "name"}}`,
			"RoleBinding":            `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/rolebindings/{{ index . "metadata" "name"}}`,
			"RoleBindingRestriction": `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/rolebindingrestrictions/{{ index . "metadata" "name"}}`,
			"Route":                  `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/routes/{{ index . "metadata" "name"}}`,
			"DeploymentConfig":       `/oapi/v1/namespaces/{{ index . "metadata" "namespace"}}/deploymentconfigs/{{ index . "metadata" "name"}}`,
			"PersistentVolumeClaim":  `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/persistentvolumeclaims/{{ index . "metadata" "name"}}`,
			"Service":                `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/services/{{ index . "metadata" "name"}}`,
			"Secret":                 `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/secrets/{{ index . "metadata" "name"}}`,
			"ServiceAccount":         `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/serviceaccounts/{{ index . "metadata" "name"}}`,
			"ConfigMap":              `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/configmaps/{{ index . "metadata" "name"}}`,
			"ResourceQuota":          `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/resourcequotas/{{ index . "metadata" "name"}}`,
			"LimitRange":             `/api/v1/namespaces/{{ index . "metadata" "namespace"}}/limitranges/{{ index . "metadata" "name"}}`,
		},
	}
)

// ApplyOptions contains options for connecting to the target API
type ApplyOptions struct {
	Config
	Overwrite bool
	Namespace string
}

func (a ApplyOptions) withNamespace(namespace string) ApplyOptions {
	return ApplyOptions{
		Config:    a.Config,
		Overwrite: a.Overwrite,
		Namespace: namespace,
	}
}

// Apply a given template structure to a target API
func Apply(source string, opts ApplyOptions) error {

	objects, err := parseObjects(source, opts.Namespace)
	if err != nil {
		return err
	}

	err = allKnownTypes(objects)
	if err != nil {
		return err
	}

	err = applyAll(objects, opts)
	if err != nil {
		return err
	}

	return nil
}

func applyAll(objects []map[interface{}]interface{}, opts ApplyOptions) error {
	for index, obj := range objects {
		_, err := apply(obj, "POST", opts)
		if err != nil {
			return err
		}
		if index == 0 {
			time.Sleep(time.Second * 2)
		}
	}
	return nil
}

func apply(object map[interface{}]interface{}, action string, opts ApplyOptions) (map[interface{}]interface{}, error) {
	body, err := yaml.Marshal(object)
	if err != nil {
		return nil, err
	}

	url, err := createURL(opts.MasterURL, action, object)
	if url == "" {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest(action, url, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/yaml")
	req.Header.Set("Content-Type", "application/yaml")
	req.Header.Set("Authorization", "Bearer "+opts.Token)

	// for debug only
	rb, _ := httputil.DumpRequest(req, true)
	if false {
		fmt.Println(string(rb))
	}

	client := http.DefaultClient
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	buf := new(bytes.Buffer)
	buf.ReadFrom(resp.Body)
	b := buf.Bytes()

	var respType map[interface{}]interface{}
	err = yaml.Unmarshal(b, &respType)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode == http.StatusConflict {
		if object[fieldKind] == valProjectRequest {
			return respType, nil
		}
		/*
			fmt.Println("Conflict-Update")
			resp, err := apply(object, "GET", opts)
			if err != nil {
				return nil, err
			}
			fmt.Println(resp)
			updateResourceVersion(resp, object)
			fmt.Println(object)
		*/
		return apply(object, "PUT", opts)
	}
	/*
		if resp.StatusCode == http.StatusForbidden && opts.Overwrite {

		} else
	*/
	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Unknown response:\n%v\n%v", *(*string)(unsafe.Pointer(&b)), string(rb))
	}

	fmt.Printf("%v %v %v in %v\n", action, respType[fieldKind], getName(respType), opts.Namespace)
	return respType, nil
}

func updateResourceVersion(source, target map[interface{}]interface{}) {
	if sourceMeta, sourceMetaFound := source[fieldMetadata].(map[interface{}]interface{}); sourceMetaFound {
		if sourceVersion, sourceVersionFound := sourceMeta[fieldResourceVersion]; sourceVersionFound {
			if targetMeta, targetMetaFound := target[fieldMetadata].(map[interface{}]interface{}); targetMetaFound {
				fmt.Println("setting v", sourceVersion, reflect.TypeOf(sourceVersion).Kind())
				targetMeta[fieldResourceVersion] = sourceVersion
			}
		}
	}
}

func getName(obj map[interface{}]interface{}) string {
	if meta, metaFound := obj[fieldMetadata].(map[interface{}]interface{}); metaFound {
		if name, nameFound := meta[fieldName].(string); nameFound {
			return name
		}
	}
	return ""
}

func parseObjects(source string, namespace string) ([]map[interface{}]interface{}, error) {
	var template map[interface{}]interface{}

	err := yaml.Unmarshal([]byte(source), &template)
	if err != nil {
		return nil, err
	}

	if template[fieldKind] == valTemplate || template[fieldKind] == valList {
		var ts []interface{}
		if template[fieldKind] == valTemplate {
			ts = template[fieldObjects].([]interface{})
		} else if template[fieldKind] == valList {
			ts = template[fieldItems].([]interface{})
		}
		var objs []map[interface{}]interface{}
		for _, obj := range ts {
			objs = append(objs, obj.(map[interface{}]interface{}))
		}
		if namespace != "" {
			for _, obj := range objs {
				if val, ok := obj[fieldMetadata].(map[interface{}]interface{}); ok {
					if _, ok := val[fieldNamespace]; !ok {
						val[fieldNamespace] = namespace
					}
				}
			}
		}

		return objs, nil
	}
	return []map[interface{}]interface{}{template}, nil
}

// TODO: a bit off now that there are multiple Action methods
func allKnownTypes(objects []map[interface{}]interface{}) error {
	m := multiError{}
	for _, obj := range objects {
		if _, ok := endpoints["POST"][obj[fieldKind].(string)]; !ok {
			m.Errors = append(m.Errors, fmt.Errorf("Unknown type: %v", obj[fieldKind]))
		}
	}
	if len(m.Errors) > 0 {
		return m
	}
	return nil
}

func createURL(hostURL, action string, object map[interface{}]interface{}) (string, error) {
	urlTemplate, found := endpoints[action][object[fieldKind].(string)]
	if !found {
		return "", nil
	}
	target, err := template.New("url").Parse(urlTemplate)
	if err != nil {
		return "", err
	}
	var buf bytes.Buffer
	err = target.Execute(&buf, object)
	if err != nil {
		return "", err
	}
	str := buf.String()
	return hostURL + str, nil
}
<|endoftext|>


<commit_before>// Functions for generating images of fractal sets
package fract

import (
	"image"
	"math/cmplx"
)

const MaxIterations = 10000

// Fractal image receiver
type Image interface {
	SetPixel(x, y, iterations int)
	Bounds() image.Rectangle
}

// Generate Mandelbrot set
func Mandelbrot(img Image, min, max complex128) {
	b := img.Bounds()
	dr := real(max-min) / float64(b.Dx())
	di := imag(max-min) / float64(b.Dy())

	ch := make(chan bool, b.Dx()*b.Dy())

	for x := 0; x < b.Dx(); x++ {
		for y := 0; y < b.Dy(); y++ {
			go func() {
				z := complex(0, 0)
				c := min + complex(float64(x)*dr, float64(y)*di)

				n := 0
				for ; n < MaxIterations; n++ {
					z = z*z + c
					if cmplx.Abs(z) > 2.0 {
						break
					}
				}

				img.SetPixel(x+b.Min.X, y+b.Min.Y, n)
				ch <- true
			}()
		}
	}

	// wait for all go routines to finish
	for i := 0; i < b.Dx()*b.Dy(); i++ {
		<-ch
	}
}
<commit_msg>Add ColorImage type<commit_after>// Functions for generating images of fractal sets
package fract

import (
	"image"
	"image/color"
	"math/cmplx"
)

const MaxIterations = 10000

// Fractal image receiver
type Image interface {
	SetPixel(x, y, iterations int)
	Bounds() image.Rectangle
}

// Generate Mandelbrot set
func Mandelbrot(img Image, min, max complex128) {
	b := img.Bounds()
	dr := real(max-min) / float64(b.Dx())
	di := imag(max-min) / float64(b.Dy())

	ch := make(chan bool, b.Dx()*b.Dy())

	for x := 0; x < b.Dx(); x++ {
		for y := 0; y < b.Dy(); y++ {
			go func() {
				z := complex(0, 0)
				c := min + complex(float64(x)*dr, float64(y)*di)

				n := 0
				for ; n < MaxIterations; n++ {
					z = z*z + c
					if cmplx.Abs(z) > 2.0 {
						break
					}
				}

				img.SetPixel(x+b.Min.X, y+b.Min.Y, n)
				ch <- true
			}()
		}
	}

	// wait for all go routines to finish
	for i := 0; i < b.Dx()*b.Dy(); i++ {
		<-ch
	}
}

type ColorImage struct {
	rgb image.RGBA
}

func (img *ColorImage) SetPixel(x, y, iterations int) {
	color := color.RGBA{128, 0, 0, 0}
	img.rgb.Set(x, y, color)
}

func (img *ColorImage) Bounds() image.Rectangle {
	return img.rgb.Bounds()
}
<|endoftext|>