<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package storagebackend

import (
	"time"

	"go.opentelemetry.io/otel/trace"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apiserver/pkg/server/egressselector"
	"k8s.io/apiserver/pkg/storage/etcd3"
	"k8s.io/apiserver/pkg/storage/value"
)

const (
	StorageTypeUnset = ""
	StorageTypeETCD2 = "etcd2"
	StorageTypeETCD3 = "etcd3"

	DefaultCompactInterval      = 5 * time.Minute
	DefaultDBMetricPollInterval = 30 * time.Second
	DefaultHealthcheckTimeout   = 2 * time.Second
)

// TransportConfig holds all connection related info,  i.e. equal TransportConfig means equal servers we talk to.
type TransportConfig struct {
	// ServerList is the list of storage servers to connect with.
	ServerList []string
	// TLS credentials
	KeyFile       string
	CertFile      string
	TrustedCAFile string
	// function to determine the egress dialer. (i.e. konnectivity server dialer)
	EgressLookup egressselector.Lookup
	// The TracerProvider can add tracing the connection
	TracerProvider *trace.TracerProvider
}

// Config is configuration for creating a storage backend.
type Config struct {
	// Type defines the type of storage backend. Default ("") is "etcd3".
	Type string
	// Prefix is the prefix to all keys passed to storage.Interface methods.
	Prefix string
	// Transport holds all connection related info, i.e. equal TransportConfig means equal servers we talk to.
	Transport TransportConfig
	// Paging indicates whether the server implementation should allow paging (if it is
	// supported). This is generally configured by feature gating, or by a specific
	// resource type not wishing to allow paging, and is not intended for end users to
	// set.
	Paging bool

	Codec runtime.Codec
	// EncodeVersioner is the same groupVersioner used to build the
	// storage encoder. Given a list of kinds the input object might belong
	// to, the EncodeVersioner outputs the gvk the object will be
	// converted to before persisted in etcd.
	EncodeVersioner runtime.GroupVersioner
	// Transformer allows the value to be transformed prior to persisting into etcd.
	Transformer value.Transformer

	// CompactionInterval is an interval of requesting compaction from apiserver.
	// If the value is 0, no compaction will be issued.
	CompactionInterval time.Duration
	// CountMetricPollPeriod specifies how often should count metric be updated
	CountMetricPollPeriod time.Duration
	// DBMetricPollInterval specifies how often should storage backend metric be updated.
	DBMetricPollInterval time.Duration
	// HealthcheckTimeout specifies the timeout used when checking health
	HealthcheckTimeout time.Duration

	LeaseManagerConfig etcd3.LeaseManagerConfig
}

func NewDefaultConfig(prefix string, codec runtime.Codec) *Config {
	return &Config{
		Paging:               true,
		Prefix:               prefix,
		Codec:                codec,
		CompactionInterval:   DefaultCompactInterval,
		DBMetricPollInterval: DefaultDBMetricPollInterval,
		HealthcheckTimeout:   DefaultHealthcheckTimeout,
		LeaseManagerConfig:   etcd3.NewDefaultLeaseManagerConfig(),
	}
}
<commit_msg>UPSTREAM: <carry>: Bug 1852056: change etcd health check timeout to 10s<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package storagebackend

import (
	"time"

	"go.opentelemetry.io/otel/trace"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apiserver/pkg/server/egressselector"
	"k8s.io/apiserver/pkg/storage/etcd3"
	"k8s.io/apiserver/pkg/storage/value"
)

const (
	StorageTypeUnset = ""
	StorageTypeETCD2 = "etcd2"
	StorageTypeETCD3 = "etcd3"

	DefaultCompactInterval      = 5 * time.Minute
	DefaultDBMetricPollInterval = 30 * time.Second
	DefaultHealthcheckTimeout   = 10 * time.Second
)

// TransportConfig holds all connection related info,  i.e. equal TransportConfig means equal servers we talk to.
type TransportConfig struct {
	// ServerList is the list of storage servers to connect with.
	ServerList []string
	// TLS credentials
	KeyFile       string
	CertFile      string
	TrustedCAFile string
	// function to determine the egress dialer. (i.e. konnectivity server dialer)
	EgressLookup egressselector.Lookup
	// The TracerProvider can add tracing the connection
	TracerProvider *trace.TracerProvider
}

// Config is configuration for creating a storage backend.
type Config struct {
	// Type defines the type of storage backend. Default ("") is "etcd3".
	Type string
	// Prefix is the prefix to all keys passed to storage.Interface methods.
	Prefix string
	// Transport holds all connection related info, i.e. equal TransportConfig means equal servers we talk to.
	Transport TransportConfig
	// Paging indicates whether the server implementation should allow paging (if it is
	// supported). This is generally configured by feature gating, or by a specific
	// resource type not wishing to allow paging, and is not intended for end users to
	// set.
	Paging bool

	Codec runtime.Codec
	// EncodeVersioner is the same groupVersioner used to build the
	// storage encoder. Given a list of kinds the input object might belong
	// to, the EncodeVersioner outputs the gvk the object will be
	// converted to before persisted in etcd.
	EncodeVersioner runtime.GroupVersioner
	// Transformer allows the value to be transformed prior to persisting into etcd.
	Transformer value.Transformer

	// CompactionInterval is an interval of requesting compaction from apiserver.
	// If the value is 0, no compaction will be issued.
	CompactionInterval time.Duration
	// CountMetricPollPeriod specifies how often should count metric be updated
	CountMetricPollPeriod time.Duration
	// DBMetricPollInterval specifies how often should storage backend metric be updated.
	DBMetricPollInterval time.Duration
	// HealthcheckTimeout specifies the timeout used when checking health
	HealthcheckTimeout time.Duration

	LeaseManagerConfig etcd3.LeaseManagerConfig
}

func NewDefaultConfig(prefix string, codec runtime.Codec) *Config {
	return &Config{
		Paging:               true,
		Prefix:               prefix,
		Codec:                codec,
		CompactionInterval:   DefaultCompactInterval,
		DBMetricPollInterval: DefaultDBMetricPollInterval,
		HealthcheckTimeout:   DefaultHealthcheckTimeout,
		LeaseManagerConfig:   etcd3.NewDefaultLeaseManagerConfig(),
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package all

import (
	"io"
	"os"
	"reflect"

	jujucmd "github.com/juju/cmd"
	"github.com/juju/errors"
	"gopkg.in/juju/names.v2"

	"github.com/juju/juju/api/base"
	"github.com/juju/juju/apiserver/charmrevisionupdater"
	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/common/apihttp"
	"github.com/juju/juju/cmd/juju/charmcmd"
	"github.com/juju/juju/cmd/juju/commands"
	"github.com/juju/juju/cmd/modelcmd"
	"github.com/juju/juju/resource"
	"github.com/juju/juju/resource/api"
	"github.com/juju/juju/resource/api/client"
	privateapi "github.com/juju/juju/resource/api/private"
	internalclient "github.com/juju/juju/resource/api/private/client"
	internalserver "github.com/juju/juju/resource/api/private/server"
	"github.com/juju/juju/resource/api/server"
	"github.com/juju/juju/resource/cmd"
	"github.com/juju/juju/resource/context"
	contextcmd "github.com/juju/juju/resource/context/cmd"
	"github.com/juju/juju/resource/resourceadapters"
	corestate "github.com/juju/juju/state"
	unitercontext "github.com/juju/juju/worker/uniter/runner/context"
	"github.com/juju/juju/worker/uniter/runner/jujuc"
)

// resources exposes the registration methods needed
// for the top-level component machinery.
type resources struct{}

// RegisterForServer is the top-level registration method
// for the component in a jujud context.
func (r resources) registerForServer() error {
	r.registerState()
	r.registerAgentWorkers()
	r.registerPublicFacade()
	r.registerHookContext()
	return nil
}

// RegisterForClient is the top-level registration method
// for the component in a "juju" command context.
func (r resources) registerForClient() error {
	r.registerPublicCommands()

	// needed for help-tool
	r.registerHookContextCommands()
	return nil
}

// registerPublicFacade adds the resources public API facade
// to the API server.
func (r resources) registerPublicFacade() {
	if !markRegistered(resource.ComponentName, "public-facade") {
		return
	}

	// NOTE: facade is also defined in api/facadeversions.go.
	common.RegisterStandardFacade(
		resource.FacadeName,
		server.Version,
		resourceadapters.NewPublicFacade,
	)

	common.RegisterAPIModelEndpoint(api.HTTPEndpointPattern, apihttp.HandlerSpec{
		Constraints: apihttp.HandlerConstraints{
			AuthKinds:           []string{names.UserTagKind, names.MachineTagKind},
			StrictValidation:    true,
			ControllerModelOnly: false,
		},
		NewHandler: resourceadapters.NewApplicationHandler,
	})
}

// resourcesAPIClient adds a Close() method to the resources public API client.
type resourcesAPIClient struct {
	*client.Client
	closeConnFunc func() error
}

// Close implements io.Closer.
func (client resourcesAPIClient) Close() error {
	return client.closeConnFunc()
}

// registerAgentWorkers adds the resources workers to the agents.
func (r resources) registerAgentWorkers() {
	if !markRegistered(resource.ComponentName, "agent-workers") {
		return
	}

	charmrevisionupdater.RegisterLatestCharmHandler("resources", resourceadapters.NewLatestCharmHandler)
}

// registerState registers the state functionality for resources.
func (resources) registerState() {
	if !markRegistered(resource.ComponentName, "state") {
		return
	}

	corestate.SetResourcesComponent(resourceadapters.NewResourceState)
	corestate.SetResourcesPersistence(resourceadapters.NewResourcePersistence)
	corestate.RegisterCleanupHandler(corestate.CleanupKindResourceBlob, resourceadapters.CleanUpBlob)
}

// registerPublicCommands adds the resources-related commands
// to the "juju" supercommand.
func (r resources) registerPublicCommands() {
	if !markRegistered(resource.ComponentName, "public-commands") {
		return
	}

	charmcmd.RegisterSubCommand(cmd.NewListCharmResourcesCommand())

	commands.RegisterEnvCommand(func() modelcmd.ModelCommand {
		return cmd.NewUploadCommand(cmd.UploadDeps{
			NewClient: func(c *cmd.UploadCommand) (cmd.UploadClient, error) {
				apiRoot, err := c.NewAPIRoot()
				if err != nil {
					return nil, errors.Trace(err)
				}
				return resourceadapters.NewAPIClient(apiRoot)
			},
			OpenResource: func(s string) (cmd.ReadSeekCloser, error) {
				return os.Open(s)
			},
		})

	})

	commands.RegisterEnvCommand(func() modelcmd.ModelCommand {
		return cmd.NewShowServiceCommand(cmd.ShowServiceDeps{
			NewClient: func(c *cmd.ShowServiceCommand) (cmd.ShowServiceClient, error) {
				apiRoot, err := c.NewAPIRoot()
				if err != nil {
					return nil, errors.Trace(err)
				}
				return resourceadapters.NewAPIClient(apiRoot)
			},
		})
	})
}

// TODO(katco): This seems to be common across components. Pop up a
// level and genericize?
func (r resources) registerHookContext() {
	if markRegistered(resource.ComponentName, "hook-context") == false {
		return
	}

	unitercontext.RegisterComponentFunc(
		resource.ComponentName,
		func(config unitercontext.ComponentConfig) (jujuc.ContextComponent, error) {
			unitID := names.NewUnitTag(config.UnitName).String()
			hctxClient, err := r.newUnitFacadeClient(unitID, config.APICaller)
			if err != nil {
				return nil, errors.Trace(err)
			}
			// TODO(ericsnow) Pass the unit's tag through to the component?
			return context.NewContextAPI(hctxClient, config.DataDir), nil
		},
	)

	r.registerHookContextCommands()
	r.registerHookContextFacade()
	r.registerUnitDownloadEndpoint()
}

func (r resources) registerHookContextCommands() {
	if markRegistered(resource.ComponentName, "hook-context-commands") == false {
		return
	}

	jujuc.RegisterCommand(
		contextcmd.GetCmdName,
		func(ctx jujuc.Context) (jujucmd.Command, error) {
			compCtx, err := ctx.Component(resource.ComponentName)
			if err != nil {
				return nil, errors.Trace(err)
			}
			cmd, err := contextcmd.NewGetCmd(compCtx)
			if err != nil {
				return nil, errors.Trace(err)
			}
			return cmd, nil
		},
	)
}

func (r resources) registerHookContextFacade() {
	common.RegisterHookContextFacade(
		context.HookContextFacade,
		internalserver.FacadeVersion,
		r.newHookContextFacade,
		reflect.TypeOf(&internalserver.UnitFacade{}),
	)

}

func (r resources) registerUnitDownloadEndpoint() {
	common.RegisterAPIModelEndpoint(privateapi.HTTPEndpointPattern, apihttp.HandlerSpec{
		Constraints: apihttp.HandlerConstraints{
			AuthKinds:           []string{names.UnitTagKind},
			StrictValidation:    true,
			ControllerModelOnly: false,
		},
		NewHandler: resourceadapters.NewDownloadHandler,
	})
}

// resourcesUnitDatastore is a shim to elide serviceName from
// ListResources.
type resourcesUnitDataStore struct {
	resources corestate.Resources
	unit      *corestate.Unit
}

// ListResources implements resource/api/private/server.UnitDataStore.
func (ds *resourcesUnitDataStore) ListResources() (resource.ServiceResources, error) {
	return ds.resources.ListResources(ds.unit.ApplicationName())
}

// GetResource implements resource/api/private/server.UnitDataStore.
func (ds *resourcesUnitDataStore) GetResource(name string) (resource.Resource, error) {
	return ds.resources.GetResource(ds.unit.ApplicationName(), name)
}

// OpenResource implements resource/api/private/server.UnitDataStore.
func (ds *resourcesUnitDataStore) OpenResource(name string) (resource.Resource, io.ReadCloser, error) {
	return ds.resources.OpenResourceForUniter(ds.unit, name)
}

func (r resources) newHookContextFacade(st *corestate.State, unit *corestate.Unit) (interface{}, error) {
	res, err := st.Resources()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return internalserver.NewUnitFacade(&resourcesUnitDataStore{res, unit}), nil
}

func (r resources) newUnitFacadeClient(unitName string, caller base.APICaller) (context.APIClient, error) {

	facadeCaller := base.NewFacadeCallerForVersion(caller, context.HookContextFacade, internalserver.FacadeVersion)
	httpClient, err := caller.HTTPClient()
	if err != nil {
		return nil, errors.Trace(err)
	}
	unitHTTPClient := internalclient.NewUnitHTTPClient(httpClient, unitName)

	return internalclient.NewUnitFacadeClient(facadeCaller, unitHTTPClient), nil
}
<commit_msg>component/all: Removed some confusing unused code<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package all

import (
	"os"
	"reflect"

	jujucmd "github.com/juju/cmd"
	"github.com/juju/errors"
	"gopkg.in/juju/names.v2"

	"github.com/juju/juju/api/base"
	"github.com/juju/juju/apiserver/charmrevisionupdater"
	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/common/apihttp"
	"github.com/juju/juju/cmd/juju/charmcmd"
	"github.com/juju/juju/cmd/juju/commands"
	"github.com/juju/juju/cmd/modelcmd"
	"github.com/juju/juju/resource"
	"github.com/juju/juju/resource/api"
	"github.com/juju/juju/resource/api/client"
	privateapi "github.com/juju/juju/resource/api/private"
	internalclient "github.com/juju/juju/resource/api/private/client"
	internalserver "github.com/juju/juju/resource/api/private/server"
	"github.com/juju/juju/resource/api/server"
	"github.com/juju/juju/resource/cmd"
	"github.com/juju/juju/resource/context"
	contextcmd "github.com/juju/juju/resource/context/cmd"
	"github.com/juju/juju/resource/resourceadapters"
	corestate "github.com/juju/juju/state"
	unitercontext "github.com/juju/juju/worker/uniter/runner/context"
	"github.com/juju/juju/worker/uniter/runner/jujuc"
)

// resources exposes the registration methods needed
// for the top-level component machinery.
type resources struct{}

// RegisterForServer is the top-level registration method
// for the component in a jujud context.
func (r resources) registerForServer() error {
	r.registerState()
	r.registerAgentWorkers()
	r.registerPublicFacade()
	r.registerHookContext()
	return nil
}

// RegisterForClient is the top-level registration method
// for the component in a "juju" command context.
func (r resources) registerForClient() error {
	r.registerPublicCommands()

	// needed for help-tool
	r.registerHookContextCommands()
	return nil
}

// registerPublicFacade adds the resources public API facade
// to the API server.
func (r resources) registerPublicFacade() {
	if !markRegistered(resource.ComponentName, "public-facade") {
		return
	}

	// NOTE: facade is also defined in api/facadeversions.go.
	common.RegisterStandardFacade(
		resource.FacadeName,
		server.Version,
		resourceadapters.NewPublicFacade,
	)

	common.RegisterAPIModelEndpoint(api.HTTPEndpointPattern, apihttp.HandlerSpec{
		Constraints: apihttp.HandlerConstraints{
			AuthKinds:           []string{names.UserTagKind, names.MachineTagKind},
			StrictValidation:    true,
			ControllerModelOnly: false,
		},
		NewHandler: resourceadapters.NewApplicationHandler,
	})
}

// resourcesAPIClient adds a Close() method to the resources public API client.
type resourcesAPIClient struct {
	*client.Client
	closeConnFunc func() error
}

// Close implements io.Closer.
func (client resourcesAPIClient) Close() error {
	return client.closeConnFunc()
}

// registerAgentWorkers adds the resources workers to the agents.
func (r resources) registerAgentWorkers() {
	if !markRegistered(resource.ComponentName, "agent-workers") {
		return
	}

	charmrevisionupdater.RegisterLatestCharmHandler("resources", resourceadapters.NewLatestCharmHandler)
}

// registerState registers the state functionality for resources.
func (resources) registerState() {
	if !markRegistered(resource.ComponentName, "state") {
		return
	}

	corestate.SetResourcesComponent(resourceadapters.NewResourceState)
	corestate.SetResourcesPersistence(resourceadapters.NewResourcePersistence)
	corestate.RegisterCleanupHandler(corestate.CleanupKindResourceBlob, resourceadapters.CleanUpBlob)
}

// registerPublicCommands adds the resources-related commands
// to the "juju" supercommand.
func (r resources) registerPublicCommands() {
	if !markRegistered(resource.ComponentName, "public-commands") {
		return
	}

	charmcmd.RegisterSubCommand(cmd.NewListCharmResourcesCommand())

	commands.RegisterEnvCommand(func() modelcmd.ModelCommand {
		return cmd.NewUploadCommand(cmd.UploadDeps{
			NewClient: func(c *cmd.UploadCommand) (cmd.UploadClient, error) {
				apiRoot, err := c.NewAPIRoot()
				if err != nil {
					return nil, errors.Trace(err)
				}
				return resourceadapters.NewAPIClient(apiRoot)
			},
			OpenResource: func(s string) (cmd.ReadSeekCloser, error) {
				return os.Open(s)
			},
		})

	})

	commands.RegisterEnvCommand(func() modelcmd.ModelCommand {
		return cmd.NewShowServiceCommand(cmd.ShowServiceDeps{
			NewClient: func(c *cmd.ShowServiceCommand) (cmd.ShowServiceClient, error) {
				apiRoot, err := c.NewAPIRoot()
				if err != nil {
					return nil, errors.Trace(err)
				}
				return resourceadapters.NewAPIClient(apiRoot)
			},
		})
	})
}

// TODO(katco): This seems to be common across components. Pop up a
// level and genericize?
func (r resources) registerHookContext() {
	if markRegistered(resource.ComponentName, "hook-context") == false {
		return
	}

	unitercontext.RegisterComponentFunc(
		resource.ComponentName,
		func(config unitercontext.ComponentConfig) (jujuc.ContextComponent, error) {
			unitID := names.NewUnitTag(config.UnitName).String()
			hctxClient, err := r.newUnitFacadeClient(unitID, config.APICaller)
			if err != nil {
				return nil, errors.Trace(err)
			}
			// TODO(ericsnow) Pass the unit's tag through to the component?
			return context.NewContextAPI(hctxClient, config.DataDir), nil
		},
	)

	r.registerHookContextCommands()
	r.registerHookContextFacade()
	r.registerUnitDownloadEndpoint()
}

func (r resources) registerHookContextCommands() {
	if markRegistered(resource.ComponentName, "hook-context-commands") == false {
		return
	}

	jujuc.RegisterCommand(
		contextcmd.GetCmdName,
		func(ctx jujuc.Context) (jujucmd.Command, error) {
			compCtx, err := ctx.Component(resource.ComponentName)
			if err != nil {
				return nil, errors.Trace(err)
			}
			cmd, err := contextcmd.NewGetCmd(compCtx)
			if err != nil {
				return nil, errors.Trace(err)
			}
			return cmd, nil
		},
	)
}

func (r resources) registerHookContextFacade() {
	common.RegisterHookContextFacade(
		context.HookContextFacade,
		internalserver.FacadeVersion,
		r.newHookContextFacade,
		reflect.TypeOf(&internalserver.UnitFacade{}),
	)

}

func (r resources) registerUnitDownloadEndpoint() {
	common.RegisterAPIModelEndpoint(privateapi.HTTPEndpointPattern, apihttp.HandlerSpec{
		Constraints: apihttp.HandlerConstraints{
			AuthKinds:           []string{names.UnitTagKind},
			StrictValidation:    true,
			ControllerModelOnly: false,
		},
		NewHandler: resourceadapters.NewDownloadHandler,
	})
}

// resourcesUnitDatastore is a shim to elide serviceName from
// ListResources.
type resourcesUnitDataStore struct {
	resources corestate.Resources
	unit      *corestate.Unit
}

// ListResources implements resource/api/private/server.UnitDataStore.
func (ds *resourcesUnitDataStore) ListResources() (resource.ServiceResources, error) {
	return ds.resources.ListResources(ds.unit.ApplicationName())
}

func (r resources) newHookContextFacade(st *corestate.State, unit *corestate.Unit) (interface{}, error) {
	res, err := st.Resources()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return internalserver.NewUnitFacade(&resourcesUnitDataStore{res, unit}), nil
}

func (r resources) newUnitFacadeClient(unitName string, caller base.APICaller) (context.APIClient, error) {

	facadeCaller := base.NewFacadeCallerForVersion(caller, context.HookContextFacade, internalserver.FacadeVersion)
	httpClient, err := caller.HTTPClient()
	if err != nil {
		return nil, errors.Trace(err)
	}
	unitHTTPClient := internalclient.NewUnitHTTPClient(httpClient, unitName)

	return internalclient.NewUnitFacadeClient(facadeCaller, unitHTTPClient), nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package all

import (
	"reflect"
	"time"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/names"
	"gopkg.in/juju/charm.v5"

	"github.com/juju/juju/api/base"
	apiserverclient "github.com/juju/juju/apiserver/client"
	"github.com/juju/juju/apiserver/common"
	cmdstatus "github.com/juju/juju/cmd/juju/status"
	"github.com/juju/juju/cmd/jujud/agent/unit"
	cmdutil "github.com/juju/juju/cmd/jujud/util"
	"github.com/juju/juju/state"
	"github.com/juju/juju/worker"
	"github.com/juju/juju/worker/dependency"
	"github.com/juju/juju/worker/uniter/runner"
	"github.com/juju/juju/worker/uniter/runner/jujuc"
	"github.com/juju/juju/workload"
	"github.com/juju/juju/workload/api/client"
	"github.com/juju/juju/workload/api/server"
	"github.com/juju/juju/workload/context"
	workloadstate "github.com/juju/juju/workload/state"
	"github.com/juju/juju/workload/status"
	"github.com/juju/juju/workload/workers"
)

type workloads struct{}

func (c workloads) registerForServer() error {
	c.registerState()
	handlers := c.registerUnitWorkers()
	c.registerHookContext(handlers)
	c.registerUnitStatus()
	return nil
}

func (workloads) registerForClient() error {
	cmdstatus.RegisterUnitStatusFormatter(workload.ComponentName, status.Format)
	return nil
}

func (c workloads) registerHookContext(handlers map[string]*workers.EventHandlers) {
	if !markRegistered(workload.ComponentName, "hook-context") {
		return
	}

	runner.RegisterComponentFunc(workload.ComponentName,
		func(unit string, caller base.APICaller) (jujuc.ContextComponent, error) {
			var addEvents func(...workload.Event)
			if unitEventHandler, ok := handlers[unit]; ok {
				addEvents = unitEventHandler.AddEvents
			}
			hctxClient := c.newHookContextAPIClient(caller)
			// TODO(ericsnow) Pass the unit's tag through to the component?
			component, err := context.NewContextAPI(hctxClient, addEvents)
			if err != nil {
				return nil, errors.Trace(err)
			}
			return component, nil
		},
	)

	c.registerHookContextCommands()
	c.registerHookContextFacade()
}

func (c workloads) newHookContextAPIClient(caller base.APICaller) context.APIClient {
	facadeCaller := base.NewFacadeCallerForVersion(caller, workload.ComponentName, 0)
	return client.NewHookContextClient(facadeCaller)
}

func (workloads) registerHookContextFacade() {

	newHookContextApi := func(st *state.State, unit *state.Unit) (interface{}, error) {
		if st == nil {
			return nil, errors.NewNotValid(nil, "st is nil")
		}

		up, err := st.UnitWorkloads(unit)
		if err != nil {
			return nil, errors.Trace(err)
		}
		return server.NewHookContextAPI(up), nil
	}

	common.RegisterHookContextFacade(
		workload.ComponentName,
		0,
		newHookContextApi,
		reflect.TypeOf(&server.HookContextAPI{}),
	)
}

type workloadsHookContext struct {
	jujuc.Context
}

// Component implements context.HookContext.
func (c workloadsHookContext) Component(name string) (context.Component, error) {
	found, err := c.Context.Component(name)
	if err != nil {
		return nil, errors.Trace(err)
	}
	compCtx, ok := found.(context.Component)
	if !ok && found != nil {
		return nil, errors.Errorf("wrong component context type registered: %T", found)
	}
	return compCtx, nil
}

func (workloads) registerHookContextCommands() {
	if !markRegistered(workload.ComponentName, "hook-context-commands") {
		return
	}

	name := context.TrackCommandInfo.Name
	jujuc.RegisterCommand(name, func(ctx jujuc.Context) cmd.Command {
		compCtx := workloadsHookContext{ctx}
		cmd, err := context.NewWorkloadTrackCommand(compCtx)
		if err != nil {
			// TODO(ericsnow) Return an error instead.
			panic(err)
		}
		return cmd
	})

	name = context.LaunchCommandInfo.Name
	jujuc.RegisterCommand(name, func(ctx jujuc.Context) cmd.Command {
		compCtx := workloadsHookContext{ctx}
		cmd, err := context.NewWorkloadLaunchCommand(compCtx)
		if err != nil {
			panic(err)
		}
		return cmd
	})

	name = context.InfoCommandInfo.Name
	jujuc.RegisterCommand(name, func(ctx jujuc.Context) cmd.Command {
		compCtx := workloadsHookContext{ctx}
		cmd, err := context.NewWorkloadInfoCommand(compCtx)
		if err != nil {
			panic(err)
		}
		return cmd
	})
}

// TODO(ericsnow) Use a watcher instead of passing around the event handlers?

func (c workloads) registerUnitWorkers() map[string]*workers.EventHandlers {
	if !markRegistered(workload.ComponentName, "workers") {
		return nil
	}

	// TODO(ericsnow) There should only be one...
	unitEventHandlers := make(map[string]*workers.EventHandlers)

	handlerFuncs := []func([]workload.Event, context.APIClient, workers.Runner) error{
		workers.StatusEventHandler,
	}

	newManifold := func(config unit.ManifoldsConfig) (dependency.Manifold, error) {
		// At this point no workload workers are running for the unit.

		unitName := config.Agent.CurrentConfig().Tag().String()
		if unitHandler, ok := unitEventHandlers[unitName]; ok {
			// The worker must have restarted.
			// TODO(ericsnow) Could cause panics?
			unitHandler.Close()
		}

		unitHandler := workers.NewEventHandlers()
		for _, handlerFunc := range handlerFuncs {
			unitHandler.RegisterHandler(handlerFunc)
		}
		unitEventHandlers[unitName] = unitHandler

		manifold, err := c.newUnitManifold(unitHandler)
		if err != nil {
			return manifold, errors.Trace(err)
		}
		return manifold, nil
	}
	err := unit.RegisterManifold(workload.ComponentName, newManifold)
	if err != nil {
		panic(err)
	}

	return unitEventHandlers
}

func (c workloads) newUnitManifold(unitHandler *workers.EventHandlers) (dependency.Manifold, error) {
	manifold := dependency.Manifold{
		Inputs: []string{unit.APICallerName},
	}
	manifold.Start = func(getResource dependency.GetResourceFunc) (worker.Worker, error) {
		var caller base.APICaller
		err := getResource(unit.APICallerName, &caller)
		if err != nil {
			return nil, errors.Trace(err)
		}
		apiClient := c.newHookContextAPIClient(caller)

		engine, err := dependency.NewEngine(dependency.EngineConfig{
			IsFatal:       cmdutil.IsFatal,
			MoreImportant: func(_ error, worst error) error { return worst },
			ErrorDelay:    3 * time.Second,
			BounceDelay:   10 * time.Millisecond,
		})
		if err != nil {
			return nil, errors.Trace(err)
		}

		var runner worker.Runner // TODO(ericsnow) Wrap engine in a runner.
		// TODO(ericsnow) Provide the runner as a resource.
		unitHandler.Init(apiClient, runner) // TODO(ericsnow) Eliminate this...

		err = engine.Install("events", dependency.Manifold{
			Inputs: []string{},
			Start: func(dependency.GetResourceFunc) (worker.Worker, error) {
				// Pull all existing from State (via API) and add an event for each.
				hctx, err := context.NewContextAPI(apiClient, unitHandler.AddEvents)
				if err != nil {
					return nil, errors.Trace(err)
				}
				events, err := workers.InitialEvents(hctx)
				if err != nil {
					return nil, errors.Trace(err)
				}

				worker, err := unitHandler.NewWorker()
				if err == nil {
					return nil, errors.Trace(err)
				}

				// These must be added *after* the worker is started.
				unitHandler.AddEvents(events...)

				return worker, nil
			},
			Output: func(in worker.Worker, out interface{}) error {
				// TODO(ericsnow) provide the runner
				return nil
			},
		})
		if err == nil {
			return nil, errors.Trace(err)
		}

		err = engine.Install("apiclient", dependency.Manifold{
			Inputs: []string{},
			Start: func(dependency.GetResourceFunc) (worker.Worker, error) {
				loop := func(<-chan struct{}) error { return nil }
				return worker.NewSimpleWorker(loop), nil
			},
			Output: func(in worker.Worker, out interface{}) error {
				// TODO(ericsnow) provide the APICaller
				return nil
			},
		})
		if err == nil {
			return nil, errors.Trace(err)
		}

		return engine, nil
	}
	return manifold, nil
}

func (workloads) registerState() {
	// TODO(ericsnow) Use a more general registration mechanism.
	//state.RegisterMultiEnvCollections(persistence.Collections...)

	newUnitWorkloads := func(persist state.Persistence, unit names.UnitTag, getMetadata func() (*charm.Meta, error)) (state.UnitWorkloads, error) {
		return workloadstate.NewUnitWorkloads(persist, unit, getMetadata), nil
	}
	state.SetWorkloadsComponent(newUnitWorkloads)
}

func (workloads) registerUnitStatus() {
	apiserverclient.RegisterStatusProviderForUnits(workload.ComponentName,
		func(unit *state.Unit) (interface{}, error) {
			up, err := unit.Workloads()
			if err != nil {
				return nil, err
			}
			workloads, err := up.List()
			if err != nil {
				return nil, err
			}
			return status.UnitStatus(workloads)
		})
}
<commit_msg>Add a logger.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package all

import (
	"reflect"
	"time"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/names"
	"gopkg.in/juju/charm.v5"

	"github.com/juju/juju/api/base"
	apiserverclient "github.com/juju/juju/apiserver/client"
	"github.com/juju/juju/apiserver/common"
	cmdstatus "github.com/juju/juju/cmd/juju/status"
	"github.com/juju/juju/cmd/jujud/agent/unit"
	cmdutil "github.com/juju/juju/cmd/jujud/util"
	"github.com/juju/juju/state"
	"github.com/juju/juju/worker"
	"github.com/juju/juju/worker/dependency"
	"github.com/juju/juju/worker/uniter/runner"
	"github.com/juju/juju/worker/uniter/runner/jujuc"
	"github.com/juju/juju/workload"
	"github.com/juju/juju/workload/api/client"
	"github.com/juju/juju/workload/api/server"
	"github.com/juju/juju/workload/context"
	workloadstate "github.com/juju/juju/workload/state"
	"github.com/juju/juju/workload/status"
	"github.com/juju/juju/workload/workers"
)

var workloadsLogger = loggo.GetLogger("component.all.workload")

type workloads struct{}

func (c workloads) registerForServer() error {
	c.registerState()
	handlers := c.registerUnitWorkers()
	c.registerHookContext(handlers)
	c.registerUnitStatus()
	return nil
}

func (workloads) registerForClient() error {
	cmdstatus.RegisterUnitStatusFormatter(workload.ComponentName, status.Format)
	return nil
}

func (c workloads) registerHookContext(handlers map[string]*workers.EventHandlers) {
	if !markRegistered(workload.ComponentName, "hook-context") {
		return
	}

	runner.RegisterComponentFunc(workload.ComponentName,
		func(unit string, caller base.APICaller) (jujuc.ContextComponent, error) {
			var addEvents func(...workload.Event)
			if unitEventHandler, ok := handlers[unit]; ok {
				addEvents = unitEventHandler.AddEvents
			}
			hctxClient := c.newHookContextAPIClient(caller)
			// TODO(ericsnow) Pass the unit's tag through to the component?
			component, err := context.NewContextAPI(hctxClient, addEvents)
			if err != nil {
				return nil, errors.Trace(err)
			}
			return component, nil
		},
	)

	c.registerHookContextCommands()
	c.registerHookContextFacade()
}

func (c workloads) newHookContextAPIClient(caller base.APICaller) context.APIClient {
	facadeCaller := base.NewFacadeCallerForVersion(caller, workload.ComponentName, 0)
	return client.NewHookContextClient(facadeCaller)
}

func (workloads) registerHookContextFacade() {

	newHookContextApi := func(st *state.State, unit *state.Unit) (interface{}, error) {
		if st == nil {
			return nil, errors.NewNotValid(nil, "st is nil")
		}

		up, err := st.UnitWorkloads(unit)
		if err != nil {
			return nil, errors.Trace(err)
		}
		return server.NewHookContextAPI(up), nil
	}

	common.RegisterHookContextFacade(
		workload.ComponentName,
		0,
		newHookContextApi,
		reflect.TypeOf(&server.HookContextAPI{}),
	)
}

type workloadsHookContext struct {
	jujuc.Context
}

// Component implements context.HookContext.
func (c workloadsHookContext) Component(name string) (context.Component, error) {
	found, err := c.Context.Component(name)
	if err != nil {
		return nil, errors.Trace(err)
	}
	compCtx, ok := found.(context.Component)
	if !ok && found != nil {
		return nil, errors.Errorf("wrong component context type registered: %T", found)
	}
	return compCtx, nil
}

func (workloads) registerHookContextCommands() {
	if !markRegistered(workload.ComponentName, "hook-context-commands") {
		return
	}

	name := context.TrackCommandInfo.Name
	jujuc.RegisterCommand(name, func(ctx jujuc.Context) cmd.Command {
		compCtx := workloadsHookContext{ctx}
		cmd, err := context.NewWorkloadTrackCommand(compCtx)
		if err != nil {
			// TODO(ericsnow) Return an error instead.
			panic(err)
		}
		return cmd
	})

	name = context.LaunchCommandInfo.Name
	jujuc.RegisterCommand(name, func(ctx jujuc.Context) cmd.Command {
		compCtx := workloadsHookContext{ctx}
		cmd, err := context.NewWorkloadLaunchCommand(compCtx)
		if err != nil {
			panic(err)
		}
		return cmd
	})

	name = context.InfoCommandInfo.Name
	jujuc.RegisterCommand(name, func(ctx jujuc.Context) cmd.Command {
		compCtx := workloadsHookContext{ctx}
		cmd, err := context.NewWorkloadInfoCommand(compCtx)
		if err != nil {
			panic(err)
		}
		return cmd
	})
}

// TODO(ericsnow) Use a watcher instead of passing around the event handlers?

func (c workloads) registerUnitWorkers() map[string]*workers.EventHandlers {
	if !markRegistered(workload.ComponentName, "workers") {
		return nil
	}

	// TODO(ericsnow) There should only be one...
	unitEventHandlers := make(map[string]*workers.EventHandlers)

	handlerFuncs := []func([]workload.Event, context.APIClient, workers.Runner) error{
		workers.StatusEventHandler,
	}

	newManifold := func(config unit.ManifoldsConfig) (dependency.Manifold, error) {
		// At this point no workload workers are running for the unit.

		unitName := config.Agent.CurrentConfig().Tag().String()
		if unitHandler, ok := unitEventHandlers[unitName]; ok {
			// The worker must have restarted.
			// TODO(ericsnow) Could cause panics?
			unitHandler.Close()
		}

		unitHandler := workers.NewEventHandlers()
		for _, handlerFunc := range handlerFuncs {
			unitHandler.RegisterHandler(handlerFunc)
		}
		unitEventHandlers[unitName] = unitHandler

		manifold, err := c.newUnitManifold(unitHandler)
		if err != nil {
			return manifold, errors.Trace(err)
		}
		return manifold, nil
	}
	err := unit.RegisterManifold(workload.ComponentName, newManifold)
	if err != nil {
		panic(err)
	}

	return unitEventHandlers
}

func (c workloads) newUnitManifold(unitHandler *workers.EventHandlers) (dependency.Manifold, error) {
	manifold := dependency.Manifold{
		Inputs: []string{unit.APICallerName},
	}
	manifold.Start = func(getResource dependency.GetResourceFunc) (worker.Worker, error) {
		var caller base.APICaller
		err := getResource(unit.APICallerName, &caller)
		if err != nil {
			return nil, errors.Trace(err)
		}
		apiClient := c.newHookContextAPIClient(caller)

		engine, err := dependency.NewEngine(dependency.EngineConfig{
			IsFatal:       cmdutil.IsFatal,
			MoreImportant: func(_ error, worst error) error { return worst },
			ErrorDelay:    3 * time.Second,
			BounceDelay:   10 * time.Millisecond,
		})
		if err != nil {
			return nil, errors.Trace(err)
		}

		var runner worker.Runner // TODO(ericsnow) Wrap engine in a runner.
		// TODO(ericsnow) Provide the runner as a resource.
		unitHandler.Init(apiClient, runner) // TODO(ericsnow) Eliminate this...

		err = engine.Install("events", dependency.Manifold{
			Inputs: []string{},
			Start: func(dependency.GetResourceFunc) (worker.Worker, error) {
				// Pull all existing from State (via API) and add an event for each.
				hctx, err := context.NewContextAPI(apiClient, unitHandler.AddEvents)
				if err != nil {
					return nil, errors.Trace(err)
				}
				events, err := workers.InitialEvents(hctx)
				if err != nil {
					return nil, errors.Trace(err)
				}

				worker, err := unitHandler.NewWorker()
				if err == nil {
					return nil, errors.Trace(err)
				}

				// These must be added *after* the worker is started.
				unitHandler.AddEvents(events...)

				return worker, nil
			},
			Output: func(in worker.Worker, out interface{}) error {
				// TODO(ericsnow) provide the runner
				return nil
			},
		})
		if err == nil {
			return nil, errors.Trace(err)
		}

		err = engine.Install("apiclient", dependency.Manifold{
			Inputs: []string{},
			Start: func(dependency.GetResourceFunc) (worker.Worker, error) {
				loop := func(<-chan struct{}) error { return nil }
				return worker.NewSimpleWorker(loop), nil
			},
			Output: func(in worker.Worker, out interface{}) error {
				// TODO(ericsnow) provide the APICaller
				return nil
			},
		})
		if err == nil {
			return nil, errors.Trace(err)
		}

		return engine, nil
	}
	return manifold, nil
}

func (workloads) registerState() {
	// TODO(ericsnow) Use a more general registration mechanism.
	//state.RegisterMultiEnvCollections(persistence.Collections...)

	newUnitWorkloads := func(persist state.Persistence, unit names.UnitTag, getMetadata func() (*charm.Meta, error)) (state.UnitWorkloads, error) {
		return workloadstate.NewUnitWorkloads(persist, unit, getMetadata), nil
	}
	state.SetWorkloadsComponent(newUnitWorkloads)
}

func (workloads) registerUnitStatus() {
	apiserverclient.RegisterStatusProviderForUnits(workload.ComponentName,
		func(unit *state.Unit) (interface{}, error) {
			up, err := unit.Workloads()
			if err != nil {
				return nil, err
			}
			workloads, err := up.List()
			if err != nil {
				return nil, err
			}
			return status.UnitStatus(workloads)
		})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/nexneo/samay/data"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"
)

var (
	commands = map[string]func(*data.Project) error{
		"start": startTimer,
		"s":     startTimer,
		"stop":  stopTimer,
		"p":     stopTimer,

		"entry": addEntry,
		"e":     addEntry,
		"rm":    deleteEntryOrProject,
		"show":  showEntryOrProject,

		"mv":  moveProject,
		"log": logProject,

		"report": report,
	}
)

func startTimer(project *data.Project) (err error) {
	data.Save(project)
	timer := data.CreateTimer(project)
	err = data.Save(timer)
	return
}

func stopTimer(project *data.Project) (err error) {
	if yes, timer := project.OnClock(); yes {
		entry := project.CreateEntry(getContent(), billable)

		if err = timer.Stop(entry); err == nil {
			fmt.Printf("%.2f mins\n", entry.Minutes())
		}
	}
	return
}

func addEntry(project *data.Project) (err error) {
	data.Save(project)
	entry := project.CreateEntryWithDuration(
		getContent(), duration, billable,
	)
	err = data.Save(entry)
	return
}

func showEntryOrProject(project *data.Project) (err error) {
	if idx < 0 {
		return showProject(project)
	}
	return showEntry(project)
}

func showProject(project *data.Project) (err error) {
	fmt.Printf("       id : %s\n", project.GetShaFromName())
	fmt.Printf("     name : %s\n", project.GetName())
	fmt.Printf("  entries : %d\n", len(project.Entries()))
	fmt.Printf(" location : %s\n", project.Location())
	return nil
}

func showEntry(project *data.Project) (err error) {
	var started, ended *time.Time
	for i, entry := range project.Entries() {
		if i > idx {
			break
		}
		if i == idx {
			started, err = entry.StartedTime()
			ended, err = entry.EndedTime()
			fmt.Printf("       id : %s\n", entry.GetId())
			fmt.Printf(" contents : %s\n", entry.GetContent())
			fmt.Printf(" duration : %s\n", strings.Trim(entry.HoursMins().String(), " "))
			fmt.Printf("  started : %s\n", started)
			fmt.Printf("    ended : %s\n", ended)
			fmt.Printf("     tags : %v\n", entry.GetTags())
			fmt.Printf(" billable : %t\n", entry.GetBillable())
			break
		}
	}
	return err
}

func deleteEntryOrProject(project *data.Project) (err error) {
	if idx < 0 {
		return deleteProject(project)
	}
	return deleteEntry(project)
}

func deleteEntry(project *data.Project) (err error) {
	for i, entry := range project.Entries() {
		if i > idx {
			break
		}
		if i == idx {
			err = data.Destroy(entry)
			break
		}
	}
	return
}

func deleteProject(project *data.Project) (err error) {
	var remove string
	fmt.Printf(
		"Remove all data for project \"%s\" ([No]/yes)? ",
		project.GetName(),
	)

	if fmt.Scanln(&remove); remove == "yes" {
		err = data.Destroy(project)
	}
	return
}

func moveProject(project *data.Project) (err error) {
	newProject := data.CreateProject(newName)
	if err = data.Save(newProject); err != nil {
		return err
	}
	for _, entry := range project.Entries() {
		entry.Project = newProject
		err = data.Save(entry)
		if err != nil {
			return err
		}
	}
	fmt.Printf("All entries copied to project \"%s\" \n...\n", newProject.GetName())
	deleteProject(project)
	return nil
}

func report(project *data.Project) (err error) {
	if month > 0 && month < 13 {
		data.PrintStatus(month)
	} else {
		err = fmt.Errorf("Month %d is not valid", month)
	}
	return
}

func logProject(project *data.Project) error {
	data.PrintProjectLog(project)
	return nil
}

// Set timelog entry content and tags using external editor.
// Only if it didn't set via -m flag
func getContent() string {
	if content == "" {
		content, _ = openEditor()
	}
	return content
}

// Open external editor for text input from user
func openEditor() (string, error) {
	file, err := ioutil.TempFile(os.TempDir(), "subl")
	args := strings.Split(os.Getenv("EDITOR"), " ")
	args = append(args, file.Name())
	cmd := exec.Command(args[0], args[1:]...)
	if err = cmd.Start(); err != nil {
		return "", err
	}
	cmd.Wait()
	data, err := ioutil.ReadFile(file.Name())
	return string(data), err
}
<commit_msg>Start static server<commit_after>package main

import (
	"fmt"
	"github.com/nexneo/samay/data"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"
)

var (
	commands = map[string]func(*data.Project) error{
		"start": startTimer,
		"s":     startTimer,
		"stop":  stopTimer,
		"p":     stopTimer,

		"entry": addEntry,
		"e":     addEntry,
		"rm":    deleteEntryOrProject,
		"show":  showEntryOrProject,

		"mv":  moveProject,
		"log": logProject,

		"report": report,
	}
)

func startTimer(project *data.Project) (err error) {
	data.Save(project)
	timer := data.CreateTimer(project)
	err = data.Save(timer)
	return
}

func stopTimer(project *data.Project) (err error) {
	if yes, timer := project.OnClock(); yes {
		entry := project.CreateEntry(getContent(), billable)

		if err = timer.Stop(entry); err == nil {
			fmt.Printf("%.2f mins\n", entry.Minutes())
		}
	}
	return
}

func addEntry(project *data.Project) (err error) {
	data.Save(project)
	entry := project.CreateEntryWithDuration(
		getContent(), duration, billable,
	)
	err = data.Save(entry)
	return
}

func showEntryOrProject(project *data.Project) (err error) {
	if idx < 0 {
		return showProject(project)
	}
	return showEntry(project)
}

func showProject(project *data.Project) (err error) {
	fmt.Printf("       id : %s\n", project.GetShaFromName())
	fmt.Printf("     name : %s\n", project.GetName())
	fmt.Printf("  entries : %d\n", len(project.Entries()))
	fmt.Printf(" location : %s\n", project.Location())
	return nil
}

func showEntry(project *data.Project) (err error) {
	var started, ended *time.Time
	for i, entry := range project.Entries() {
		if i > idx {
			break
		}
		if i == idx {
			started, err = entry.StartedTime()
			ended, err = entry.EndedTime()
			fmt.Printf("       id : %s\n", entry.GetId())
			fmt.Printf(" contents : %s\n", entry.GetContent())
			fmt.Printf(" duration : %s\n", strings.Trim(entry.HoursMins().String(), " "))
			fmt.Printf("  started : %s\n", started)
			fmt.Printf("    ended : %s\n", ended)
			fmt.Printf("     tags : %v\n", entry.GetTags())
			fmt.Printf(" billable : %t\n", entry.GetBillable())
			break
		}
	}
	return err
}

func deleteEntryOrProject(project *data.Project) (err error) {
	if idx < 0 {
		return deleteProject(project)
	}
	return deleteEntry(project)
}

func deleteEntry(project *data.Project) (err error) {
	for i, entry := range project.Entries() {
		if i > idx {
			break
		}
		if i == idx {
			err = data.Destroy(entry)
			break
		}
	}
	return
}

func deleteProject(project *data.Project) (err error) {
	var remove string
	fmt.Printf(
		"Remove all data for project \"%s\" ([No]/yes)? ",
		project.GetName(),
	)

	if fmt.Scanln(&remove); remove == "yes" {
		err = data.Destroy(project)
	}
	return
}

func moveProject(project *data.Project) (err error) {
	newProject := data.CreateProject(newName)
	if err = data.Save(newProject); err != nil {
		return err
	}
	for _, entry := range project.Entries() {
		entry.Project = newProject
		err = data.Save(entry)
		if err != nil {
			return err
		}
	}
	fmt.Printf("All entries copied to project \"%s\" \n...\n", newProject.GetName())
	deleteProject(project)
	return nil
}

func report(project *data.Project) error {
	http.Handle("/",
		http.FileServer(http.Dir("./public")),
	)
	time.AfterFunc(time.Duration(1*time.Microsecond), func() {
		exec.Command("open", "http://localhost:8080/").Run()
	})
	return http.ListenAndServe(":8080", nil)
}

func logProject(project *data.Project) error {
	data.PrintProjectLog(project)
	return nil
}

// Set timelog entry content and tags using external editor.
// Only if it didn't set via -m flag
func getContent() string {
	if content == "" {
		content, _ = openEditor()
	}
	return content
}

// Open external editor for text input from user
func openEditor() (string, error) {
	file, err := ioutil.TempFile(os.TempDir(), "subl")
	args := strings.Split(os.Getenv("EDITOR"), " ")
	args = append(args, file.Name())
	cmd := exec.Command(args[0], args[1:]...)
	if err = cmd.Start(); err != nil {
		return "", err
	}
	cmd.Wait()
	data, err := ioutil.ReadFile(file.Name())
	return string(data), err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/k0kubun/grimoire/dict"
	"log"
	"os"
)

var Commands = []cli.Command{
	commandCommon,
	commandPerson,
	commandGreek,
	commandNorse,
}

var commandCommon = cli.Command{
	Name:  "common",
	Usage: "common English word list",
	Description: `
`,
	Action: func(c *cli.Context) {
		for _, w := range dict.CommonDict() {
			fmt.Println(w)
		}
	},
}

var commandGreek = cli.Command{
	Name:  "greek",
	Usage: "greek mythological figures",
	Description: `
`,
	Action: func(c *cli.Context) {
		for _, w := range dict.GreekDict() {
			fmt.Println(w)
		}
	},
}

var commandNorse = cli.Command{
	Name:  "norse",
	Usage: "norse gods and goddesses",
	Description: `
`,
	Action: func(c *cli.Context) {
		for _, w := range dict.NorseDict() {
			fmt.Println(w)
		}
	},
}

var commandPerson = cli.Command{
	Name:  "person",
	Usage: "person name in British, French, Italy, Spain, Greek, Finalnd and Russia",
	Description: `
`,
	Action: func(c *cli.Context) {
		for _, w := range dict.PersonDict() {
			fmt.Println(w)
		}
	},
}

func debug(v ...interface{}) {
	if os.Getenv("DEBUG") != "" {
		log.Println(v...)
	}
}

func assert(err error) {
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Refactor actions<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/k0kubun/grimoire/dict"
	"log"
	"os"
)

var Commands = []cli.Command{
	commandCommon,
	commandPerson,
	commandGreek,
	commandNorse,
}

var commandCommon = cli.Command{
	Name:  "common",
	Usage: "common English word list",
	Description: `
`,
	Action: actionByDict(dict.CommonDict),
}

var commandGreek = cli.Command{
	Name:  "greek",
	Usage: "greek mythological figures",
	Description: `
`,
	Action: actionByDict(dict.GreekDict),
}

var commandNorse = cli.Command{
	Name:  "norse",
	Usage: "norse gods and goddesses",
	Description: `
`,
	Action: actionByDict(dict.NorseDict),
}

var commandPerson = cli.Command{
	Name:  "person",
	Usage: "person name in British, French, Italy, Spain, Greek, Finalnd and Russia",
	Description: `
`,
	Action: actionByDict(dict.PersonDict),
}

func actionByDict(dictFunc func() []string) func(*cli.Context) {
	return func(c *cli.Context) {
		for _, w := range dictFunc() {
			fmt.Println(w)
		}
	}
}

func debug(v ...interface{}) {
	if os.Getenv("DEBUG") != "" {
		log.Println(v...)
	}
}

func assert(err error) {
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main
import (
	"fmt"
	"net"
	"strconv"
	"strings"
)
const commandRejectMessage = "I don't understand."
var commands = map[string] func([]string, net.Conn, string)() {}

func walk(d Direction, c net.Conn, playerName string) {
	player, exists := getPlayer(playerName)
	if !exists {
		fmt.Println("walk called with nonplayer '" + playerName + "'")
		return
	}
	currentRoom := rooms[player.roomId]
	newRoom, ok := currentRoom.exits[d]
	if !ok {
		const invalidDirectionMessage = "You faceplant a wall. Suck."
		c.Write([]byte(invalidDirectionMessage + "\n"))
		return
	}
	playerChange<- struct {key string; modify func(*player_state)} {player.name, func(player *player_state){
			player.roomId = newRoom.id
			go look(c, playerName)
	}}
}

func look(c net.Conn, playerName string) {
	player, exists := getPlayer(playerName)
	if !exists {
		fmt.Println("look called with nonplayer '" + playerName + "'")
		return
	}

	currentRoom := rooms[player.roomId]
	c.Write([]byte(currentRoom.Print() + "\n"))
}

func quicklook(c net.Conn, playerName string) {
	player, exists := getPlayer(playerName)
	if !exists {
		fmt.Println("quicklook called with nonplayer '" + playerName + "'")
		return
	}
	currentRoom := rooms[player.roomId]
	c.Write([]byte(currentRoom.PrintBrief() + "\n"))
}

func initCommandsAdmin(){
	commands["makeroom"] = func(args []string, c net.Conn, playerName string) {
		if len(args) < 2 {
			c.Write([]byte(commandRejectMessage + "3\n")) ///< @todo give better error
			return
		}
		newRoomDirection := stringToDirection(args[0])
		if newRoomDirection < north || newRoomDirection > southwest {
			c.Write([]byte(commandRejectMessage + "4\n")) ///< @todo give better error
			fmt.Println(args[0]) ///< @todo give more descriptive error
			fmt.Println(args[1]) ///< @todo give more descriptive error
			return
		}
		player, exists := getPlayer(playerName)
		if !exists {
			fmt.Println("makeroom called with nonplayer '" + playerName + "'")
			return
		}
		currentRoom := rooms[player.roomId]
		newRoomName := strings.Join(args[1:], " ")
		currentRoom.NewRoom(newRoomDirection, newRoomName, "")
		c.Write([]byte(newRoomName + " materializes to the " + newRoomDirection.String() + ". It is nondescript and seems as though it might fade away at any moment.\n"))
	}
	commands["mr"] = commands["makeroom"]

	commands["connectroom"] = func(args []string, c net.Conn, playerName string) {
		if len(args) < 2 {
			c.Write([]byte(commandRejectMessage + "5\n"))
			return
		}
		toConnectRoomId, err := strconv.Atoi(args[1])
		if err != nil {
			c.Write([]byte(commandRejectMessage + "6\n"))
			return
		}
		player, exists := getPlayer(playerName)
		if !exists {
			fmt.Println("connectroom called with nonplayer '" + playerName + "'")
			return
		}
		currentRoom := rooms[player.roomId]
		newRoomDirection := stringToDirection(args[0])
		toConnectRoom := rooms[toConnectRoomId]
		currentRoom.exits[newRoomDirection] = toConnectRoom
		toConnectRoom.exits[newRoomDirection.reverse()] = currentRoom
		c.Write([]byte("You become aware of a " + newRoomDirection.String() + " passage to " + toConnectRoom.name + ".\n"))
	}
	commands["cr"] = commands["connectroom"]

	commands["describeroom"] = func(args []string, c net.Conn, playerName string) {
		if len(args) < 1 {
			c.Write([]byte(commandRejectMessage + "3\n")) ///< @todo give better  error
			return
		}
		player, exists := getPlayer(playerName)
		if !exists {
			fmt.Println("describeroom called with nonplayer '" + playerName + "'")
			return
		}
		currentRoom := rooms[player.roomId]
		currentRoom.description = strings.Join(args[0:], " ")
		const setDescriptionSuccessMessage = "Everything seems a bit more corporeal."
		c.Write([]byte(setDescriptionSuccessMessage + "\n"))
	}
	commands["dr"] = commands["describeroom"]
	// just displays the current room's ID. Probably doesn't need to be an admin nd
	commands["roomid"] = func(args []string, c net.Conn, playerName string) {
		player, exists := getPlayer(playerName)
		if !exists {
			fmt.Println("describeroom called with nonplayer '" + playerName + "'")
			return
		}
		currentRoom := rooms[player.roomId]
		c.Write([]byte(strconv.Itoa(currentRoom.id) + "\n"))
	}
}

func initCommandsDirections() {
	commands["south"] = func(args []string, c net.Conn, player string) {
		walk(south, c, player)
	}
	commands["s"] = commands["south"]
	commands["north"] = func(args []string, c net.Conn, player string) {
		walk(north, c, player)
	}
	commands["n"] = commands["north"]
	commands["east"] = func(args []string, c net.Conn, player string) {
		walk(east, c, player)
	}
	commands["e"] = commands["east"]
	commands["west"] = func(args []string, c net.Conn, player string) {
		walk(west, c, player)
	}
	commands["w"] = commands["west"]
	commands["northeast"] = func(args []string, c net.Conn, player string) {
		walk(northeast, c, player)
	}
	commands["ne"] = commands["northeast"]
	commands["northwest"] = func(args []string, c net.Conn, player string) {
		walk(northwest, c, player)
	}
	commands["nw"] = commands["northwest"]
	commands["southeast"] = func(args []string, c net.Conn, player string) {
		walk(southeast, c, player)
	}
	commands["se"] = commands["southeast"]
	commands["southwest"] = func(args []string, c net.Conn, player string) {
		walk(southwest, c, player)
	}
	commands["sw"] = commands["southwest"]
}

func initCommands() {

	commands["look"] = func(args []string, c net.Conn, player string) {
		look(c, player)
	}
	commands["l"] = commands["look"]

	commands["quicklook"] = func(args []string, c net.Conn, player string) {
		quicklook(c, player)
	}
	commands["ql"] = commands["quicklook"]

	initCommandsDirections()
	initCommandsAdmin()
}
<commit_msg>fixed a typo in a comment<commit_after>package main
import (
	"fmt"
	"net"
	"strconv"
	"strings"
)
const commandRejectMessage = "I don't understand."
var commands = map[string] func([]string, net.Conn, string)() {}

func walk(d Direction, c net.Conn, playerName string) {
	player, exists := getPlayer(playerName)
	if !exists {
		fmt.Println("walk called with nonplayer '" + playerName + "'")
		return
	}
	currentRoom := rooms[player.roomId]
	newRoom, ok := currentRoom.exits[d]
	if !ok {
		const invalidDirectionMessage = "You faceplant a wall. Suck."
		c.Write([]byte(invalidDirectionMessage + "\n"))
		return
	}
	playerChange<- struct {key string; modify func(*player_state)} {player.name, func(player *player_state){
			player.roomId = newRoom.id
			go look(c, playerName)
	}}
}

func look(c net.Conn, playerName string) {
	player, exists := getPlayer(playerName)
	if !exists {
		fmt.Println("look called with nonplayer '" + playerName + "'")
		return
	}

	currentRoom := rooms[player.roomId]
	c.Write([]byte(currentRoom.Print() + "\n"))
}

func quicklook(c net.Conn, playerName string) {
	player, exists := getPlayer(playerName)
	if !exists {
		fmt.Println("quicklook called with nonplayer '" + playerName + "'")
		return
	}
	currentRoom := rooms[player.roomId]
	c.Write([]byte(currentRoom.PrintBrief() + "\n"))
}

func initCommandsAdmin(){
	commands["makeroom"] = func(args []string, c net.Conn, playerName string) {
		if len(args) < 2 {
			c.Write([]byte(commandRejectMessage + "3\n")) ///< @todo give better error
			return
		}
		newRoomDirection := stringToDirection(args[0])
		if newRoomDirection < north || newRoomDirection > southwest {
			c.Write([]byte(commandRejectMessage + "4\n")) ///< @todo give better error
			fmt.Println(args[0]) ///< @todo give more descriptive error
			fmt.Println(args[1]) ///< @todo give more descriptive error
			return
		}
		player, exists := getPlayer(playerName)
		if !exists {
			fmt.Println("makeroom called with nonplayer '" + playerName + "'")
			return
		}
		currentRoom := rooms[player.roomId]
		newRoomName := strings.Join(args[1:], " ")
		currentRoom.NewRoom(newRoomDirection, newRoomName, "")
		c.Write([]byte(newRoomName + " materializes to the " + newRoomDirection.String() + ". It is nondescript and seems as though it might fade away at any moment.\n"))
	}
	commands["mr"] = commands["makeroom"]

	commands["connectroom"] = func(args []string, c net.Conn, playerName string) {
		if len(args) < 2 {
			c.Write([]byte(commandRejectMessage + "5\n"))
			return
		}
		toConnectRoomId, err := strconv.Atoi(args[1])
		if err != nil {
			c.Write([]byte(commandRejectMessage + "6\n"))
			return
		}
		player, exists := getPlayer(playerName)
		if !exists {
			fmt.Println("connectroom called with nonplayer '" + playerName + "'")
			return
		}
		currentRoom := rooms[player.roomId]
		newRoomDirection := stringToDirection(args[0])
		toConnectRoom := rooms[toConnectRoomId]
		currentRoom.exits[newRoomDirection] = toConnectRoom
		toConnectRoom.exits[newRoomDirection.reverse()] = currentRoom
		c.Write([]byte("You become aware of a " + newRoomDirection.String() + " passage to " + toConnectRoom.name + ".\n"))
	}
	commands["cr"] = commands["connectroom"]

	commands["describeroom"] = func(args []string, c net.Conn, playerName string) {
		if len(args) < 1 {
			c.Write([]byte(commandRejectMessage + "3\n")) ///< @todo give better  error
			return
		}
		player, exists := getPlayer(playerName)
		if !exists {
			fmt.Println("describeroom called with nonplayer '" + playerName + "'")
			return
		}
		currentRoom := rooms[player.roomId]
		currentRoom.description = strings.Join(args[0:], " ")
		const setDescriptionSuccessMessage = "Everything seems a bit more corporeal."
		c.Write([]byte(setDescriptionSuccessMessage + "\n"))
	}
	commands["dr"] = commands["describeroom"]
	// just displays the current room's ID. Probably doesn't need to be an admin command
	commands["roomid"] = func(args []string, c net.Conn, playerName string) {
		player, exists := getPlayer(playerName)
		if !exists {
			fmt.Println("describeroom called with nonplayer '" + playerName + "'")
			return
		}
		currentRoom := rooms[player.roomId]
		c.Write([]byte(strconv.Itoa(currentRoom.id) + "\n"))
	}
}

func initCommandsDirections() {
	commands["south"] = func(args []string, c net.Conn, player string) {
		walk(south, c, player)
	}
	commands["s"] = commands["south"]
	commands["north"] = func(args []string, c net.Conn, player string) {
		walk(north, c, player)
	}
	commands["n"] = commands["north"]
	commands["east"] = func(args []string, c net.Conn, player string) {
		walk(east, c, player)
	}
	commands["e"] = commands["east"]
	commands["west"] = func(args []string, c net.Conn, player string) {
		walk(west, c, player)
	}
	commands["w"] = commands["west"]
	commands["northeast"] = func(args []string, c net.Conn, player string) {
		walk(northeast, c, player)
	}
	commands["ne"] = commands["northeast"]
	commands["northwest"] = func(args []string, c net.Conn, player string) {
		walk(northwest, c, player)
	}
	commands["nw"] = commands["northwest"]
	commands["southeast"] = func(args []string, c net.Conn, player string) {
		walk(southeast, c, player)
	}
	commands["se"] = commands["southeast"]
	commands["southwest"] = func(args []string, c net.Conn, player string) {
		walk(southwest, c, player)
	}
	commands["sw"] = commands["southwest"]
}

func initCommands() {

	commands["look"] = func(args []string, c net.Conn, player string) {
		look(c, player)
	}
	commands["l"] = commands["look"]

	commands["quicklook"] = func(args []string, c net.Conn, player string) {
		quicklook(c, player)
	}
	commands["ql"] = commands["quicklook"]

	initCommandsDirections()
	initCommandsAdmin()
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/hashicorp/serf/cli"
	"github.com/hashicorp/serf/cli/agent"
	"github.com/hashicorp/serf/command"
	"os"
	"os/signal"
)

// Commands is the mapping of all the available Serf commands.
var Commands map[string]cli.CommandFactory

func init() {
	Commands = map[string]cli.CommandFactory{
		"agent": func() (cli.Command, error) {
			return &agent.Command{
				ShutdownCh: makeShutdownCh(),
			}, nil
		},

		"members": func() (cli.Command, error) {
			return &command.MembersCommand{}, nil
		},

		"version": func() (cli.Command, error) {
			return &command.VersionCommand{
				Revision:          GitCommit,
				Version:           Version,
				VersionPrerelease: VersionPrerelease,
			}, nil
		},
	}
}

// makeShutdownCh returns a channel that can be used for shutdown
// notifications for commands. This channel will send a message for every
// interrupt received.
func makeShutdownCh() <-chan struct{} {
	resultCh := make(chan struct{})

	signalCh := make(chan os.Signal, 4)
	signal.Notify(signalCh, os.Interrupt)
	go func() {
		for {
			<-signalCh
			resultCh <- struct{}{}
		}
	}()

	return resultCh
}
<commit_msg>main: add the join command<commit_after>package main

import (
	"github.com/hashicorp/serf/cli"
	"github.com/hashicorp/serf/cli/agent"
	"github.com/hashicorp/serf/command"
	"os"
	"os/signal"
)

// Commands is the mapping of all the available Serf commands.
var Commands map[string]cli.CommandFactory

func init() {
	Commands = map[string]cli.CommandFactory{
		"agent": func() (cli.Command, error) {
			return &agent.Command{
				ShutdownCh: makeShutdownCh(),
			}, nil
		},

		"join": func() (cli.Command, error) {
			return &command.JoinCommand{}, nil
		},

		"members": func() (cli.Command, error) {
			return &command.MembersCommand{}, nil
		},

		"version": func() (cli.Command, error) {
			return &command.VersionCommand{
				Revision:          GitCommit,
				Version:           Version,
				VersionPrerelease: VersionPrerelease,
			}, nil
		},
	}
}

// makeShutdownCh returns a channel that can be used for shutdown
// notifications for commands. This channel will send a message for every
// interrupt received.
func makeShutdownCh() <-chan struct{} {
	resultCh := make(chan struct{})

	signalCh := make(chan os.Signal, 4)
	signal.Notify(signalCh, os.Interrupt)
	go func() {
		for {
			<-signalCh
			resultCh <- struct{}{}
		}
	}()

	return resultCh
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/barracudanetworks/wbd/config"
	"github.com/barracudanetworks/wbd/database"
	"github.com/barracudanetworks/wbd/web"

	"github.com/codegangsta/cli"
	"github.com/howeyc/gopass"
)

func handleRun(c *cli.Context) {
	conf := &config.Configuration{
		ListenAddress: c.String("listen"),
		ListenPort:    c.Int("port"),
		WebAddress:    c.String("url"),
		Database:      c.String("database"),
	}

	if _, err := os.Stat(conf.Database); err != nil {
		log.Fatal("database does not exist")
	}
	log.Printf("Using database %s", conf.Database)

	if conf.ListenPort == 0 {
		conf.ListenPort = 80
	}
	if conf.ListenAddress == "" {
		conf.ListenAddress = "0.0.0.0"
	}

	web.Start(conf)
}

func handleUrl(c *cli.Context) {
	if _, err := os.Stat(c.String("database")); err != nil {
		log.Fatal("database does not exist")
	}
	log.Printf("Using database %s", c.String("database"))

	addUrl, deleteUrl := c.String("add"), c.String("delete")
	if addUrl != "" && deleteUrl != "" {
		log.Fatal("Can't both remove and add a URL")
	}

	db, err := database.Connect(c.String("database"))
	defer db.Close()
	if err != nil {
		log.Fatal(err)
	}

	if addUrl != "" {
		log.Printf("Adding url %s to rotation", addUrl)
		if err := db.InsertUrl(addUrl); err != nil {
			log.Fatal(err)
		}
	}

	if deleteUrl != "" {
		log.Printf("Removing url %s from rotation", deleteUrl)
		if err := db.DeleteUrl(deleteUrl); err != nil {
			log.Fatal(err)
		}
	}

	if c.Bool("list") {
		log.Print("URLs in rotation:")
		urls, err := db.FetchUrls()
		if err != nil {
			log.Fatal(err)
		}

		for _, url := range urls {
			log.Print("  ", url)
		}
	}
}

func handleClient(c *cli.Context) {
	if _, err := os.Stat(c.String("database")); err != nil {
		log.Fatal("database does not exist")
	}
	log.Printf("Using database %s", c.String("database"))

	aliasClient, toAlias := c.String("alias"), c.String("to")
	deleteClient := c.String("delete")

	if aliasClient != "" && deleteClient != "" {
		log.Fatal("Can't both remove and alias a client")
	}

	db, err := database.Connect(c.String("database"))
	defer db.Close()
	if err != nil {
		log.Fatal(err)
	}

	if aliasClient != "" {
		if toAlias == "" {
			log.Fatal("No alias specified (use --to)")
		}

		log.Printf("Aliasing client '%s' to '%s'", aliasClient, toAlias)
		if err := db.SetClientAlias(aliasClient, toAlias); err != nil {
			log.Fatal(err)
		}
	}

	if deleteClient != "" {
		log.Printf("Removing client '%s' from the database", deleteClient)
		if err := db.DeleteClient(deleteClient); err != nil {
			log.Fatal(err)
		}
	}

	if c.Bool("list") {
		log.Print("Known clients:")
		clients, err := db.FetchClients()
		if err != nil {
			log.Fatal(err)
		}

		for _, client := range clients {
			if client.Alias == "" {
				log.Printf("  %s (%s) - Last active %s", client.Identifier, client.IpAddress, client.LastPing)
			} else {
				log.Printf("  %s [%s] (%s) - Last active %s", client.Alias, client.Identifier, client.IpAddress, client.LastPing)
			}
		}
	}
}

func handleAssign(c *cli.Context) {
	if _, err := os.Stat(c.String("database")); err != nil {
		log.Fatal("database does not exist")
	}
	log.Printf("Using database %s", c.String("database"))

	deleteFlag := c.Bool("delete")
	assignList := c.String("list")
	assignUrl, assignClient := c.String("url"), c.String("client")

	if (assignList == "" && (!deleteFlag || assignClient == "")) || (assignClient == "" && assignUrl == "") {
		log.Fatal("Must specify a list, and a client or URL to assign to it")
	}

	db, err := database.Connect(c.String("database"))
	defer db.Close()
	if err != nil {
		log.Fatal(err)
	}

	if assignUrl != "" {
		// delete association if delete flag is true
		if deleteFlag {
			if err := db.RemoveUrlFromList(assignList, assignUrl); err != nil {
				log.Fatal(err)
			}
			log.Printf("Removed URL %s from list %s", assignUrl, assignList)
		} else {
			if err := db.AssignUrlToList(assignList, assignUrl); err != nil {
				log.Fatal(err)
			}
			log.Printf("Assigned URL %s to list %s", assignUrl, assignList)
		}
	}
	if assignClient != "" {
		// delete association if delete flag is true
		if deleteFlag {
			if err := db.RemoveClientFromList(assignClient); err != nil {
				log.Fatal(err)
			}
			log.Printf("Assigned client %s back to the Default list", assignClient)
		} else {
			if err := db.AssignClientToList(assignList, assignClient); err != nil {
				log.Fatal(err)
			}
			log.Printf("Assigned client %s to list %s", assignClient, assignList)
		}
	}
}

func handleList(c *cli.Context) {
	if _, err := os.Stat(c.String("database")); err != nil {
		log.Fatal("database does not exist")
	}
	log.Printf("Using database %s", c.String("database"))

	addList, deleteList := c.String("add"), c.String("delete")
	if addList != "" && deleteList != "" {
		log.Fatal("Can't both remove and add a list")
	}

	db, err := database.Connect(c.String("database"))
	defer db.Close()
	if err != nil {
		log.Fatal(err)
	}

	if addList != "" {
		log.Printf("Creating list %s", addList)
		if err := db.InsertList(addList); err != nil {
			log.Fatal(err)
		}
	}

	if deleteList != "" {
		log.Printf("Deleting list %s", deleteList)
		if err := db.DeleteList(deleteList); err != nil {
			log.Fatal(err)
		}
	}

	if c.Bool("list") {
		log.Print("URL lists defined:")
		lists, err := db.FetchLists()
		if err != nil {
			log.Fatal(err)
		}

		for _, list := range lists {
			urls, err := db.FetchListUrlsByName(list)
			if err != nil {
				log.Fatal(err)
			}

			log.Print("  ", list)

			for _, url := range urls {
				log.Print("    ", url)
			}
		}
	}
}

func handleInstall(c *cli.Context) {
	log.Print("Starting installation")

	var (
		path     string
		password []byte
	)

	path = c.String("database")

	// Don't overwrite db if one already exists
	if _, err := os.Stat(path); err == nil {
		log.Fatal("database already exists")
	}

	if resp := confirmDefault("Would you like to set a password?", true); resp == true {
		fmt.Printf("Password: ")

		var err error
		password, err = gopass.GetPasswd()
		if err != nil {
			log.Fatal(err)
		}
	}

	log.Printf("Creating database at %s", path)

	// Create a new connection to the database
	db, err := database.Connect(path)
	defer db.Close()
	if err != nil {
		log.Fatal(err)
	}

	// Start a transaction
	tx, err := db.Conn.Begin()
	if err != nil {
		log.Fatal(err)
	}

	// Create table schema
	if err = db.CreateTables(); err != nil {
		tx.Rollback()
		log.Fatal(err)
	}

	// Insert password if one was given
	if len(password) == 0 {
		if err = db.InsertConfig("password", string(password)); err != nil {
			tx.Rollback()
			log.Fatal(err)
		}
	}

	tx.Commit()
	log.Print("Database created")
}

func handleClean(c *cli.Context) {
	database := c.String("database")
	log.Printf("Removing database at %s", database)

	if _, err := os.Stat(database); err != nil {
		log.Fatal("database does not exist")
	}

	if err := os.Remove(database); err != nil {
		log.Fatal(err)
	}

	log.Print("Database removed")
}
<commit_msg>Fix deprecation notices from codegangsta/cli<commit_after>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/barracudanetworks/wbd/config"
	"github.com/barracudanetworks/wbd/database"
	"github.com/barracudanetworks/wbd/web"

	"github.com/codegangsta/cli"
	"github.com/howeyc/gopass"
)

func handleRun(c *cli.Context) error {
	conf := &config.Configuration{
		ListenAddress: c.String("listen"),
		ListenPort:    c.Int("port"),
		WebAddress:    c.String("url"),
		Database:      c.String("database"),
	}

	if _, err := os.Stat(conf.Database); err != nil {
		log.Fatal("database does not exist")
	}
	log.Printf("Using database %s", conf.Database)

	if conf.ListenPort == 0 {
		conf.ListenPort = 80
	}
	if conf.ListenAddress == "" {
		conf.ListenAddress = "0.0.0.0"
	}

	web.Start(conf)

	return nil
}

func handleUrl(c *cli.Context) error {
	if _, err := os.Stat(c.String("database")); err != nil {
		log.Fatal("database does not exist")
	}
	log.Printf("Using database %s", c.String("database"))

	addUrl, deleteUrl := c.String("add"), c.String("delete")
	if addUrl != "" && deleteUrl != "" {
		log.Fatal("Can't both remove and add a URL")
	}

	db, err := database.Connect(c.String("database"))
	defer db.Close()
	if err != nil {
		log.Fatal(err)
	}

	if addUrl != "" {
		log.Printf("Adding url %s to rotation", addUrl)
		if err := db.InsertUrl(addUrl); err != nil {
			log.Fatal(err)
		}
	}

	if deleteUrl != "" {
		log.Printf("Removing url %s from rotation", deleteUrl)
		if err := db.DeleteUrl(deleteUrl); err != nil {
			log.Fatal(err)
		}
	}

	if c.Bool("list") {
		log.Print("URLs in rotation:")
		urls, err := db.FetchUrls()
		if err != nil {
			log.Fatal(err)
		}

		for _, url := range urls {
			log.Print("  ", url)
		}
	}

	return nil
}

func handleClient(c *cli.Context) error {
	if _, err := os.Stat(c.String("database")); err != nil {
		log.Fatal("database does not exist")
	}
	log.Printf("Using database %s", c.String("database"))

	aliasClient, toAlias := c.String("alias"), c.String("to")
	deleteClient := c.String("delete")

	if aliasClient != "" && deleteClient != "" {
		log.Fatal("Can't both remove and alias a client")
	}

	db, err := database.Connect(c.String("database"))
	defer db.Close()
	if err != nil {
		log.Fatal(err)
	}

	if aliasClient != "" {
		if toAlias == "" {
			log.Fatal("No alias specified (use --to)")
		}

		log.Printf("Aliasing client '%s' to '%s'", aliasClient, toAlias)
		if err := db.SetClientAlias(aliasClient, toAlias); err != nil {
			log.Fatal(err)
		}
	}

	if deleteClient != "" {
		log.Printf("Removing client '%s' from the database", deleteClient)
		if err := db.DeleteClient(deleteClient); err != nil {
			log.Fatal(err)
		}
	}

	if c.Bool("list") {
		log.Print("Known clients:")
		clients, err := db.FetchClients()
		if err != nil {
			log.Fatal(err)
		}

		for _, client := range clients {
			if client.Alias == "" {
				log.Printf("  %s (%s) - Last active %s", client.Identifier, client.IpAddress, client.LastPing)
			} else {
				log.Printf("  %s [%s] (%s) - Last active %s", client.Alias, client.Identifier, client.IpAddress, client.LastPing)
			}
		}
	}

	return nil
}

func handleAssign(c *cli.Context) error {
	if _, err := os.Stat(c.String("database")); err != nil {
		log.Fatal("database does not exist")
	}
	log.Printf("Using database %s", c.String("database"))

	deleteFlag := c.Bool("delete")
	assignList := c.String("list")
	assignUrl, assignClient := c.String("url"), c.String("client")

	if (assignList == "" && (!deleteFlag || assignClient == "")) || (assignClient == "" && assignUrl == "") {
		log.Fatal("Must specify a list, and a client or URL to assign to it")
	}

	db, err := database.Connect(c.String("database"))
	defer db.Close()
	if err != nil {
		log.Fatal(err)
	}

	if assignUrl != "" {
		// delete association if delete flag is true
		if deleteFlag {
			if err := db.RemoveUrlFromList(assignList, assignUrl); err != nil {
				log.Fatal(err)
			}
			log.Printf("Removed URL %s from list %s", assignUrl, assignList)
		} else {
			if err := db.AssignUrlToList(assignList, assignUrl); err != nil {
				log.Fatal(err)
			}
			log.Printf("Assigned URL %s to list %s", assignUrl, assignList)
		}
	}
	if assignClient != "" {
		// delete association if delete flag is true
		if deleteFlag {
			if err := db.RemoveClientFromList(assignClient); err != nil {
				log.Fatal(err)
			}
			log.Printf("Assigned client %s back to the Default list", assignClient)
		} else {
			if err := db.AssignClientToList(assignList, assignClient); err != nil {
				log.Fatal(err)
			}
			log.Printf("Assigned client %s to list %s", assignClient, assignList)
		}
	}

	return nil
}

func handleList(c *cli.Context) error {
	if _, err := os.Stat(c.String("database")); err != nil {
		log.Fatal("database does not exist")
	}
	log.Printf("Using database %s", c.String("database"))

	addList, deleteList := c.String("add"), c.String("delete")
	if addList != "" && deleteList != "" {
		log.Fatal("Can't both remove and add a list")
	}

	db, err := database.Connect(c.String("database"))
	defer db.Close()
	if err != nil {
		log.Fatal(err)
	}

	if addList != "" {
		log.Printf("Creating list %s", addList)
		if err := db.InsertList(addList); err != nil {
			log.Fatal(err)
		}
	}

	if deleteList != "" {
		log.Printf("Deleting list %s", deleteList)
		if err := db.DeleteList(deleteList); err != nil {
			log.Fatal(err)
		}
	}

	if c.Bool("list") {
		log.Print("URL lists defined:")
		lists, err := db.FetchLists()
		if err != nil {
			log.Fatal(err)
		}

		for _, list := range lists {
			urls, err := db.FetchListUrlsByName(list)
			if err != nil {
				log.Fatal(err)
			}

			log.Print("  ", list)

			for _, url := range urls {
				log.Print("    ", url)
			}
		}
	}

	return nil
}

func handleInstall(c *cli.Context) error {
	log.Print("Starting installation")

	var (
		path     string
		password []byte
	)

	path = c.String("database")

	// Don't overwrite db if one already exists
	if _, err := os.Stat(path); err == nil {
		log.Fatal("database already exists")
	}

	if resp := confirmDefault("Would you like to set a password?", true); resp == true {
		fmt.Printf("Password: ")

		var err error
		password, err = gopass.GetPasswd()
		if err != nil {
			log.Fatal(err)
		}
	}

	log.Printf("Creating database at %s", path)

	// Create a new connection to the database
	db, err := database.Connect(path)
	defer db.Close()
	if err != nil {
		log.Fatal(err)
	}

	// Start a transaction
	tx, err := db.Conn.Begin()
	if err != nil {
		log.Fatal(err)
	}

	// Create table schema
	if err = db.CreateTables(); err != nil {
		tx.Rollback()
		log.Fatal(err)
	}

	// Insert password if one was given
	if len(password) == 0 {
		if err = db.InsertConfig("password", string(password)); err != nil {
			tx.Rollback()
			log.Fatal(err)
		}
	}

	tx.Commit()
	log.Print("Database created")

	return nil
}

func handleClean(c *cli.Context) error {
	database := c.String("database")
	log.Printf("Removing database at %s", database)

	if _, err := os.Stat(database); err != nil {
		log.Fatal("database does not exist")
	}

	if err := os.Remove(database); err != nil {
		log.Fatal(err)
	}

	log.Print("Database removed")

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"fmt"

	"github.com/codegangsta/cli"
)

var Commands = []cli.Command{
	commandAll,
	commandBiz,
	commandHack,
}

var commandAll = cli.Command{
	Name:  "all",
	Usage: "",
	Description: `
`,
	Action: doAll,
}

var commandBiz = cli.Command{
	Name:  "biz",
	Usage: "",
	Description: `
`,
	Action: doBiz,
}

var commandHack = cli.Command{
	Name:  "hack",
	Usage: "",
	Description: `
`,
	Action: doHack,
}

func debug(v ...interface{}) {
	if os.Getenv("DEBUG") != "" {
		log.Println(v...)
	}
}

func assert(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func doAll(c *cli.Context) {
	fmt.Printf("print all")
}

func doBiz(c *cli.Context) {
}

func doHack(c *cli.Context) {
}
<commit_msg>First test of goroutine and channel<commit_after>package main

import (
	"log"
	"os"
	"fmt"
	"time"

	"github.com/codegangsta/cli"
)

var Commands = []cli.Command{
	commandAll,
	commandBiz,
	commandHack,
}

var commandAll = cli.Command{
	Name:  "all",
	Usage: "",
	Description: `
`,
	Action: doAll,
}

var commandBiz = cli.Command{
	Name:  "biz",
	Usage: "",
	Description: `
`,
	Action: doBiz,
}

var commandHack = cli.Command{
	Name:  "hack",
	Usage: "",
	Description: `
`,
	Action: doHack,
}

func debug(v ...interface{}) {
	if os.Getenv("DEBUG") != "" {
		log.Println(v...)
	}
}

func goRouTest(test chan int) {
	test <- 10
}

func goRouTestTwo(test2 chan int) {
	time.Sleep(time.Second * 3)
	test2 <- 20
}

func assert(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func doAll(c *cli.Context) {
	test := make(chan int)
	test2 := make(chan int)
	go goRouTest(test)
	go goRouTestTwo(test2)
	fmt.Printf("print all\n")
	result := <- test
	resTwo := <- test2
	fmt.Printf("%d%d\n", result, resTwo)
}

func doBiz(c *cli.Context) {
}

func doHack(c *cli.Context) {
}
<|endoftext|>


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

// Package repotool is able to fetch information from a source code repository.
// Typically, it can get all commits, their authors and commiters and so on
// and return this information in a JSON object. Alternatively, it is able
// to populate the information into a PostgreSQL database.
// Currently, on the Git VCS is supported.
package main

import (
	"bufio"
	"bytes"
	"database/sql"
	"encoding/hex"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	_ "github.com/lib/pq"
	mmh3 "github.com/spaolacci/murmur3"

	"github.com/DevMine/srcanlzr/src"

	"github.com/DevMine/repotool/config"
	"github.com/DevMine/repotool/model"
	"github.com/DevMine/repotool/repo"
)

const version = "0.1.0"

// database fields per tables
var (
	diffDeltaFields = []string{
		"commit_id",
		"file_status",
		"is_file_binary",
		"similarity",
		"old_file_path",
		"new_file_path"}

	commitFields = []string{
		"repository_id",
		"author_id",
		"committer_id",
		"hash",
		"vcs_id",
		"message",
		"author_date",
		"commit_date",
		"file_changed_count",
		"insertions_count",
		"deletions_count"}
)

// program flags
var (
	configPath  = flag.String("c", "", "configuration file")
	vflag       = flag.Bool("v", false, "print version.")
	jsonflag    = flag.Bool("json", true, "json output")
	dbflag      = flag.Bool("db", false, "import data into the database")
	srctoolflag = flag.String("srctool", "", "read json file produced by srctool (give stdin to read from stdin)")
)

func main() {
	var err error

	flag.Usage = func() {
		fmt.Printf("usage: %s [OPTION(S)] [REPOSITORY PATH]\n", os.Args[0])
		flag.PrintDefaults()
		os.Exit(0)
	}
	flag.Parse()

	if *vflag {
		fmt.Printf("%s - %s\n", filepath.Base(os.Args[0]), version)
		os.Exit(0)
	}

	if len(flag.Args()) != 1 {
		fmt.Fprintln(os.Stderr, "invalid # of arguments")
		flag.Usage()
	}

	if *dbflag && len(*configPath) == 0 {
		fatal(errors.New("a configuration file must be specified when using db option"))
	}

	if !*jsonflag && (len(*srctoolflag) > 0) {
		fatal(errors.New("srctool flag may be used only in conjonction with json flag"))
	}

	var cfg *config.Config
	cfg, err = config.ReadConfig(*configPath)
	if err != nil {
		fatal(err)
	}

	repoPath := flag.Arg(0)
	var repository repo.Repo
	repository, err = repo.New(*cfg, repoPath)
	if err != nil {
		fatal(err)
	}
	defer func() {
		repository.Cleanup()
		if err != nil {
			fatal(err)
		}
	}()

	fmt.Fprintln(os.Stderr, "fetching repository commits...")
	tic := time.Now()
	err = repository.FetchCommits()
	if err != nil {
		return
	}
	toc := time.Now()
	fmt.Fprintln(os.Stderr, "done in ", toc.Sub(tic))

	if *jsonflag && (len(*srctoolflag) == 0) {
		var bs []byte
		bs, err = json.Marshal(repository)
		if err != nil {
			return
		}
		fmt.Println(string(bs))
	}

	if *jsonflag && (len(*srctoolflag)) > 0 {
		var r *bufio.Reader
		if *srctoolflag == strings.ToLower("stdin") {
			// read from stdin
			r = bufio.NewReader(os.Stdin)
		} else {
			// read from srctool json file
			var f *os.File
			if f, err = os.Open(*srctoolflag); err != nil {
				return
			}
			r = bufio.NewReader(f)
		}

		buf := new(bytes.Buffer)
		if _, err = io.Copy(buf, r); err != nil {
			fail(err)
			return
		}

		bs := buf.Bytes()
		var p *src.Project
		p, err = src.Unmarshal(bs)
		if err != nil {
			return
		}

		p.Repo = repository.GetRepository()
		bs, err = src.Marshal(p)
		if err != nil {
			return
		}

		fmt.Println(string(bs))
	}

	if *dbflag {
		var db *sql.DB
		db, err = openDBSession(cfg.Database)
		if err != nil {
			return
		}
		defer db.Close()

		fmt.Fprintf(os.Stderr,
			"inserting %d commits from %s repository into the database...\n",
			len(repository.GetCommits()), repository.GetName())
		tic := time.Now()
		err = insertRepoData(db, repository)
		toc := time.Now()
		if err != nil {
			return
		}
		fmt.Fprintln(os.Stderr, "done in ", toc.Sub(tic))
	}
}

// fatal prints an error on standard error stream and exits.
func fatal(a ...interface{}) {
	fmt.Fprintln(os.Stderr, a...)
	os.Exit(1)
}

// fail prints an error on standard error stream.
func fail(a ...interface{}) {
	fmt.Fprintln(os.Stderr, a...)
}

// openDBSession creates a session to the database.
func openDBSession(cfg config.DatabaseConfig) (*sql.DB, error) {
	dbURL := fmt.Sprintf(
		"user='%s' password='%s' host='%s' port=%d dbname='%s' sslmode='%s'",
		cfg.UserName, cfg.Password, cfg.HostName, cfg.Port, cfg.DBName, cfg.SSLMode)

	return sql.Open("postgres", dbURL)
}

// insertRepoData inserts repository data into the database, or updates it
// if it is already there.
func insertRepoData(db *sql.DB, r repo.Repo) error {
	if db == nil {
		return errors.New("nil database given")
	}

	repoID, err := getRepoID(db, r)
	if err != nil {
		return err
	}
	if repoID == nil {
		return errors.New("cannot find corresponding repository in database")
	}

	userIDs, err := getAllUsers(db)
	if err != nil {
		return err
	}

	tx, err := db.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	commitStmt, err := tx.Prepare(genInsQuery("commits", commitFields...) + " RETURNING id")
	if err != nil {
		return err
	}

	deltaStmt, err := tx.Prepare(genInsQuery("commit_diff_deltas", diffDeltaFields...))
	if err != nil {
		return err
	}

	for _, c := range r.GetCommits() {
		if err := insertCommit(userIDs, *repoID, c, tx, commitStmt, deltaStmt); err != nil {
			return err
		}
	}

	if err := commitStmt.Close(); err != nil {
		return err
	}
	if err := deltaStmt.Close(); err != nil {
		return err
	}
	if err := tx.Commit(); err != nil {
		return err
	}

	return nil
}

// insertCommit inserts a commit into the database
func insertCommit(userIDs map[string]uint64, repoID uint64, c model.Commit, tx *sql.Tx, commitStmt, deltaStmt *sql.Stmt) error {
	authorID := userIDs[c.Author.Email]
	committerID := userIDs[c.Committer.Email]
	hash := genCommitHash(c)

	var commitID uint64
	err := commitStmt.QueryRow(
		repoID, authorID, committerID, hash,
		c.VCSID, c.Message, c.AuthorDate, c.CommitDate,
		c.FileChangedCount, c.InsertionsCount, c.DeletionsCount).Scan(&commitID)
	if err != nil {
		return err
	}

	for _, d := range c.DiffDelta {
		if err := insertDiffDelta(commitID, d, deltaStmt); err != nil {
			return err
		}
	}

	return nil
}

// insertDiffDelta inserts a commit diff delta into the database.
func insertDiffDelta(commitID uint64, d model.DiffDelta, stmt *sql.Stmt) error {
	_, err := stmt.Exec(commitID, d.Status, d.Binary, d.Similarity, d.OldFilePath, d.NewFilePath)
	if err != nil {
		return err
	}
	return nil
}

// getAllUsers returns a map of all users IDs with their email address as keys.
func getAllUsers(db *sql.DB) (map[string]uint64, error) {
	rows, err := db.Query("SELECT id, email FROM users WHERE email IS NOT NULL AND email != ''")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	userIDs := map[string]uint64{}
	for rows.Next() {
		var email string
		var id uint64
		if err := rows.Scan(&id, &email); err != nil {
			return nil, err
		}
		userIDs[email] = id
	}

	return userIDs, nil
}

// genCommitHash generates a hash (mmh3) from commit fields.
// This hash can then be used to uniquely identify a commit.
// Typically, we want to make sure not to insert twice the same commit into the
// database after an eventual second repotool run on the same repository.
func genCommitHash(c model.Commit) string {
	h := mmh3.New128()

	io.WriteString(h, c.VCSID)
	io.WriteString(h, c.Message)
	io.WriteString(h, c.Author.Name)
	io.WriteString(h, c.Author.Email)
	io.WriteString(h, c.Committer.Name)
	io.WriteString(h, c.Committer.Email)
	io.WriteString(h, c.AuthorDate.String())
	io.WriteString(h, c.CommitDate.String())
	io.WriteString(h, strconv.FormatInt(int64(c.FileChangedCount), 10))
	io.WriteString(h, strconv.FormatInt(int64(c.InsertionsCount), 10))
	io.WriteString(h, strconv.FormatInt(int64(c.DeletionsCount), 10))

	return hex.EncodeToString(h.Sum(nil))
}

// getRepoID returns the repository id of a repo in repositories table.
// If repo is not in the table, then 0 is returned.
func getRepoID(db *sql.DB, r repo.Repo) (*uint64, error) {
	if db == nil {
		return nil, errors.New("nil database given")
	}

	var id *uint64
	// Clone URL is unique
	err := db.QueryRow("SELECT id FROM repositories WHERE clone_url=$1", r.GetCloneURL()).Scan(&id)
	switch {
	case err == sql.ErrNoRows:
		return nil, nil
	case err != nil:
		return nil, err
	}
	return id, nil
}

// genInsQuery generates a query string for an insertion in the database.
func genInsQuery(tableName string, fields ...string) string {
	var buf bytes.Buffer

	buf.WriteString(fmt.Sprintf("INSERT INTO %s(%s)\n",
		tableName, strings.Join(fields, ",")))
	buf.WriteString("VALUES(")

	for ind := range fields {
		if ind > 0 {
			buf.WriteString(",")
		}

		buf.WriteString(fmt.Sprintf("$%d", ind+1))
	}

	buf.WriteString(")\n")

	return buf.String()
}
<commit_msg>repotool: add cpuprofile option for profiling<commit_after>// Copyright 2014-2015 The DevMine authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package repotool is able to fetch information from a source code repository.
// Typically, it can get all commits, their authors and commiters and so on
// and return this information in a JSON object. Alternatively, it is able
// to populate the information into a PostgreSQL database.
// Currently, on the Git VCS is supported.
package main

import (
	"bufio"
	"bytes"
	"database/sql"
	"encoding/hex"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"

	_ "github.com/lib/pq"
	mmh3 "github.com/spaolacci/murmur3"

	"github.com/DevMine/srcanlzr/src"

	"github.com/DevMine/repotool/config"
	"github.com/DevMine/repotool/model"
	"github.com/DevMine/repotool/repo"
)

const version = "0.1.0"

// database fields per tables
var (
	diffDeltaFields = []string{
		"commit_id",
		"file_status",
		"is_file_binary",
		"similarity",
		"old_file_path",
		"new_file_path"}

	commitFields = []string{
		"repository_id",
		"author_id",
		"committer_id",
		"hash",
		"vcs_id",
		"message",
		"author_date",
		"commit_date",
		"file_changed_count",
		"insertions_count",
		"deletions_count"}
)

// program flags
var (
	configPath  = flag.String("c", "", "configuration file")
	vflag       = flag.Bool("v", false, "print version.")
	jsonflag    = flag.Bool("json", true, "json output")
	dbflag      = flag.Bool("db", false, "import data into the database")
	srctoolflag = flag.String("srctool", "", "read json file produced by srctool (give stdin to read from stdin)")
	cpuprofile  = flag.String("cpuprofile", "", "write cpu profile to file")
)

func main() {
	var err error

	flag.Usage = func() {
		fmt.Printf("usage: %s [OPTION(S)] [REPOSITORY PATH]\n", os.Args[0])
		flag.PrintDefaults()
		os.Exit(0)
	}
	flag.Parse()

	if *vflag {
		fmt.Printf("%s - %s\n", filepath.Base(os.Args[0]), version)
		os.Exit(0)
	}

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if len(flag.Args()) != 1 {
		fmt.Fprintln(os.Stderr, "invalid # of arguments")
		flag.Usage()
	}

	if *dbflag && len(*configPath) == 0 {
		fatal(errors.New("a configuration file must be specified when using db option"))
	}

	if !*jsonflag && (len(*srctoolflag) > 0) {
		fatal(errors.New("srctool flag may be used only in conjonction with json flag"))
	}

	var cfg *config.Config
	cfg, err = config.ReadConfig(*configPath)
	if err != nil {
		fatal(err)
	}

	repoPath := flag.Arg(0)
	var repository repo.Repo
	repository, err = repo.New(*cfg, repoPath)
	if err != nil {
		fatal(err)
	}
	defer func() {
		repository.Cleanup()
		if err != nil {
			fatal(err)
		}
	}()

	fmt.Fprintln(os.Stderr, "fetching repository commits...")
	tic := time.Now()
	err = repository.FetchCommits()
	if err != nil {
		return
	}
	toc := time.Now()
	fmt.Fprintln(os.Stderr, "done in ", toc.Sub(tic))

	if *jsonflag && (len(*srctoolflag) == 0) {
		var bs []byte
		bs, err = json.Marshal(repository)
		if err != nil {
			return
		}
		fmt.Println(string(bs))
	}

	if *jsonflag && (len(*srctoolflag)) > 0 {
		var r *bufio.Reader
		if *srctoolflag == strings.ToLower("stdin") {
			// read from stdin
			r = bufio.NewReader(os.Stdin)
		} else {
			// read from srctool json file
			var f *os.File
			if f, err = os.Open(*srctoolflag); err != nil {
				return
			}
			r = bufio.NewReader(f)
		}

		buf := new(bytes.Buffer)
		if _, err = io.Copy(buf, r); err != nil {
			fail(err)
			return
		}

		bs := buf.Bytes()
		var p *src.Project
		p, err = src.Unmarshal(bs)
		if err != nil {
			return
		}

		p.Repo = repository.GetRepository()
		bs, err = src.Marshal(p)
		if err != nil {
			return
		}

		fmt.Println(string(bs))
	}

	if *dbflag {
		var db *sql.DB
		db, err = openDBSession(cfg.Database)
		if err != nil {
			return
		}
		defer db.Close()

		fmt.Fprintf(os.Stderr,
			"inserting %d commits from %s repository into the database...\n",
			len(repository.GetCommits()), repository.GetName())
		tic := time.Now()
		err = insertRepoData(db, repository)
		toc := time.Now()
		if err != nil {
			return
		}
		fmt.Fprintln(os.Stderr, "done in ", toc.Sub(tic))
	}
}

// fatal prints an error on standard error stream and exits.
func fatal(a ...interface{}) {
	fmt.Fprintln(os.Stderr, a...)
	os.Exit(1)
}

// fail prints an error on standard error stream.
func fail(a ...interface{}) {
	fmt.Fprintln(os.Stderr, a...)
}

// openDBSession creates a session to the database.
func openDBSession(cfg config.DatabaseConfig) (*sql.DB, error) {
	dbURL := fmt.Sprintf(
		"user='%s' password='%s' host='%s' port=%d dbname='%s' sslmode='%s'",
		cfg.UserName, cfg.Password, cfg.HostName, cfg.Port, cfg.DBName, cfg.SSLMode)

	return sql.Open("postgres", dbURL)
}

// insertRepoData inserts repository data into the database, or updates it
// if it is already there.
func insertRepoData(db *sql.DB, r repo.Repo) error {
	if db == nil {
		return errors.New("nil database given")
	}

	repoID, err := getRepoID(db, r)
	if err != nil {
		return err
	}
	if repoID == nil {
		return errors.New("cannot find corresponding repository in database")
	}

	userIDs, err := getAllUsers(db)
	if err != nil {
		return err
	}

	tx, err := db.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	commitStmt, err := tx.Prepare(genInsQuery("commits", commitFields...) + " RETURNING id")
	if err != nil {
		return err
	}

	deltaStmt, err := tx.Prepare(genInsQuery("commit_diff_deltas", diffDeltaFields...))
	if err != nil {
		return err
	}

	for _, c := range r.GetCommits() {
		if err := insertCommit(userIDs, *repoID, c, tx, commitStmt, deltaStmt); err != nil {
			return err
		}
	}

	if err := commitStmt.Close(); err != nil {
		return err
	}
	if err := deltaStmt.Close(); err != nil {
		return err
	}
	if err := tx.Commit(); err != nil {
		return err
	}

	return nil
}

// insertCommit inserts a commit into the database
func insertCommit(userIDs map[string]uint64, repoID uint64, c model.Commit, tx *sql.Tx, commitStmt, deltaStmt *sql.Stmt) error {
	authorID := userIDs[c.Author.Email]
	committerID := userIDs[c.Committer.Email]
	hash := genCommitHash(c)

	var commitID uint64
	err := commitStmt.QueryRow(
		repoID, authorID, committerID, hash,
		c.VCSID, c.Message, c.AuthorDate, c.CommitDate,
		c.FileChangedCount, c.InsertionsCount, c.DeletionsCount).Scan(&commitID)
	if err != nil {
		return err
	}

	for _, d := range c.DiffDelta {
		if err := insertDiffDelta(commitID, d, deltaStmt); err != nil {
			return err
		}
	}

	return nil
}

// insertDiffDelta inserts a commit diff delta into the database.
func insertDiffDelta(commitID uint64, d model.DiffDelta, stmt *sql.Stmt) error {
	_, err := stmt.Exec(commitID, d.Status, d.Binary, d.Similarity, d.OldFilePath, d.NewFilePath)
	if err != nil {
		return err
	}
	return nil
}

// getAllUsers returns a map of all users IDs with their email address as keys.
func getAllUsers(db *sql.DB) (map[string]uint64, error) {
	rows, err := db.Query("SELECT id, email FROM users WHERE email IS NOT NULL AND email != ''")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	userIDs := map[string]uint64{}
	for rows.Next() {
		var email string
		var id uint64
		if err := rows.Scan(&id, &email); err != nil {
			return nil, err
		}
		userIDs[email] = id
	}

	return userIDs, nil
}

// genCommitHash generates a hash (mmh3) from commit fields.
// This hash can then be used to uniquely identify a commit.
// Typically, we want to make sure not to insert twice the same commit into the
// database after an eventual second repotool run on the same repository.
func genCommitHash(c model.Commit) string {
	h := mmh3.New128()

	io.WriteString(h, c.VCSID)
	io.WriteString(h, c.Message)
	io.WriteString(h, c.Author.Name)
	io.WriteString(h, c.Author.Email)
	io.WriteString(h, c.Committer.Name)
	io.WriteString(h, c.Committer.Email)
	io.WriteString(h, c.AuthorDate.String())
	io.WriteString(h, c.CommitDate.String())
	io.WriteString(h, strconv.FormatInt(int64(c.FileChangedCount), 10))
	io.WriteString(h, strconv.FormatInt(int64(c.InsertionsCount), 10))
	io.WriteString(h, strconv.FormatInt(int64(c.DeletionsCount), 10))

	return hex.EncodeToString(h.Sum(nil))
}

// getRepoID returns the repository id of a repo in repositories table.
// If repo is not in the table, then 0 is returned.
func getRepoID(db *sql.DB, r repo.Repo) (*uint64, error) {
	if db == nil {
		return nil, errors.New("nil database given")
	}

	var id *uint64
	// Clone URL is unique
	err := db.QueryRow("SELECT id FROM repositories WHERE clone_url=$1", r.GetCloneURL()).Scan(&id)
	switch {
	case err == sql.ErrNoRows:
		return nil, nil
	case err != nil:
		return nil, err
	}
	return id, nil
}

// genInsQuery generates a query string for an insertion in the database.
func genInsQuery(tableName string, fields ...string) string {
	var buf bytes.Buffer

	buf.WriteString(fmt.Sprintf("INSERT INTO %s(%s)\n",
		tableName, strings.Join(fields, ",")))
	buf.WriteString("VALUES(")

	for ind := range fields {
		if ind > 0 {
			buf.WriteString(",")
		}

		buf.WriteString(fmt.Sprintf("$%d", ind+1))
	}

	buf.WriteString(")\n")

	return buf.String()
}
<|endoftext|>