<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 apiserver

import (
	"net/http"
	"os"
	"time"

	"k8s.io/apimachinery/pkg/apimachinery/announced"
	"k8s.io/apimachinery/pkg/apimachinery/registered"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/util/sets"
	genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
	"k8s.io/apiserver/pkg/registry/rest"
	genericapiserver "k8s.io/apiserver/pkg/server"
	genericfilters "k8s.io/apiserver/pkg/server/filters"
	kubeinformers "k8s.io/client-go/informers"
	kubeclientset "k8s.io/client-go/kubernetes"
	v1listers "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/pkg/version"

	"k8s.io/kube-aggregator/pkg/apis/apiregistration"
	"k8s.io/kube-aggregator/pkg/apis/apiregistration/install"
	"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1alpha1"
	"k8s.io/kube-aggregator/pkg/client/clientset_generated/internalclientset"
	informers "k8s.io/kube-aggregator/pkg/client/informers/internalversion"
	listers "k8s.io/kube-aggregator/pkg/client/listers/apiregistration/internalversion"
	apiservicestorage "k8s.io/kube-aggregator/pkg/registry/apiservice/etcd"
)

var (
	groupFactoryRegistry = make(announced.APIGroupFactoryRegistry)
	registry             = registered.NewOrDie("")
	Scheme               = runtime.NewScheme()
	Codecs               = serializer.NewCodecFactory(Scheme)
)

func init() {
	install.Install(groupFactoryRegistry, registry, Scheme)

	// we need to add the options (like ListOptions) to empty v1
	metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Group: "", Version: "v1"})

	unversioned := schema.GroupVersion{Group: "", Version: "v1"}
	Scheme.AddUnversionedTypes(unversioned,
		&metav1.Status{},
		&metav1.APIVersions{},
		&metav1.APIGroupList{},
		&metav1.APIGroup{},
		&metav1.APIResourceList{},
	)
}

// legacyAPIServiceName is the fixed name of the only non-groupified API version
const legacyAPIServiceName = "v1."

type Config struct {
	GenericConfig       *genericapiserver.Config
	CoreAPIServerClient kubeclientset.Interface

	// ProxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use
	// this to confirm the proxy's identity
	ProxyClientCert []byte
	ProxyClientKey  []byte
}

// APIAggregator contains state for a Kubernetes cluster master/api server.
type APIAggregator struct {
	GenericAPIServer *genericapiserver.GenericAPIServer

	contextMapper genericapirequest.RequestContextMapper

	handlerChainConfig *handlerChainConfig

	// proxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use
	// this to confirm the proxy's identity
	proxyClientCert []byte
	proxyClientKey  []byte

	// proxyHandlers are the proxy handlers that are currently registered, keyed by apiservice.name
	proxyHandlers map[string]*proxyHandler
	// handledGroups are the groups that already have routes
	handledGroups sets.String

	// lister is used to add group handling for /apis/<group> aggregator lookups based on
	// controller state
	lister listers.APIServiceLister

	// serviceLister is used by the aggregator handler to determine whether or not to try to expose the group
	serviceLister v1listers.ServiceLister
	// endpointsLister is used by the aggregator handler to determine whether or not to try to expose the group
	endpointsLister v1listers.EndpointsLister

	// proxyMux intercepts requests that need to be proxied to backing API servers
	proxyMux *http.ServeMux
}

type completedConfig struct {
	*Config
}

// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
func (c *Config) Complete() completedConfig {
	c.GenericConfig.Complete()

	version := version.Get()
	c.GenericConfig.Version = &version

	return completedConfig{c}
}

// SkipComplete provides a way to construct a server instance without config completion.
func (c *Config) SkipComplete() completedConfig {
	return completedConfig{c}
}

// New returns a new instance of APIAggregator from the given config.
func (c completedConfig) New(stopCh <-chan struct{}) (*APIAggregator, error) {
	informerFactory := informers.NewSharedInformerFactory(
		internalclientset.NewForConfigOrDie(c.Config.GenericConfig.LoopbackClientConfig),
		5*time.Minute, // this is effectively used as a refresh interval right now.  Might want to do something nicer later on.
	)
	kubeInformers := kubeinformers.NewSharedInformerFactory(c.CoreAPIServerClient, 5*time.Minute)

	proxyMux := http.NewServeMux()

	handlerChainConfig := &handlerChainConfig{
		informers:       informerFactory,
		proxyMux:        proxyMux,
		serviceLister:   kubeInformers.Core().V1().Services().Lister(),
		endpointsLister: kubeInformers.Core().V1().Endpoints().Lister(),
	}
	// most API servers don't need to do this, but we need a custom handler chain to handle the special /apis handling here
	c.Config.GenericConfig.BuildHandlerChainsFunc = handlerChainConfig.handlerChain

	genericServer, err := c.Config.GenericConfig.SkipComplete().New() // completion is done in Complete, no need for a second time
	if err != nil {
		return nil, err
	}

	s := &APIAggregator{
		GenericAPIServer:   genericServer,
		contextMapper:      c.GenericConfig.RequestContextMapper,
		handlerChainConfig: handlerChainConfig,
		proxyClientCert:    c.ProxyClientCert,
		proxyClientKey:     c.ProxyClientKey,
		proxyHandlers:      map[string]*proxyHandler{},
		handledGroups:      sets.String{},
		lister:             informerFactory.Apiregistration().InternalVersion().APIServices().Lister(),
		serviceLister:      kubeInformers.Core().V1().Services().Lister(),
		endpointsLister:    kubeInformers.Core().V1().Endpoints().Lister(),
		proxyMux:           proxyMux,
	}

	apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiregistration.GroupName, registry, Scheme, metav1.ParameterCodec, Codecs)
	apiGroupInfo.GroupMeta.GroupVersion = v1alpha1.SchemeGroupVersion
	v1alpha1storage := map[string]rest.Storage{}
	v1alpha1storage["apiservices"] = apiservicestorage.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter)
	apiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = v1alpha1storage

	if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
		return nil, err
	}

	apiserviceRegistrationController := NewAPIServiceRegistrationController(informerFactory.Apiregistration().InternalVersion().APIServices(), s)

	s.GenericAPIServer.AddPostStartHook("start-kube-aggregator-informers", func(context genericapiserver.PostStartHookContext) error {
		informerFactory.Start(stopCh)
		kubeInformers.Start(stopCh)
		return nil
	})
	s.GenericAPIServer.AddPostStartHook("apiservice-registration-controller", func(context genericapiserver.PostStartHookContext) error {
		apiserviceRegistrationController.Run(stopCh)
		return nil
	})

	return s, nil
}

// handlerChainConfig is the config used to build the custom handler chain for this api server
type handlerChainConfig struct {
	informers       informers.SharedInformerFactory
	proxyMux        *http.ServeMux
	serviceLister   v1listers.ServiceLister
	endpointsLister v1listers.EndpointsLister

	// fallThroughHandler keeps track of the handler that comes after the proxy so that it can be used
	// to satisfy delegation cases.  It is set as a side-effect while building the handler chain.
	// TODO refactor to run the proxy *after* authorization so we can simply run at the end of the handling chain
	fallThroughHandler http.Handler
}

// handlerChain is a method to build the handler chain for this API server.  We need a custom handler chain so that we
// can have custom handling for `/apis`, since we're hosting aggregation differently from anyone else and we're hosting
// the endpoints differently, since we're proxying all groups except for apiregistration.k8s.io.
func (h *handlerChainConfig) handlerChain(apiHandler http.Handler, c *genericapiserver.Config) (secure, insecure http.Handler) {
	// add this as a filter so that we never collide with "already registered" failures on `/apis`
	handler := WithAPIs(apiHandler, Codecs, h.informers.Apiregistration().InternalVersion().APIServices(), h.serviceLister, h.endpointsLister)

	handler = genericapifilters.WithAuthorization(handler, c.RequestContextMapper, c.Authorizer)
	h.fallThroughHandler = handler

	// this mux is NOT protected by authorization, but DOES have authentication information
	// this is so that everyone can hit the proxy and we can properly identify the user.  The backing
	// API server will deal with authorization
	handler = WithProxyMux(handler, h.proxyMux)

	handler = genericapifilters.WithImpersonation(handler, c.RequestContextMapper, c.Authorizer)
	// audit to stdout to help with debugging as we get this started
	handler = genericapifilters.WithAudit(handler, c.RequestContextMapper, os.Stdout)
	handler = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, genericapifilters.Unauthorized(c.SupportsBasicAuth))

	handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
	handler = genericfilters.WithPanicRecovery(handler, c.RequestContextMapper)
	handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.RequestContextMapper, c.LongRunningFunc)
	handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.RequestContextMapper, c.LongRunningFunc)
	handler = genericapifilters.WithRequestInfo(handler, genericapiserver.NewRequestInfoResolver(c), c.RequestContextMapper)
	handler = genericapirequest.WithRequestContext(handler, c.RequestContextMapper)

	return handler, nil
}

// AddAPIService adds an API service.  It is not thread-safe, so only call it on one thread at a time please.
// It's a slow moving API, so its ok to run the controller on a single thread
func (s *APIAggregator) AddAPIService(apiService *apiregistration.APIService) {
	// if the proxyHandler already exists, it needs to be updated. The aggregation bits do not
	// since they are wired against listers because they require multiple resources to respond
	if proxyHandler, exists := s.proxyHandlers[apiService.Name]; exists {
		proxyHandler.updateAPIService(apiService)
		return
	}

	proxyPath := "/apis/" + apiService.Spec.Group + "/" + apiService.Spec.Version
	// v1. is a special case for the legacy API.  It proxies to a wider set of endpoints.
	if apiService.Name == "v1." {
		proxyPath = "/api"
	}

	// register the proxy handler
	proxyHandler := &proxyHandler{
		contextMapper:   s.contextMapper,
		localDelegate:   s.handlerChainConfig.fallThroughHandler,
		proxyClientCert: s.proxyClientCert,
		proxyClientKey:  s.proxyClientKey,
	}
	proxyHandler.updateAPIService(apiService)
	s.proxyHandlers[apiService.Name] = proxyHandler
	s.proxyMux.Handle(proxyPath, proxyHandler)
	s.proxyMux.Handle(proxyPath+"/", proxyHandler)

	// if we're dealing with the legacy group, we're done here
	if apiService.Name == legacyAPIServiceName {
		return
	}

	// if we've already registered the path with the handler, we don't want to do it again.
	if s.handledGroups.Has(apiService.Spec.Group) {
		return
	}

	// it's time to register the group aggregation endpoint
	groupPath := "/apis/" + apiService.Spec.Group
	groupDiscoveryHandler := &apiGroupHandler{
		codecs:          Codecs,
		groupName:       apiService.Spec.Group,
		lister:          s.lister,
		serviceLister:   s.serviceLister,
		endpointsLister: s.endpointsLister,
	}
	// aggregation is protected
	s.GenericAPIServer.HandlerContainer.UnlistedRoutes.Handle(groupPath, groupDiscoveryHandler)
	s.GenericAPIServer.HandlerContainer.UnlistedRoutes.Handle(groupPath+"/", groupDiscoveryHandler)
	s.handledGroups.Insert(apiService.Spec.Group)
}

// RemoveAPIService removes the APIService from being handled.  Later on it will disable the proxy endpoint.
// Right now it does nothing because our handler has to properly 404 itself since muxes don't unregister
func (s *APIAggregator) RemoveAPIService(apiServiceName string) {
	proxyHandler, exists := s.proxyHandlers[apiServiceName]
	if !exists {
		return
	}
	proxyHandler.removeAPIService()
}

func WithProxyMux(handler http.Handler, mux *http.ServeMux) http.Handler {
	if mux == nil {
		return handler
	}

	// register the handler at this stage against everything under slash.  More specific paths that get registered will take precedence
	// this effectively delegates by default unless something specific gets registered.
	mux.Handle("/", handler)

	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		mux.ServeHTTP(w, req)
	})
}
<commit_msg>allow fallthrough handling from go-restful routes<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 apiserver

import (
	"net/http"
	"os"
	"time"

	"k8s.io/apimachinery/pkg/apimachinery/announced"
	"k8s.io/apimachinery/pkg/apimachinery/registered"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/util/sets"
	genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
	"k8s.io/apiserver/pkg/registry/rest"
	genericapiserver "k8s.io/apiserver/pkg/server"
	genericfilters "k8s.io/apiserver/pkg/server/filters"
	kubeinformers "k8s.io/client-go/informers"
	kubeclientset "k8s.io/client-go/kubernetes"
	v1listers "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/pkg/version"

	"k8s.io/kube-aggregator/pkg/apis/apiregistration"
	"k8s.io/kube-aggregator/pkg/apis/apiregistration/install"
	"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1alpha1"
	"k8s.io/kube-aggregator/pkg/client/clientset_generated/internalclientset"
	informers "k8s.io/kube-aggregator/pkg/client/informers/internalversion"
	listers "k8s.io/kube-aggregator/pkg/client/listers/apiregistration/internalversion"
	apiservicestorage "k8s.io/kube-aggregator/pkg/registry/apiservice/etcd"
)

var (
	groupFactoryRegistry = make(announced.APIGroupFactoryRegistry)
	registry             = registered.NewOrDie("")
	Scheme               = runtime.NewScheme()
	Codecs               = serializer.NewCodecFactory(Scheme)
)

func init() {
	install.Install(groupFactoryRegistry, registry, Scheme)

	// we need to add the options (like ListOptions) to empty v1
	metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Group: "", Version: "v1"})

	unversioned := schema.GroupVersion{Group: "", Version: "v1"}
	Scheme.AddUnversionedTypes(unversioned,
		&metav1.Status{},
		&metav1.APIVersions{},
		&metav1.APIGroupList{},
		&metav1.APIGroup{},
		&metav1.APIResourceList{},
	)
}

// legacyAPIServiceName is the fixed name of the only non-groupified API version
const legacyAPIServiceName = "v1."

type Config struct {
	GenericConfig       *genericapiserver.Config
	CoreAPIServerClient kubeclientset.Interface

	// ProxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use
	// this to confirm the proxy's identity
	ProxyClientCert []byte
	ProxyClientKey  []byte
}

// APIAggregator contains state for a Kubernetes cluster master/api server.
type APIAggregator struct {
	GenericAPIServer *genericapiserver.GenericAPIServer

	contextMapper genericapirequest.RequestContextMapper

	handlerChainConfig *handlerChainConfig

	// proxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use
	// this to confirm the proxy's identity
	proxyClientCert []byte
	proxyClientKey  []byte

	// proxyHandlers are the proxy handlers that are currently registered, keyed by apiservice.name
	proxyHandlers map[string]*proxyHandler
	// handledGroups are the groups that already have routes
	handledGroups sets.String

	// lister is used to add group handling for /apis/<group> aggregator lookups based on
	// controller state
	lister listers.APIServiceLister

	// serviceLister is used by the aggregator handler to determine whether or not to try to expose the group
	serviceLister v1listers.ServiceLister
	// endpointsLister is used by the aggregator handler to determine whether or not to try to expose the group
	endpointsLister v1listers.EndpointsLister

	// proxyMux intercepts requests that need to be proxied to backing API servers
	proxyMux *http.ServeMux
}

type completedConfig struct {
	*Config
}

// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
func (c *Config) Complete() completedConfig {
	c.GenericConfig.Complete()

	version := version.Get()
	c.GenericConfig.Version = &version

	return completedConfig{c}
}

// SkipComplete provides a way to construct a server instance without config completion.
func (c *Config) SkipComplete() completedConfig {
	return completedConfig{c}
}

// New returns a new instance of APIAggregator from the given config.
func (c completedConfig) New(stopCh <-chan struct{}) (*APIAggregator, error) {
	informerFactory := informers.NewSharedInformerFactory(
		internalclientset.NewForConfigOrDie(c.Config.GenericConfig.LoopbackClientConfig),
		5*time.Minute, // this is effectively used as a refresh interval right now.  Might want to do something nicer later on.
	)
	kubeInformers := kubeinformers.NewSharedInformerFactory(c.CoreAPIServerClient, 5*time.Minute)

	proxyMux := http.NewServeMux()

	handlerChainConfig := &handlerChainConfig{
		informers:       informerFactory,
		proxyMux:        proxyMux,
		serviceLister:   kubeInformers.Core().V1().Services().Lister(),
		endpointsLister: kubeInformers.Core().V1().Endpoints().Lister(),
	}
	// most API servers don't need to do this, but we need a custom handler chain to handle the special /apis handling here
	c.Config.GenericConfig.BuildHandlerChainsFunc = handlerChainConfig.handlerChain

	genericServer, err := c.Config.GenericConfig.SkipComplete().New() // completion is done in Complete, no need for a second time
	if err != nil {
		return nil, err
	}

	s := &APIAggregator{
		GenericAPIServer:   genericServer,
		contextMapper:      c.GenericConfig.RequestContextMapper,
		handlerChainConfig: handlerChainConfig,
		proxyClientCert:    c.ProxyClientCert,
		proxyClientKey:     c.ProxyClientKey,
		proxyHandlers:      map[string]*proxyHandler{},
		handledGroups:      sets.String{},
		lister:             informerFactory.Apiregistration().InternalVersion().APIServices().Lister(),
		serviceLister:      kubeInformers.Core().V1().Services().Lister(),
		endpointsLister:    kubeInformers.Core().V1().Endpoints().Lister(),
		proxyMux:           proxyMux,
	}

	apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiregistration.GroupName, registry, Scheme, metav1.ParameterCodec, Codecs)
	apiGroupInfo.GroupMeta.GroupVersion = v1alpha1.SchemeGroupVersion
	v1alpha1storage := map[string]rest.Storage{}
	v1alpha1storage["apiservices"] = apiservicestorage.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter)
	apiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = v1alpha1storage

	if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
		return nil, err
	}

	apiserviceRegistrationController := NewAPIServiceRegistrationController(informerFactory.Apiregistration().InternalVersion().APIServices(), s)

	s.GenericAPIServer.AddPostStartHook("start-kube-aggregator-informers", func(context genericapiserver.PostStartHookContext) error {
		informerFactory.Start(stopCh)
		kubeInformers.Start(stopCh)
		return nil
	})
	s.GenericAPIServer.AddPostStartHook("apiservice-registration-controller", func(context genericapiserver.PostStartHookContext) error {
		apiserviceRegistrationController.Run(stopCh)
		return nil
	})

	return s, nil
}

// handlerChainConfig is the config used to build the custom handler chain for this api server
type handlerChainConfig struct {
	informers       informers.SharedInformerFactory
	proxyMux        *http.ServeMux
	serviceLister   v1listers.ServiceLister
	endpointsLister v1listers.EndpointsLister

	// fallThroughHandler keeps track of the handler that comes after the proxy so that it can be used
	// to satisfy delegation cases.  It is set as a side-effect while building the handler chain.
	// TODO refactor to run the proxy *after* authorization so we can simply run at the end of the handling chain
	fallThroughHandler http.Handler
}

// handlerChain is a method to build the handler chain for this API server.  We need a custom handler chain so that we
// can have custom handling for `/apis`, since we're hosting aggregation differently from anyone else and we're hosting
// the endpoints differently, since we're proxying all groups except for apiregistration.k8s.io.
func (h *handlerChainConfig) handlerChain(apiHandler http.Handler, c *genericapiserver.Config) (secure, insecure http.Handler) {
	// add this as a filter so that we never collide with "already registered" failures on `/apis`
	handler := WithAPIs(apiHandler, Codecs, h.informers.Apiregistration().InternalVersion().APIServices(), h.serviceLister, h.endpointsLister)

	handler = genericapifilters.WithAuthorization(handler, c.RequestContextMapper, c.Authorizer)
	h.fallThroughHandler = handler

	// this mux is NOT protected by authorization, but DOES have authentication information
	// this is so that everyone can hit the proxy and we can properly identify the user.  The backing
	// API server will deal with authorization
	handler = WithProxyMux(handler, h.proxyMux)

	handler = genericapifilters.WithImpersonation(handler, c.RequestContextMapper, c.Authorizer)
	// audit to stdout to help with debugging as we get this started
	handler = genericapifilters.WithAudit(handler, c.RequestContextMapper, os.Stdout)
	handler = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, genericapifilters.Unauthorized(c.SupportsBasicAuth))

	handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
	handler = genericfilters.WithPanicRecovery(handler, c.RequestContextMapper)
	handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.RequestContextMapper, c.LongRunningFunc)
	handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.RequestContextMapper, c.LongRunningFunc)
	handler = genericapifilters.WithRequestInfo(handler, genericapiserver.NewRequestInfoResolver(c), c.RequestContextMapper)
	handler = genericapirequest.WithRequestContext(handler, c.RequestContextMapper)

	return handler, nil
}

// AddAPIService adds an API service.  It is not thread-safe, so only call it on one thread at a time please.
// It's a slow moving API, so its ok to run the controller on a single thread
func (s *APIAggregator) AddAPIService(apiService *apiregistration.APIService) {
	// if the proxyHandler already exists, it needs to be updated. The aggregation bits do not
	// since they are wired against listers because they require multiple resources to respond
	if proxyHandler, exists := s.proxyHandlers[apiService.Name]; exists {
		proxyHandler.updateAPIService(apiService)
		return
	}

	proxyPath := "/apis/" + apiService.Spec.Group + "/" + apiService.Spec.Version
	// v1. is a special case for the legacy API.  It proxies to a wider set of endpoints.
	if apiService.Name == "v1." {
		proxyPath = "/api"
	}

	// register the proxy handler
	proxyHandler := &proxyHandler{
		contextMapper:   s.contextMapper,
		localDelegate:   s.handlerChainConfig.fallThroughHandler,
		proxyClientCert: s.proxyClientCert,
		proxyClientKey:  s.proxyClientKey,
	}
	proxyHandler.updateAPIService(apiService)
	s.proxyHandlers[apiService.Name] = proxyHandler
	s.proxyMux.Handle(proxyPath, proxyHandler)
	s.proxyMux.Handle(proxyPath+"/", proxyHandler)

	// if we're dealing with the legacy group, we're done here
	if apiService.Name == legacyAPIServiceName {
		return
	}

	// if we've already registered the path with the handler, we don't want to do it again.
	if s.handledGroups.Has(apiService.Spec.Group) {
		return
	}

	// it's time to register the group aggregation endpoint
	groupPath := "/apis/" + apiService.Spec.Group
	groupDiscoveryHandler := &apiGroupHandler{
		codecs:          Codecs,
		groupName:       apiService.Spec.Group,
		lister:          s.lister,
		serviceLister:   s.serviceLister,
		endpointsLister: s.endpointsLister,
	}
	// aggregation is protected
	s.GenericAPIServer.FallThroughHandler.UnlistedHandle(groupPath, groupDiscoveryHandler)
	s.GenericAPIServer.FallThroughHandler.UnlistedHandle(groupPath+"/", groupDiscoveryHandler)
	s.handledGroups.Insert(apiService.Spec.Group)
}

// RemoveAPIService removes the APIService from being handled.  Later on it will disable the proxy endpoint.
// Right now it does nothing because our handler has to properly 404 itself since muxes don't unregister
func (s *APIAggregator) RemoveAPIService(apiServiceName string) {
	proxyHandler, exists := s.proxyHandlers[apiServiceName]
	if !exists {
		return
	}
	proxyHandler.removeAPIService()
}

func WithProxyMux(handler http.Handler, mux *http.ServeMux) http.Handler {
	if mux == nil {
		return handler
	}

	// register the handler at this stage against everything under slash.  More specific paths that get registered will take precedence
	// this effectively delegates by default unless something specific gets registered.
	mux.Handle("/", handler)

	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		mux.ServeHTTP(w, req)
	})
}
<|endoftext|>


<commit_before>package csrf

import (
	"crypto/rand"
	"crypto/sha1"
	"crypto/subtle"
	"encoding/base64"
	"github.com/cssivision/looli"
	"net/http"
	"strings"
)

var (
	maxAge        = 12 * 3600
	cookieName    = "_csrf"
	formKey       = "csrf_token"
	headerKey     = "X-CSRF-Token"
	cookieOptions = &http.Cookie{}
)

type Options struct {
	FormKey   string
	HeaderKey string
	Skip      func(*looli.Context) bool
	MaxAge    int
	Domain    string
	Path      string
	HttpOnly  bool
	Secure    bool
}

func Default() looli.HandlerFunc {
	return New(Options{})
}

func New(options Options) looli.HandlerFunc {
	if options.FormKey == "" {
		options.FormKey = formKey
	}

	if options.HeaderKey == "" {
		options.HeaderKey = headerKey
	}

	if options.MaxAge == 0 {
		options.MaxAge = maxAge
	}

	cookieOptions.MaxAge = options.MaxAge
	cookieOptions.Path = options.Path
	cookieOptions.HttpOnly = options.HttpOnly
	cookieOptions.Secure = options.Secure
	cookieOptions.Domain = options.Domain

	return func(c *looli.Context) {
		if options.Skip != nil && options.Skip(c) {
			return
		}

		// Set the Vary: Cookie header to protect clients from caching the response.
		c.ResponseWriter.Header().Add("Vary", "Cookie")

		csrfToken := c.PostForm(options.FormKey)
		if csrfToken == "" {
			csrfToken = c.Header(options.HeaderKey)
		}

		if csrfToken == "" || !verify(getSecret(c), csrfToken) {
			c.AbortWithStatus(http.StatusForbidden)
			c.String("invalid csrf token")
			return
		}
	}
}

func getSecret(c *looli.Context) string {
	value, err := c.Cookie(cookieName)
	var secretCookie *http.Cookie
	if err != nil {
		*secretCookie = *cookieOptions
		secretCookie.Name = cookieName
		secretCookie.Value = randomKey(16)
		value = secretCookie.Value
		c.SetCookie(secretCookie)
	}
	return value
}

func NewToken(c *looli.Context) string {
	secret := getSecret(c)
	salt := randomKey(16)
	return generateWithSalt(secret, salt)
}

func randomKey(length int) string {
	b := make([]byte, length)
	rand.Read(b)
	return base64.RawURLEncoding.EncodeToString(b)
}

func verify(secret, token string) bool {
	i := strings.Index(token, ".")

	if i == -1 {
		return false
	}

	return subtle.ConstantTimeCompare([]byte(token), []byte(generateWithSalt(secret, token[0:i]))) == 1
}

func generateWithSalt(secret, salt string) string {
	h := sha1.New()
	h.Write([]byte(salt + "." + secret))

	return salt + "." + base64.RawURLEncoding.EncodeToString(h.Sum(nil))
}
<commit_msg>fix: cookieOptions nil<commit_after>package csrf

import (
	"crypto/rand"
	"crypto/sha1"
	"crypto/subtle"
	"encoding/base64"
	"github.com/cssivision/looli"
	"net/http"
	"strings"
)

var (
	maxAge        = 12 * 3600
	cookieName    = "_csrf"
	formKey       = "csrf_token"
	headerKey     = "X-CSRF-Token"
	cookieOptions = &http.Cookie{}
)

type Options struct {
	FormKey   string
	HeaderKey string
	Skip      func(*looli.Context) bool
	MaxAge    int
	Domain    string
	Path      string
	HttpOnly  bool
	Secure    bool
}

func Default() looli.HandlerFunc {
	return New(Options{})
}

func New(options Options) looli.HandlerFunc {
	if options.FormKey == "" {
		options.FormKey = formKey
	}

	if options.HeaderKey == "" {
		options.HeaderKey = headerKey
	}

	if options.MaxAge == 0 {
		options.MaxAge = maxAge
	}

	cookieOptions.MaxAge = options.MaxAge
	cookieOptions.Path = options.Path
	cookieOptions.HttpOnly = options.HttpOnly
	cookieOptions.Secure = options.Secure
	cookieOptions.Domain = options.Domain

	return func(c *looli.Context) {
		if options.Skip != nil && options.Skip(c) {
			return
		}

		// Set the Vary: Cookie header to protect clients from caching the response.
		c.ResponseWriter.Header().Add("Vary", "Cookie")

		csrfToken := c.PostForm(options.FormKey)
		if csrfToken == "" {
			csrfToken = c.Header(options.HeaderKey)
		}

		if csrfToken == "" || !verify(getSecret(c), csrfToken) {
			c.AbortWithStatus(http.StatusForbidden)
			c.String("invalid csrf token")
			return
		}
	}
}

func getSecret(c *looli.Context) string {
	value, err := c.Cookie(cookieName)
	var secretCookie *http.Cookie
	if err != nil {
		secretCookie = cookieOptions
		secretCookie.Name = cookieName
		secretCookie.Value = randomKey(16)
		value = secretCookie.Value
		c.SetCookie(secretCookie)
	}
	return value
}

func NewToken(c *looli.Context) string {
	secret := getSecret(c)
	salt := randomKey(16)
	return generateWithSalt(secret, salt)
}

func randomKey(length int) string {
	b := make([]byte, length)
	rand.Read(b)
	return base64.RawURLEncoding.EncodeToString(b)
}

func verify(secret, token string) bool {
	i := strings.Index(token, ".")

	if i == -1 {
		return false
	}

	return subtle.ConstantTimeCompare([]byte(token), []byte(generateWithSalt(secret, token[0:i]))) == 1
}

func generateWithSalt(secret, salt string) string {
	h := sha1.New()
	h.Write([]byte(salt + "." + secret))

	return salt + "." + base64.RawURLEncoding.EncodeToString(h.Sum(nil))
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 JT Olds
// See LICENSE for copying information

package lincs_gse92742_v0

import (
	"bufio"
	"database/sql"
	"flag"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/jtolds/golincs/mmm"
	"github.com/jtolds/golincs/web/dbs"
	_ "github.com/mattn/go-sqlite3"
	"github.com/spacemonkeygo/errors"
	"github.com/spacemonkeygo/spacelog"
)

var (
	db = flag.String("gse92742.db_path", "/home/jt/school/bio/gse92742/db.db",
		"path to connect to the metadata db")
	driver     = flag.String("gse92742.db_driver", "sqlite3", "database driver")
	samplePath = flag.String("gse92742.samples",
		"/home/jt/school/bio/gse92742/filtered-unit.mmap",
		"path to sample data")
	genesigPath = flag.String("gse92742.gene_sigs",
		"/home/jt/school/bio/gse92742/filtered-unit-sh_and_oe-grouped.mmap",
		"path to gene signature data")
	msigdb = flag.String("gse92742.msigdb",
		"/home/jt/school/bio/msigdb.v6.0.symbols.gmt", "path to gene sets (gmt)")

	logger = spacelog.GetLogger()
)

type Dataset struct {
	db *sql.DB
	tx *sql.Tx

	samples  *mmm.Handle
	genesigs *mmm.Handle
	genesets []*geneset

	dimensionMap        []string
	dimensionMapReverse map[string]int
	geneSigsByName      map[string]mmm.Ident
}

var _ dbs.Dataset = (*Dataset)(nil)

func New() (*Dataset, error) {
	ds := &Dataset{}
	var success bool
	defer func() {
		if !success {
			ds.Close()
		}
	}()

	db, err := sql.Open(*driver, *db)
	if err != nil {
		return nil, err
	}
	ds.db = db
	tx, err := db.Begin()
	if err != nil {
		return nil, err
	}
	ds.tx = tx

	sample_fh, err := mmm.Open(*samplePath)
	if err != nil {
		return nil, err
	}
	ds.samples = sample_fh

	genesig_fh, err := mmm.Open(*genesigPath)
	if err != nil {
		return nil, err
	}
	ds.genesigs = genesig_fh

	if genesig_fh.Cols() != sample_fh.Cols() {
		return nil, fmt.Errorf("gene sig and sample data column mismatch")
	}
	for idx, col := range genesig_fh.ColIds() {
		if sample_fh.ColIds()[idx] != col {
			return nil, fmt.Errorf("gene sig and sample data column mismatch")
		}
	}

	ds.dimensionMap = make([]string, sample_fh.Cols())
	ds.dimensionMapReverse = make(map[string]int, sample_fh.Cols())
	rows, err := tx.Query("SELECT id, pr_gene_id FROM dimensions")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var id mmm.Ident
		var gene_id string
		err = rows.Scan(&id, &gene_id)
		if err != nil {
			return nil, err
		}

		idx, found := sample_fh.ColIdxById(id)
		if !found {
			continue
		}

		var gene_symbol string
		err := tx.QueryRow("SELECT pr_gene_symbol FROM pr_gene WHERE "+
			"pr_gene_id = ?", gene_id).Scan(&gene_symbol)
		if err != nil {
			return nil, err
		}

		ds.dimensionMap[idx] = gene_symbol
		ds.dimensionMapReverse[gene_symbol] = idx
	}
	err = rows.Err()
	if err != nil {
		return nil, err
	}

	ds.geneSigsByName = map[string]mmm.Ident{}
	for i := 0; i < ds.genesigs.Rows(); i++ {
		s, err := ds.byIdx(ds.genesigs, i, false)
		if err != nil {
			return nil, err
		}
		ds.geneSigsByName[s.name] = s.mmm_id
	}

	gsfh, err := os.Open(*msigdb)
	if err != nil {
		return nil, err
	}
	defer gsfh.Close()

	gsfhb := bufio.NewReader(gsfh)
	possible := 0
	for {
		line, err := gsfhb.ReadString('\n')
		if err != nil && err != io.EOF {
			return nil, err
		}
		parts := strings.Split(strings.TrimSpace(line), "\t")
		if len(parts) > 2 {
			possible += 1
			var cleaned []string
			for _, gene := range parts[2:] {
				if _, exists := ds.geneSigsByName[gene]; exists {
					cleaned = append(cleaned, gene)
				}
			}
			if len(cleaned) > 0 {
				ds.genesets = append(ds.genesets, &geneset{
					id:    len(ds.genesets),
					name:  parts[0],
					desc:  parts[1],
					genes: cleaned,
					ds:    ds,
				})
			}
		}
		if err == io.EOF {
			break
		}
	}
	logger.Noticef("loaded %d genesets out of %d possible\n",
		len(ds.genesets), possible)

	success = true
	return ds, nil
}

func (ds *Dataset) Close() error {
	var errs errors.ErrorGroup
	if ds.samples != nil {
		errs.Add(ds.samples.Close())
		ds.samples = nil
	}
	if ds.genesigs != nil {
		errs.Add(ds.genesigs.Close())
		ds.genesigs = nil
	}
	if ds.tx != nil {
		errs.Add(ds.tx.Rollback())
		ds.tx = nil
	}
	if ds.db != nil {
		errs.Add(ds.db.Close())
		ds.db = nil
	}
	ds.genesets = nil
	ds.dimensionMap = nil
	ds.dimensionMapReverse = nil
	ds.geneSigsByName = nil
	return errs.Finalize()
}

func (ds *Dataset) Name() string {
	return "LINCS Phase 1 v0"
}

func (ds *Dataset) Dimensions() int {
	return ds.samples.Cols()
}

func (ds *Dataset) Samples() int {
	return ds.samples.Rows()
}

func (ds *Dataset) GeneSigs() int {
	return ds.genesigs.Rows()
}

func (ds *Dataset) Genesets() int { return len(ds.genesets) }

func (ds *Dataset) DimMax() float64 { return 1 }

func (ds *Dataset) SampleTagNames() []string {
	return []string{
		// sig_id skipped
		"pert_id", "pert_type", "cell_id", "pert_idose",
		"pert_itime", "is_touchstone"}
}

func (ds *Dataset) list(h *mmm.Handle, offset, limit int, tags bool) (
	rv []*sample, err error) {
	for i := offset; i < offset+limit && i < h.Rows(); i++ {
		s, err := ds.byIdx(h, i, tags)
		if err != nil {
			return nil, err
		}
		rv = append(rv, s)
	}
	return rv, nil
}

func (ds *Dataset) ListGeneSigs(offset, limit int) ([]dbs.GeneSig, error) {
	rv, err := ds.list(ds.genesigs, offset, limit, false)
	return samplesToGeneSigs(rv), err
}

func (ds *Dataset) ListSamples(offset, limit int) (samples []dbs.Sample,
	err error) {
	rv, err := ds.list(ds.samples, offset, limit, true)
	return samplesToSamples(rv), err
}

func (ds *Dataset) ListGenesets(offset, limit int) (rv []dbs.Geneset,
	err error) {
	for i := offset; i < offset+limit && i < len(ds.genesets); i++ {
		rv = append(rv, ds.genesets[i])
	}
	return rv, nil
}

func (ds *Dataset) load(h *mmm.Handle, mmm_id mmm.Ident, tags bool) (
	rv *sample, found bool, err error) {
	values, found := h.RowById(mmm_id)
	if !found {
		return nil, false, nil
	}
	var pert_iname, pert_id, pert_type, cell_id, pert_idose, pert_itime,
		is_touchstone string
	if tags {
		err = ds.tx.QueryRow("SELECT sig.pert_iname, sig.pert_id, sig.pert_type, "+
			"sig.cell_id, sig.pert_idose, sig.pert_itime, sig.is_touchstone "+
			"FROM sig sig, signatures s WHERE s.sig_id = sig.sig_id AND s.id = ?",
			mmm_id).Scan(&pert_iname, &pert_id, &pert_type, &cell_id, &pert_idose,
			&pert_itime, &is_touchstone)
	} else {
		err = ds.tx.QueryRow("SELECT sig.pert_iname "+
			"FROM sig sig, signatures s WHERE s.sig_id = sig.sig_id AND s.id = ?",
			mmm_id).Scan(&pert_iname)
	}
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, false, nil
		}
		return nil, false, err
	}
	rv = &sample{
		mmm_id:       mmm_id,
		name:         pert_iname,
		tags:         map[string]string{},
		data:         values,
		dimensionMap: ds.dimensionMap,
	}
	if tags {
		rv.tags["pert_id"] = pert_id
		rv.tags["pert_type"] = pert_type
		rv.tags["cell_id"] = cell_id
		rv.tags["pert_idose"] = pert_idose
		rv.tags["pert_itime"] = pert_itime
		rv.tags["is_touchstone"] = is_touchstone
	}
	return rv, true, nil
}

func (ds *Dataset) byIdx(h *mmm.Handle, idx int, tags bool) (*sample, error) {
	s, found, err := ds.load(h, h.RowIdByIdx(idx), tags)
	return s, notFound(found, err)
}

func (ds *Dataset) GetGeneSig(geneSigId string) (dbs.GeneSig, error) {
	id, err := strconv.ParseUint(geneSigId, 10, 32)
	if err != nil {
		return nil, err
	}
	rv, found, err := ds.load(ds.genesigs, mmm.Ident(id), false)
	return rv, notFound(found, err)
}

func (ds *Dataset) GetSample(sampleId string) (dbs.Sample, error) {
	id, err := strconv.ParseUint(sampleId, 10, 32)
	if err != nil {
		return nil, err
	}
	rv, found, err := ds.load(ds.samples, mmm.Ident(id), true)
	return rv, notFound(found, err)
}
<commit_msg>minimum gene set size<commit_after>// Copyright (C) 2017 JT Olds
// See LICENSE for copying information

package lincs_gse92742_v0

import (
	"bufio"
	"database/sql"
	"flag"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/jtolds/golincs/mmm"
	"github.com/jtolds/golincs/web/dbs"
	_ "github.com/mattn/go-sqlite3"
	"github.com/spacemonkeygo/errors"
	"github.com/spacemonkeygo/spacelog"
)

const (
	minimumGeneSetSize = 10
)

var (
	db = flag.String("gse92742.db_path", "/home/jt/school/bio/gse92742/db.db",
		"path to connect to the metadata db")
	driver     = flag.String("gse92742.db_driver", "sqlite3", "database driver")
	samplePath = flag.String("gse92742.samples",
		"/home/jt/school/bio/gse92742/filtered-unit.mmap",
		"path to sample data")
	genesigPath = flag.String("gse92742.gene_sigs",
		"/home/jt/school/bio/gse92742/filtered-unit-sh_and_oe-grouped.mmap",
		"path to gene signature data")
	msigdb = flag.String("gse92742.msigdb",
		"/home/jt/school/bio/msigdb.v6.0.symbols.gmt", "path to gene sets (gmt)")

	logger = spacelog.GetLogger()
)

type Dataset struct {
	db *sql.DB
	tx *sql.Tx

	samples  *mmm.Handle
	genesigs *mmm.Handle
	genesets []*geneset

	dimensionMap        []string
	dimensionMapReverse map[string]int
	geneSigsByName      map[string]mmm.Ident
}

var _ dbs.Dataset = (*Dataset)(nil)

func New() (*Dataset, error) {
	ds := &Dataset{}
	var success bool
	defer func() {
		if !success {
			ds.Close()
		}
	}()

	db, err := sql.Open(*driver, *db)
	if err != nil {
		return nil, err
	}
	ds.db = db
	tx, err := db.Begin()
	if err != nil {
		return nil, err
	}
	ds.tx = tx

	sample_fh, err := mmm.Open(*samplePath)
	if err != nil {
		return nil, err
	}
	ds.samples = sample_fh

	genesig_fh, err := mmm.Open(*genesigPath)
	if err != nil {
		return nil, err
	}
	ds.genesigs = genesig_fh

	if genesig_fh.Cols() != sample_fh.Cols() {
		return nil, fmt.Errorf("gene sig and sample data column mismatch")
	}
	for idx, col := range genesig_fh.ColIds() {
		if sample_fh.ColIds()[idx] != col {
			return nil, fmt.Errorf("gene sig and sample data column mismatch")
		}
	}

	ds.dimensionMap = make([]string, sample_fh.Cols())
	ds.dimensionMapReverse = make(map[string]int, sample_fh.Cols())
	rows, err := tx.Query("SELECT id, pr_gene_id FROM dimensions")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var id mmm.Ident
		var gene_id string
		err = rows.Scan(&id, &gene_id)
		if err != nil {
			return nil, err
		}

		idx, found := sample_fh.ColIdxById(id)
		if !found {
			continue
		}

		var gene_symbol string
		err := tx.QueryRow("SELECT pr_gene_symbol FROM pr_gene WHERE "+
			"pr_gene_id = ?", gene_id).Scan(&gene_symbol)
		if err != nil {
			return nil, err
		}

		ds.dimensionMap[idx] = gene_symbol
		ds.dimensionMapReverse[gene_symbol] = idx
	}
	err = rows.Err()
	if err != nil {
		return nil, err
	}

	ds.geneSigsByName = map[string]mmm.Ident{}
	for i := 0; i < ds.genesigs.Rows(); i++ {
		s, err := ds.byIdx(ds.genesigs, i, false)
		if err != nil {
			return nil, err
		}
		ds.geneSigsByName[s.name] = s.mmm_id
	}

	gsfh, err := os.Open(*msigdb)
	if err != nil {
		return nil, err
	}
	defer gsfh.Close()

	gsfhb := bufio.NewReader(gsfh)
	possible := 0
	for {
		line, err := gsfhb.ReadString('\n')
		if err != nil && err != io.EOF {
			return nil, err
		}
		parts := strings.Split(strings.TrimSpace(line), "\t")
		if len(parts) > 2 {
			possible += 1
			var cleaned []string
			for _, gene := range parts[2:] {
				if _, exists := ds.geneSigsByName[gene]; exists {
					cleaned = append(cleaned, gene)
				}
			}
			if len(cleaned) > minimumGeneSetSize {
				ds.genesets = append(ds.genesets, &geneset{
					id:    len(ds.genesets),
					name:  parts[0],
					desc:  parts[1],
					genes: cleaned,
					ds:    ds,
				})
			}
		}
		if err == io.EOF {
			break
		}
	}
	logger.Noticef("loaded %d genesets out of %d possible\n",
		len(ds.genesets), possible)

	success = true
	return ds, nil
}

func (ds *Dataset) Close() error {
	var errs errors.ErrorGroup
	if ds.samples != nil {
		errs.Add(ds.samples.Close())
		ds.samples = nil
	}
	if ds.genesigs != nil {
		errs.Add(ds.genesigs.Close())
		ds.genesigs = nil
	}
	if ds.tx != nil {
		errs.Add(ds.tx.Rollback())
		ds.tx = nil
	}
	if ds.db != nil {
		errs.Add(ds.db.Close())
		ds.db = nil
	}
	ds.genesets = nil
	ds.dimensionMap = nil
	ds.dimensionMapReverse = nil
	ds.geneSigsByName = nil
	return errs.Finalize()
}

func (ds *Dataset) Name() string {
	return "LINCS Phase 1 v0"
}

func (ds *Dataset) Dimensions() int {
	return ds.samples.Cols()
}

func (ds *Dataset) Samples() int {
	return ds.samples.Rows()
}

func (ds *Dataset) GeneSigs() int {
	return ds.genesigs.Rows()
}

func (ds *Dataset) Genesets() int { return len(ds.genesets) }

func (ds *Dataset) DimMax() float64 { return 1 }

func (ds *Dataset) SampleTagNames() []string {
	return []string{
		// sig_id skipped
		"pert_id", "pert_type", "cell_id", "pert_idose",
		"pert_itime", "is_touchstone"}
}

func (ds *Dataset) list(h *mmm.Handle, offset, limit int, tags bool) (
	rv []*sample, err error) {
	for i := offset; i < offset+limit && i < h.Rows(); i++ {
		s, err := ds.byIdx(h, i, tags)
		if err != nil {
			return nil, err
		}
		rv = append(rv, s)
	}
	return rv, nil
}

func (ds *Dataset) ListGeneSigs(offset, limit int) ([]dbs.GeneSig, error) {
	rv, err := ds.list(ds.genesigs, offset, limit, false)
	return samplesToGeneSigs(rv), err
}

func (ds *Dataset) ListSamples(offset, limit int) (samples []dbs.Sample,
	err error) {
	rv, err := ds.list(ds.samples, offset, limit, true)
	return samplesToSamples(rv), err
}

func (ds *Dataset) ListGenesets(offset, limit int) (rv []dbs.Geneset,
	err error) {
	for i := offset; i < offset+limit && i < len(ds.genesets); i++ {
		rv = append(rv, ds.genesets[i])
	}
	return rv, nil
}

func (ds *Dataset) load(h *mmm.Handle, mmm_id mmm.Ident, tags bool) (
	rv *sample, found bool, err error) {
	values, found := h.RowById(mmm_id)
	if !found {
		return nil, false, nil
	}
	var pert_iname, pert_id, pert_type, cell_id, pert_idose, pert_itime,
		is_touchstone string
	if tags {
		err = ds.tx.QueryRow("SELECT sig.pert_iname, sig.pert_id, sig.pert_type, "+
			"sig.cell_id, sig.pert_idose, sig.pert_itime, sig.is_touchstone "+
			"FROM sig sig, signatures s WHERE s.sig_id = sig.sig_id AND s.id = ?",
			mmm_id).Scan(&pert_iname, &pert_id, &pert_type, &cell_id, &pert_idose,
			&pert_itime, &is_touchstone)
	} else {
		err = ds.tx.QueryRow("SELECT sig.pert_iname "+
			"FROM sig sig, signatures s WHERE s.sig_id = sig.sig_id AND s.id = ?",
			mmm_id).Scan(&pert_iname)
	}
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, false, nil
		}
		return nil, false, err
	}
	rv = &sample{
		mmm_id:       mmm_id,
		name:         pert_iname,
		tags:         map[string]string{},
		data:         values,
		dimensionMap: ds.dimensionMap,
	}
	if tags {
		rv.tags["pert_id"] = pert_id
		rv.tags["pert_type"] = pert_type
		rv.tags["cell_id"] = cell_id
		rv.tags["pert_idose"] = pert_idose
		rv.tags["pert_itime"] = pert_itime
		rv.tags["is_touchstone"] = is_touchstone
	}
	return rv, true, nil
}

func (ds *Dataset) byIdx(h *mmm.Handle, idx int, tags bool) (*sample, error) {
	s, found, err := ds.load(h, h.RowIdByIdx(idx), tags)
	return s, notFound(found, err)
}

func (ds *Dataset) GetGeneSig(geneSigId string) (dbs.GeneSig, error) {
	id, err := strconv.ParseUint(geneSigId, 10, 32)
	if err != nil {
		return nil, err
	}
	rv, found, err := ds.load(ds.genesigs, mmm.Ident(id), false)
	return rv, notFound(found, err)
}

func (ds *Dataset) GetSample(sampleId string) (dbs.Sample, error) {
	id, err := strconv.ParseUint(sampleId, 10, 32)
	if err != nil {
		return nil, err
	}
	rv, found, err := ds.load(ds.samples, mmm.Ident(id), true)
	return rv, notFound(found, err)
}
<|endoftext|>


<commit_before>package version

// Version contains the LXD version number
var Version = "2.21"
<commit_msg>Release LXD 3.0.0.beta1<commit_after>package version

// Version contains the LXD version number
var Version = "3.0.0.beta1"
<|endoftext|>


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

/*

	Package flag implements command-line flag parsing.
	Package microdata implements a HTML microdata parser. It depends on the
	golang.org/x/net/html HTML5-compliant parser.

	Usage:

	Pass a reader, baseURL and contentType to the Parse function.
		data, err := microdata.Parse(reader, baseURL, contentType)
		items := data.Items

	Pass an URL to the ParseURL function.
		data, _ := microdata.ParseURL("http://example.com/blogposting")
		items := data.Items
*/

package microdata

import (
	"bytes"
	"io"
	"net/http"
	"net/url"
	"strings"

	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
	"golang.org/x/net/html/charset"
)

type Microdata struct {
	Items []*Item `json:"items"`
}

// addItem adds the item to the items list.
func (m *Microdata) addItem(item *Item) {
	m.Items = append(m.Items, item)
}

type ValueList []interface{}

type PropertyMap map[string]ValueList

type Item struct {
	Types      []string    `json:"type"`
	Properties PropertyMap `json:"properties"`
	Id         string      `json:"id,omitempty"`
}

// addString adds the property, value pair to the properties map. It appends to any
// existing property.
func (i *Item) addString(property, value string) {
	i.Properties[property] = append(i.Properties[property], value)
}

// addItem adds the property, value pair to the properties map. It appends to any
// existing property.
func (i *Item) addItem(property string, value *Item) {
	i.Properties[property] = append(i.Properties[property], value)
}

// addType adds the value to the types list.
func (i *Item) addType(value string) {
	i.Types = append(i.Types, value)
}

// NewItem returns a new Item.
func NewItem() *Item {
	return &Item{
		Types:      make([]string, 0),
		Properties: make(PropertyMap, 0),
	}
}

type parser struct {
	tree            *html.Node
	data            *Microdata
	baseURL         *url.URL
	identifiedNodes map[string]*html.Node
}

// parse returns the microdata from the parser's node tree.
func (p *parser) parse() (*Microdata, error) {

	toplevelNodes := []*html.Node{}

	walkNodes(p.tree, func(n *html.Node) {
		if _, ok := getAttr("itemscope", n); ok {
			if _, ok := getAttr("itemtype", n); ok {
				toplevelNodes = append(toplevelNodes, n)
			}
		}
		if id, ok := getAttr("id", n); ok {
			p.identifiedNodes[id] = n
		}
	})

	for _, node := range toplevelNodes {
		item := NewItem()
		p.data.Items = append(p.data.Items, item)
		if s, ok := getAttr("itemtype", node); ok {
			for _, itemtype := range strings.Split(s, " ") {
				if len(itemtype) > 0 {
					item.addType(itemtype)
				}
			}

			if s, ok := getAttr("itemid", node); ok {
				if u, err := p.baseURL.Parse(s); err == nil {
					item.Id = u.String()
				}
			}
		}

		if s, ok := getAttr("itemref", node); ok {
			for _, itemref := range strings.Split(s, " ") {
				if len(itemref) > 0 {
					if n, ok := p.identifiedNodes[itemref]; ok {
						p.readItem(item, n)
					}
				}
			}
		}

		for c := node.FirstChild; c != nil; c = c.NextSibling {
			p.readItem(item, c)
		}
	}
	return p.data, nil
}

// readItem parses the given node and populates the given item with the result.
func (p *parser) readItem(item *Item, node *html.Node) {
	if itemprops, ok := getAttr("itemprop", node); ok {
		if _, ok := getAttr("itemscope", node); ok {
			subItem := NewItem()

			if itemrefs, ok := getAttr("itemref", node); ok {
				for _, itemref := range strings.Split(itemrefs, " ") {
					if len(itemref) > 0 {
						if n, ok := p.identifiedNodes[itemref]; ok {
							p.readItem(subItem, n)
						}
					}
				}
			}

			for c := node.FirstChild; c != nil; c = c.NextSibling {
				p.readItem(subItem, c)
			}

			for _, propName := range strings.Split(itemprops, " ") {
				if len(propName) > 0 {
					item.addItem(propName, subItem)
				}
			}
			return
		} else {
			var propValue string

			switch node.DataAtom {
			case atom.Meta:
				if value, ok := getAttr("content", node); ok {
					propValue = value
				}
			case atom.Audio, atom.Embed, atom.Iframe, atom.Img, atom.Source, atom.Track, atom.Video:
				if value, ok := getAttr("src", node); ok {
					if u, err := p.baseURL.Parse(value); err == nil {
						propValue = u.String()
					}
				}
			case atom.A, atom.Area, atom.Link:
				if value, ok := getAttr("href", node); ok {
					if u, err := p.baseURL.Parse(value); err == nil {
						propValue = u.String()
					}
				}
			case atom.Data, atom.Meter:
				if value, ok := getAttr("value", node); ok {
					propValue = value
				}
			case atom.Time:
				if value, ok := getAttr("datetime", node); ok {
					propValue = value
				}
			default:
				var buf bytes.Buffer
				walkNodes(node, func(n *html.Node) {
					if n.Type == html.TextNode {
						buf.WriteString(n.Data)
					}
				})
				propValue = buf.String()
			}

			if len(propValue) > 0 {
				for _, propName := range strings.Split(itemprops, " ") {
					if len(propName) > 0 {
						item.addString(propName, propValue)
					}
				}
			}
		}
	}

	for c := node.FirstChild; c != nil; c = c.NextSibling {
		p.readItem(item, c)
	}
}

// newParser returns a parser that converts the content of r to UTF-8 based on the content type of r.
func newParser(r io.Reader, contentType string, baseURL *url.URL) (*parser, error) {
	r, err := charset.NewReader(r, contentType)
	if err != nil {
		return nil, err
	}

	tree, err := html.Parse(r)
	if err != nil {
		return nil, err
	}

	return &parser{
		tree:            tree,
		data:            &Microdata{},
		baseURL:         baseURL,
		identifiedNodes: make(map[string]*html.Node),
	}, nil
}

// getAttr returns the value associated with the given attribute from the given node.
func getAttr(attribute string, node *html.Node) (string, bool) {
	for _, attr := range node.Attr {
		if attribute == attr.Key {
			return attr.Val, true
		}
	}
	return "", false
}

// walkNodes traverses the node tree executing the given functions.
func walkNodes(n *html.Node, f func(*html.Node)) {
	if n != nil {
		f(n)
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			walkNodes(c, f)
		}
	}
}

// ParseHTML parses the HTML document available in the given reader and returns
// the microdata. The given url is used to resolve the URLs in the
// attributes. The given contentType is used convert the content of r to UTF-8.
func ParseHTML(r io.Reader, contentType string, u *url.URL) (*Microdata, error) {
	p, err := newParser(r, contentType, u)
	if err != nil {
		return nil, err
	}
	return p.parse()
}

// ParseURL parses the HTML document available at the given URL and returns the
// microdata.
func ParseURL(urlStr string) (*Microdata, error) {
	var data *Microdata

	u, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	resp, err := http.DefaultClient.Get(urlStr)
	if err != nil {
		return data, err
	}

	contentType := resp.Header.Get("Content-Type")

	p, err := newParser(resp.Body, contentType, u)
	if err != nil {
		return nil, err
	}

	return p.parse()
}
<commit_msg>Fixed issue where top level items where wrongly identified.<commit_after>// Copyright 2015 Lars Wiegman. All rights reserved. Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.

/*

	Package flag implements command-line flag parsing.
	Package microdata implements a HTML microdata parser. It depends on the
	golang.org/x/net/html HTML5-compliant parser.

	Usage:

	Pass a reader, baseURL and contentType to the Parse function.
		data, err := microdata.Parse(reader, baseURL, contentType)
		items := data.Items

	Pass an URL to the ParseURL function.
		data, _ := microdata.ParseURL("http://example.com/blogposting")
		items := data.Items
*/

package microdata

import (
	"bytes"
	"io"
	"net/http"
	"net/url"
	"strings"

	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
	"golang.org/x/net/html/charset"
)

type Microdata struct {
	Items []*Item `json:"items"`
}

// addItem adds the item to the items list.
func (m *Microdata) addItem(item *Item) {
	m.Items = append(m.Items, item)
}

type ValueList []interface{}

type PropertyMap map[string]ValueList

type Item struct {
	Types      []string    `json:"type"`
	Properties PropertyMap `json:"properties"`
	Id         string      `json:"id,omitempty"`
}

// addString adds the property, value pair to the properties map. It appends to any
// existing property.
func (i *Item) addString(property, value string) {
	i.Properties[property] = append(i.Properties[property], value)
}

// addItem adds the property, value pair to the properties map. It appends to any
// existing property.
func (i *Item) addItem(property string, value *Item) {
	i.Properties[property] = append(i.Properties[property], value)
}

// addType adds the value to the types list.
func (i *Item) addType(value string) {
	i.Types = append(i.Types, value)
}

// NewItem returns a new Item.
func NewItem() *Item {
	return &Item{
		Types:      make([]string, 0),
		Properties: make(PropertyMap, 0),
	}
}

type parser struct {
	tree            *html.Node
	data            *Microdata
	baseURL         *url.URL
	identifiedNodes map[string]*html.Node
}

// parse returns the microdata from the parser's node tree.
func (p *parser) parse() (*Microdata, error) {

	toplevelNodes := []*html.Node{}

	walkNodes(p.tree, func(n *html.Node) {
		if _, ok := getAttr("itemscope", n); ok {
			if _, ok := getAttr("itemprop", n); !ok {
				toplevelNodes = append(toplevelNodes, n)
			}
		}
		if id, ok := getAttr("id", n); ok {
			p.identifiedNodes[id] = n
		}
	})

	for _, node := range toplevelNodes {
		item := NewItem()
		p.data.Items = append(p.data.Items, item)
		if s, ok := getAttr("itemtype", node); ok {
			for _, itemtype := range strings.Split(s, " ") {
				if len(itemtype) > 0 {
					item.addType(itemtype)
				}
			}

			if s, ok := getAttr("itemid", node); ok {
				if u, err := p.baseURL.Parse(s); err == nil {
					item.Id = u.String()
				}
			}
		}

		if s, ok := getAttr("itemref", node); ok {
			for _, itemref := range strings.Split(s, " ") {
				if len(itemref) > 0 {
					if n, ok := p.identifiedNodes[itemref]; ok {
						p.readItem(item, n)
					}
				}
			}
		}

		for c := node.FirstChild; c != nil; c = c.NextSibling {
			p.readItem(item, c)
		}
	}
	return p.data, nil
}

// readItem parses the given node and populates the given item with the result.
func (p *parser) readItem(item *Item, node *html.Node) {
	if itemprops, ok := getAttr("itemprop", node); ok {
		if _, ok := getAttr("itemscope", node); ok {
			subItem := NewItem()

			if itemrefs, ok := getAttr("itemref", node); ok {
				for _, itemref := range strings.Split(itemrefs, " ") {
					if len(itemref) > 0 {
						if n, ok := p.identifiedNodes[itemref]; ok {
							p.readItem(subItem, n)
						}
					}
				}
			}

			for c := node.FirstChild; c != nil; c = c.NextSibling {
				p.readItem(subItem, c)
			}

			for _, propName := range strings.Split(itemprops, " ") {
				if len(propName) > 0 {
					item.addItem(propName, subItem)
				}
			}
			return
		} else {
			var propValue string

			switch node.DataAtom {
			case atom.Meta:
				if value, ok := getAttr("content", node); ok {
					propValue = value
				}
			case atom.Audio, atom.Embed, atom.Iframe, atom.Img, atom.Source, atom.Track, atom.Video:
				if value, ok := getAttr("src", node); ok {
					if u, err := p.baseURL.Parse(value); err == nil {
						propValue = u.String()
					}
				}
			case atom.A, atom.Area, atom.Link:
				if value, ok := getAttr("href", node); ok {
					if u, err := p.baseURL.Parse(value); err == nil {
						propValue = u.String()
					}
				}
			case atom.Data, atom.Meter:
				if value, ok := getAttr("value", node); ok {
					propValue = value
				}
			case atom.Time:
				if value, ok := getAttr("datetime", node); ok {
					propValue = value
				}
			default:
				var buf bytes.Buffer
				walkNodes(node, func(n *html.Node) {
					if n.Type == html.TextNode {
						buf.WriteString(n.Data)
					}
				})
				propValue = buf.String()
			}

			if len(propValue) > 0 {
				for _, propName := range strings.Split(itemprops, " ") {
					if len(propName) > 0 {
						item.addString(propName, propValue)
					}
				}
			}
		}
	}

	for c := node.FirstChild; c != nil; c = c.NextSibling {
		p.readItem(item, c)
	}
}

// newParser returns a parser that converts the content of r to UTF-8 based on the content type of r.
func newParser(r io.Reader, contentType string, baseURL *url.URL) (*parser, error) {
	r, err := charset.NewReader(r, contentType)
	if err != nil {
		return nil, err
	}

	tree, err := html.Parse(r)
	if err != nil {
		return nil, err
	}

	return &parser{
		tree:            tree,
		data:            &Microdata{},
		baseURL:         baseURL,
		identifiedNodes: make(map[string]*html.Node),
	}, nil
}

// getAttr returns the value associated with the given attribute from the given node.
func getAttr(attribute string, node *html.Node) (string, bool) {
	for _, attr := range node.Attr {
		if attribute == attr.Key {
			return attr.Val, true
		}
	}
	return "", false
}

// walkNodes traverses the node tree executing the given functions.
func walkNodes(n *html.Node, f func(*html.Node)) {
	if n != nil {
		f(n)
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			walkNodes(c, f)
		}
	}
}

// ParseHTML parses the HTML document available in the given reader and returns
// the microdata. The given url is used to resolve the URLs in the
// attributes. The given contentType is used convert the content of r to UTF-8.
func ParseHTML(r io.Reader, contentType string, u *url.URL) (*Microdata, error) {
	p, err := newParser(r, contentType, u)
	if err != nil {
		return nil, err
	}
	return p.parse()
}

// ParseURL parses the HTML document available at the given URL and returns the
// microdata.
func ParseURL(urlStr string) (*Microdata, error) {
	var data *Microdata

	u, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	resp, err := http.DefaultClient.Get(urlStr)
	if err != nil {
		return data, err
	}

	contentType := resp.Header.Get("Content-Type")

	p, err := newParser(resp.Body, contentType, u)
	if err != nil {
		return nil, err
	}

	return p.parse()
}
<|endoftext|>


<commit_before>/*
 * mktun_openbsd.go
 * OpenBSD-specific source to make the tun(4) device
 * by J. Stuart McMurray
 * created 20150116
 * last modified 20150116
 *
 * Copyright (c) 2014 J. Stuart McMurray <kd5pbo@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

var (
	mac = flag.String("mac", "", "MAC address to use for tun(4) "+
		"device.  If none is specified, none will be set, and "+
		"OpenBSD will assign an address.")
	ip = flag.String("ip", "", "IP Address to use for tun(4) "+
		"device.  Should probably be an IP address on the remote "+
		"subnet.  If not set, no address will be assigned (e.g. for "+
		"DHCP).")
	netmask = flag.String("nm", "", "Netmask.  Will only be set if an "+
		"IP address is specified.")
)

/* Make and open a tun(4) device. */
func make_tun() (*os.File, string, error) {
	var t *os.File = nil /* Tun device */
	var devname = ""     /* Name of tun device */
	/* Validate MAC and IP Addres */
	if "" != *mac && !regexp.MustCompile(`^[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:`+
		`[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:`+
		`[0-9A-Fa-f]{2}$`).MatchString(*mac) {
		return nil, "", fmt.Errorf("invalid MAC address: %v", *mac)
	}
	if "" != *ip && !regexp.MustCompile(`^[0-9A-Fa-f:]{2,39}|`+
		`\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`).MatchString(*ip) {
		return nil, "", fmt.Errorf("Invalid IP address: %v", *ip)
	}
	/* Try successive device numbers until one works */
	for i := 0; i < 256; i++ {
		/* Work out which device to open */
		devname = fmt.Sprintf("tun%v", i)
		/* Try to open a tun device */
		var err error
		if t, err = os.Open(fmt.Sprintf("/dev/%v", devname)); nil !=
			err {
			debug("Unable to open %v: %v", devname, err)
			continue
		}
		debug("Opened /dev/%v", devname)
		break
	}
	/* If we still have no device, give up */
	if nil == t {
		return nil, "", fmt.Errorf("unable to open any tun device")
	}

	/* Set device to layer 2 mode */
	if output, err := exec.Command("/sbin/ifconfig", devname,
		"link0").CombinedOutput(); nil != err {
		return nil, "", fmt.Errorf("setting link0: %v (output: %v)",
			err, strings.TrimSpace(string(output)))
	}
	//o, e := exec.Command("ifconfig").CombinedOutput() /* DEBUG */
	//log.Printf("(%v) %v", e, string(o)) /* DEBUG */
	/* Set mac address if one is given */
	if "" != *mac {
		if output, err := exec.Command("/sbin/ifconfig", devname,
			"lladdr", *mac).CombinedOutput(); nil != err {
			return nil, "", fmt.Errorf("setting mac address (%v): %v "+
				"(output %v)",
				*mac, err, strings.TrimSpace(string(output)))
		}
	}
	/* Set the IP address if one is given */
	if "" != *ip {
		if output, err := exec.Command("/sbin/ifconfig", devname,
			"inet", *ip).CombinedOutput(); nil != err {
			return nil, "", fmt.Errorf("setting IP address: %v "+
				"(output %v)",
				err, strings.TrimSpace(string(output)))
		}
		/* Set the netmask if it's given */
		if "" != *netmask {
			if err := exec.Command("/sbin/ifconfig", devname,
				"netmask", *netmask); nil != err {
				return nil, "", fmt.Errorf("setting netmask: %v")
			}
		}
	}
	return t, devname, nil
}

/* Destroy the tunnel */
func destroy_tun(devname string) {
	if output, err := exec.Command("/sbin/ifconfig", devname,
		"destroy").CombinedOutput(); nil != err {
		debug("Unable to destroy %v: %v (output %v)",
			devname, err, strings.TrimSpace(string(output)))
	}
	debug("Destroyed %v", devname)
}
<commit_msg>Formatting<commit_after>package main

/*
 * mktun_openbsd.go
 * OpenBSD-specific source to make the tun(4) device
 * by J. Stuart McMurray
 * created 20150116
 * last modified 20150116
 *
 * Copyright (c) 2014 J. Stuart McMurray <kd5pbo@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

var (
	mac = flag.String("mac", "", "MAC address to use for tun(4) "+
		"device.  If none is specified, none will be set, and "+
		"OpenBSD will assign an address.")
	ip = flag.String("ip", "", "IP Address to use for tun(4) "+
		"device.  Should probably be an IP address on the remote "+
		"subnet.  If not set, no address will be assigned (e.g. for "+
		"DHCP).")
	netmask = flag.String("nm", "", "Netmask.  Will only be set if an "+
		"IP address is specified.")
)

/* Make and open a tun(4) device. */
func make_tun() (*os.File, string, error) {
	var t *os.File = nil /* Tun device */
	var devname = ""     /* Name of tun device */
	/* Validate MAC and IP Addres */
	if "" != *mac && !regexp.MustCompile(`^[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:`+
		`[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:`+
		`[0-9A-Fa-f]{2}$`).MatchString(*mac) {
		return nil, "", fmt.Errorf("invalid MAC address: %v", *mac)
	}
	if "" != *ip && !regexp.MustCompile(`^[0-9A-Fa-f:]{2,39}|`+
		`\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`).MatchString(*ip) {
		return nil, "", fmt.Errorf("Invalid IP address: %v", *ip)
	}
	/* Try successive device numbers until one works */
	for i := 0; i < 256; i++ {
		/* Work out which device to open */
		devname = fmt.Sprintf("tun%v", i)
		/* Try to open a tun device */
		var err error
		if t, err = os.Open(fmt.Sprintf("/dev/%v", devname)); nil !=
			err {
			debug("Unable to open %v: %v", devname, err)
			continue
		}
		debug("Opened /dev/%v", devname)
		break
	}
	/* If we still have no device, give up */
	if nil == t {
		return nil, "", fmt.Errorf("unable to open any tun device")
	}

	/* Set device to layer 2 mode */
	if output, err := exec.Command("/sbin/ifconfig", devname,
		"link0").CombinedOutput(); nil != err {
		return nil, "", fmt.Errorf("setting link0: %v (output: %v)",
			err, strings.TrimSpace(string(output)))
	}
	//o, e := exec.Command("ifconfig").CombinedOutput() /* DEBUG */
	//log.Printf("(%v) %v", e, string(o)) /* DEBUG */
	/* Set mac address if one is given */
	if "" != *mac {
		if output, err := exec.Command("/sbin/ifconfig", devname,
			"lladdr", *mac).CombinedOutput(); nil != err {
			return nil, "", fmt.Errorf("setting mac address (%v): %v "+
				"(output %v)",
				*mac, err, strings.TrimSpace(string(output)))
		}
	}
	/* Set the IP address if one is given */
	if "" != *ip {
		if output, err := exec.Command("/sbin/ifconfig", devname,
			"inet", *ip).CombinedOutput(); nil != err {
			return nil, "", fmt.Errorf("setting IP address: %v "+
				"(output %v)",
				err, strings.TrimSpace(string(output)))
		}
		/* Set the netmask if it's given */
		if "" != *netmask {
			if err := exec.Command("/sbin/ifconfig", devname,
				"netmask", *netmask); nil != err {
				return nil, "", fmt.Errorf("setting netmask: %v")
			}
		}
	}
	return t, devname, nil
}

/* Destroy the tunnel */
func destroy_tun(devname string) {
	if output, err := exec.Command("/sbin/ifconfig", devname,
		"destroy").CombinedOutput(); nil != err {
		debug("Unable to destroy %v: %v (output %v)",
			devname, err, strings.TrimSpace(string(output)))
	}
	debug("Destroyed %v", devname)
}
<|endoftext|>


<commit_before>// Package millipede provides a framework for creating millipedes.
// millipede is designed to be easy to understand and write, the most simple
// application can be written as follow:
//   func main() {
//     fmt.Println(millipede.New(20))
//   }
package millipede

import (
	"bytes"
	"fmt"
	"math"
	"strings"
	"unicode/utf8"

	"github.com/getmillipede/millipede-go/vendor/github.com/Sirupsen/logrus"
	"github.com/getmillipede/millipede-go/vendor/github.com/kortschak/zalgo"
	"github.com/getmillipede/millipede-go/vendor/github.com/mgutz/ansi"
)

type Millipede struct {
	// Size is the amount of feet pairs
	Size uint64
	// Reverse is the flag that indicates the direction (up/down)
	Reverse bool
	// Skin is the current millipede skin (template)
	Skin string
	// Opposite is the flag that indicates the direction (left/right)
	Opposite bool
	// Width is the width of the millipede (depending on its age and the food it consumes)
	Width uint64
	// Curve is the size of the curve
	Curve uint64
	// Chameleon is the flag that indicates the millipede share its environment color
	Chameleon bool
	// Rainbow is the flag that indicates the millipede live with care bears
	Rainbow bool
	// Zalgo is the flag that invoke the hive-mind representing chaos
	Zalgo bool
	// Step is the amount of steps done by the millipede (useful for animations)
	Steps uint64

	// PadRight is the flag that indicates the need to add spacing on the right of the output
	PadRight bool
}

type Skin struct {
	// Head is used by the millipede to think about its life
	Head string
	// Pede are what make this arthropod so special
	Pede string
	// Reverse is the reverse skin of the millipede
	Reverse *Skin
}

// String returns a string representing a millipede
func (m *Millipede) String() string {
	paddingOffsets := []string{""}
	if m.Curve > 0 {
		for n := uint64(1); n < m.Curve*2; n++ {
			size := int(math.Min(float64(n%(m.Curve*2)), float64(m.Curve*2-n%(m.Curve*2))))
			paddingOffsets = append(paddingOffsets, strings.Repeat(" ", size))
		}
		// --opposite support
		if m.Opposite {
			paddingOffsets = append(paddingOffsets[m.Curve:], paddingOffsets[:m.Curve]...)
		}
	}

	skins := map[string]Skin{
		"default": {
			Head: "  ╚⊙ ⊙╝  ",
			Pede: "╚═(███)═╝",
			Reverse: &Skin{
				Head: "  ╔⊙ ⊙╗  ",
				Pede: "╔═(███)═╗",
			},
		},
		"frozen": {
			Head: "  ╚⊙ ⊙╝  ",
			Pede: "╚═(❄❄❄)═╝",
			Reverse: &Skin{
				Head: "  ╔⊙ ⊙╗  ",
				Pede: "╔═(❄❄❄)═╗",
			},
		},
		"corporate": {
			Head: "  ╚⊙ ⊙╝  ",
			Pede: "╚═(©©©)═╝",
			Reverse: &Skin{
				Head: "  ╔⊙ ⊙╗  ",
				Pede: "╔═(©©©)═╗",
			},
		},
		"love": {
			Head: "  ╚⊙ ⊙╝  ",
			Pede: "╚═(♥♥♥)═╝",
			Reverse: &Skin{
				Head: "  ╔⊙ ⊙╗  ",
				Pede: "╔═(♥♥♥)═╗",
			},
		},
		"musician": {
			Head: "  ╚⊙ ⊙╝  ",
			Pede: "╚═(♫♩♬)═╝",
			Reverse: &Skin{
				Head: "  ╔⊙ ⊙╗  ",
				Pede: "╔═(♫♩♬)═╗",
			},
		},
		"bocal": {
			Head: "  ╚⊙ ⊙╝  ",
			Pede: "╚═(🐟🐟🐟)═╝",
			Reverse: &Skin{
				Head: "  ╔⊙ ⊙╗  ",
				Pede: "╔═(🐟🐟🐟)═╗",
			},
		},
		"ascii": {
			Head: "  \\o o/  ",
			Pede: "|=(###)=|",
			Reverse: &Skin{
				Head: "  /o o\\  ",
				Pede: "|=(###)=|",
			},
		},
		"inception": {
			Head: "    👀    ",
			Pede: "╚═(🐛🐛🐛)═╝",
			Reverse: &Skin{
				Head: "    👀    ",
				Pede: "╔═(🐛🐛🐛)═╗",
			},
		},
		"humancentipede": {
			Head: "    👀    ",
			Pede: "╚═(😷😷😷)═╝",
			Reverse: &Skin{
				Head: "    👀    ",
				Pede: "╔═(😷😷😷)═╗",
			},
		},
		"finger": {
			Head: "    👀    ",
			Pede: "👈~~~  ~~~👉",
			Reverse: &Skin{
				Head: "    👀    ",
				Pede: "👈~~~~~~~~👉",
			},
		},
	}

	// --skin support
	skin := skins[m.Skin]
	if skin.Head == "" {
		logrus.Fatalf("no such skin: '%s'", m.Skin)
	}

	// --reverse support
	if m.Reverse && skin.Reverse != nil && skin.Reverse.Head != "" {
		skin = *skin.Reverse
	}

	// --width support
	if m.Width < 3 {
		logrus.Fatalf("millipede cannot have a witch < 3")
	}
	if m.Width > 3 {
		w := utf8.RuneCountInString(skin.Head)
		head := StringToRuneSlice(skin.Head)
		skin.Head = string(head[:w/2]) + strings.Repeat(string(head[w/2:w/2+1]), int(m.Width-2)) + string(head[w/2+1:])
		pede := StringToRuneSlice(skin.Pede)
		skin.Pede = string(pede[:w/2]) + strings.Repeat(string(pede[w/2:w/2+1]), int(m.Width-2)) + string(pede[w/2+1:])
	}

	// build the millipede body
	var body []string
	if m.Curve > 0 {
		body = []string{paddingOffsets[m.Steps%(m.Curve*2)] + strings.TrimRight(skin.Head, " ")}
	} else {
		body = []string{strings.TrimRight(skin.Head, " ")}
	}
	var x uint64
	for x = 0; x < m.Size; x++ {
		var line string
		if m.Curve > 0 {
			line = paddingOffsets[(m.Steps+x)%(m.Curve*2)] + skin.Pede
		} else {
			line = "" + skin.Pede
		}
		body = append(body, line)
	}

	// --reverse support
	if m.Reverse {
		for i, j := 0, len(body)-1; i < j; i, j = i+1, j-1 {
			body[i], body[j] = body[j], body[i]
		}
	}

	// --chameleon and --rainbow support
	for idx, line := range body {
		colors := []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white"}

		fgColor := ""
		bgColor := ""

		// --chameleon support
		if m.Chameleon {
			fgColor = "black"
		}

		// --rainbow
		if m.Rainbow {
			bgColor = colors[idx%len(colors)]
			if m.Chameleon {
				fgColor = bgColor
				fgColor = "black"
			}
		}

		if fgColor != "" || bgColor != "" {
			paddingSize := len(line) - len(strings.TrimSpace(line))
			line = strings.Repeat(" ", paddingSize) + ansi.Color(line[paddingSize:], fgColor+":"+bgColor)
		}

		if m.PadRight {
			line = line + strings.Repeat(" ", int(m.Curve))
		}

		body[idx] = line
	}

	output := strings.Join(body, "\n")

	// --zalgo support
	if m.Zalgo {
		buf := new(bytes.Buffer)

		z := zalgo.NewCorrupter(buf)
		z.Zalgo = func(n int, r rune, z *zalgo.Corrupter) bool {
			if string(r) == " " || r == 10 {
				z.Up = 0
				z.Middle = 0
				z.Down = 0
			} else {
				if z.Up == 0 {
					z.Up = complex(0, 0.2)
					z.Middle = complex(0, 0.2)
					z.Down = complex(0.001, 0.3)
				}
				z.Up += 0.1
				z.Middle += complex(0.1, 0.2)
				z.Down += 0.1
			}
			return false
		}
		fmt.Fprintln(z, output)
		output = buf.String()
	}

	return output
}

// New returns a millipede
func New(size uint64) *Millipede {
	return &Millipede{
		Size:      size,
		Reverse:   false,
		Skin:      "default",
		Opposite:  false,
		Width:     3,
		Curve:     4,
		Chameleon: false,
		Rainbow:   false,
		Zalgo:     false,
		Steps:     0,
	}
}

// StringToRuneSlice converts a string to a slice of runes
func StringToRuneSlice(input string) []rune {
	output := make([]rune, utf8.RuneCountInString(input))
	n := 0
	for _, r := range input {
		output[n] = r
		n++
	}
	return output
}
<commit_msg>Golint fixes<commit_after>// Package millipede provides a framework for creating millipedes.
// millipede is designed to be easy to understand and write, the most simple
// application can be written as follow:
//   func main() {
//     fmt.Println(millipede.New(20))
//   }
package millipede

import (
	"bytes"
	"fmt"
	"math"
	"strings"
	"unicode/utf8"

	"github.com/getmillipede/millipede-go/vendor/github.com/Sirupsen/logrus"
	"github.com/getmillipede/millipede-go/vendor/github.com/kortschak/zalgo"
	"github.com/getmillipede/millipede-go/vendor/github.com/mgutz/ansi"
)

// Millipede defines a millipede configuration
type Millipede struct {
	// Size is the amount of feet pairs
	Size uint64
	// Reverse is the flag that indicates the direction (up/down)
	Reverse bool
	// Skin is the current millipede skin (template)
	Skin string
	// Opposite is the flag that indicates the direction (left/right)
	Opposite bool
	// Width is the width of the millipede (depending on its age and the food it consumes)
	Width uint64
	// Curve is the size of the curve
	Curve uint64
	// Chameleon is the flag that indicates the millipede share its environment color
	Chameleon bool
	// Rainbow is the flag that indicates the millipede live with care bears
	Rainbow bool
	// Zalgo is the flag that invoke the hive-mind representing chaos
	Zalgo bool
	// Step is the amount of steps done by the millipede (useful for animations)
	Steps uint64

	// PadRight is the flag that indicates the need to add spacing on the right of the output
	PadRight bool
}

// Skin defines the different parts of a millipede body
type Skin struct {
	// Head is used by the millipede to think about its life
	Head string
	// Pede are what make this arthropod so special
	Pede string
	// Reverse is the reverse skin of the millipede
	Reverse *Skin
}

// String returns a string representing a millipede
func (m *Millipede) String() string {
	paddingOffsets := []string{""}
	if m.Curve > 0 {
		for n := uint64(1); n < m.Curve*2; n++ {
			size := int(math.Min(float64(n%(m.Curve*2)), float64(m.Curve*2-n%(m.Curve*2))))
			paddingOffsets = append(paddingOffsets, strings.Repeat(" ", size))
		}
		// --opposite support
		if m.Opposite {
			paddingOffsets = append(paddingOffsets[m.Curve:], paddingOffsets[:m.Curve]...)
		}
	}

	skins := map[string]Skin{
		"default": {
			Head: "  ╚⊙ ⊙╝  ",
			Pede: "╚═(███)═╝",
			Reverse: &Skin{
				Head: "  ╔⊙ ⊙╗  ",
				Pede: "╔═(███)═╗",
			},
		},
		"frozen": {
			Head: "  ╚⊙ ⊙╝  ",
			Pede: "╚═(❄❄❄)═╝",
			Reverse: &Skin{
				Head: "  ╔⊙ ⊙╗  ",
				Pede: "╔═(❄❄❄)═╗",
			},
		},
		"corporate": {
			Head: "  ╚⊙ ⊙╝  ",
			Pede: "╚═(©©©)═╝",
			Reverse: &Skin{
				Head: "  ╔⊙ ⊙╗  ",
				Pede: "╔═(©©©)═╗",
			},
		},
		"love": {
			Head: "  ╚⊙ ⊙╝  ",
			Pede: "╚═(♥♥♥)═╝",
			Reverse: &Skin{
				Head: "  ╔⊙ ⊙╗  ",
				Pede: "╔═(♥♥♥)═╗",
			},
		},
		"musician": {
			Head: "  ╚⊙ ⊙╝  ",
			Pede: "╚═(♫♩♬)═╝",
			Reverse: &Skin{
				Head: "  ╔⊙ ⊙╗  ",
				Pede: "╔═(♫♩♬)═╗",
			},
		},
		"bocal": {
			Head: "  ╚⊙ ⊙╝  ",
			Pede: "╚═(🐟🐟🐟)═╝",
			Reverse: &Skin{
				Head: "  ╔⊙ ⊙╗  ",
				Pede: "╔═(🐟🐟🐟)═╗",
			},
		},
		"ascii": {
			Head: "  \\o o/  ",
			Pede: "|=(###)=|",
			Reverse: &Skin{
				Head: "  /o o\\  ",
				Pede: "|=(###)=|",
			},
		},
		"inception": {
			Head: "    👀    ",
			Pede: "╚═(🐛🐛🐛)═╝",
			Reverse: &Skin{
				Head: "    👀    ",
				Pede: "╔═(🐛🐛🐛)═╗",
			},
		},
		"humancentipede": {
			Head: "    👀    ",
			Pede: "╚═(😷😷😷)═╝",
			Reverse: &Skin{
				Head: "    👀    ",
				Pede: "╔═(😷😷😷)═╗",
			},
		},
		"finger": {
			Head: "    👀    ",
			Pede: "👈~~~  ~~~👉",
			Reverse: &Skin{
				Head: "    👀    ",
				Pede: "👈~~~~~~~~👉",
			},
		},
	}

	// --skin support
	skin := skins[m.Skin]
	if skin.Head == "" {
		logrus.Fatalf("no such skin: '%s'", m.Skin)
	}

	// --reverse support
	if m.Reverse && skin.Reverse != nil && skin.Reverse.Head != "" {
		skin = *skin.Reverse
	}

	// --width support
	if m.Width < 3 {
		logrus.Fatalf("millipede cannot have a witch < 3")
	}
	if m.Width > 3 {
		w := utf8.RuneCountInString(skin.Head)
		head := StringToRuneSlice(skin.Head)
		skin.Head = string(head[:w/2]) + strings.Repeat(string(head[w/2:w/2+1]), int(m.Width-2)) + string(head[w/2+1:])
		pede := StringToRuneSlice(skin.Pede)
		skin.Pede = string(pede[:w/2]) + strings.Repeat(string(pede[w/2:w/2+1]), int(m.Width-2)) + string(pede[w/2+1:])
	}

	// build the millipede body
	var body []string
	if m.Curve > 0 {
		body = []string{paddingOffsets[m.Steps%(m.Curve*2)] + strings.TrimRight(skin.Head, " ")}
	} else {
		body = []string{strings.TrimRight(skin.Head, " ")}
	}
	var x uint64
	for x = 0; x < m.Size; x++ {
		var line string
		if m.Curve > 0 {
			line = paddingOffsets[(m.Steps+x)%(m.Curve*2)] + skin.Pede
		} else {
			line = "" + skin.Pede
		}
		body = append(body, line)
	}

	// --reverse support
	if m.Reverse {
		for i, j := 0, len(body)-1; i < j; i, j = i+1, j-1 {
			body[i], body[j] = body[j], body[i]
		}
	}

	// --chameleon and --rainbow support
	for idx, line := range body {
		colors := []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white"}

		fgColor := ""
		bgColor := ""

		// --chameleon support
		if m.Chameleon {
			fgColor = "black"
		}

		// --rainbow
		if m.Rainbow {
			bgColor = colors[idx%len(colors)]
			if m.Chameleon {
				fgColor = bgColor
				fgColor = "black"
			}
		}

		if fgColor != "" || bgColor != "" {
			paddingSize := len(line) - len(strings.TrimSpace(line))
			line = strings.Repeat(" ", paddingSize) + ansi.Color(line[paddingSize:], fgColor+":"+bgColor)
		}

		if m.PadRight {
			line = line + strings.Repeat(" ", int(m.Curve))
		}

		body[idx] = line
	}

	output := strings.Join(body, "\n")

	// --zalgo support
	if m.Zalgo {
		buf := new(bytes.Buffer)

		z := zalgo.NewCorrupter(buf)
		z.Zalgo = func(n int, r rune, z *zalgo.Corrupter) bool {
			if string(r) == " " || r == 10 {
				z.Up = 0
				z.Middle = 0
				z.Down = 0
			} else {
				if z.Up == 0 {
					z.Up = complex(0, 0.2)
					z.Middle = complex(0, 0.2)
					z.Down = complex(0.001, 0.3)
				}
				z.Up += 0.1
				z.Middle += complex(0.1, 0.2)
				z.Down += 0.1
			}
			return false
		}
		fmt.Fprintln(z, output)
		output = buf.String()
	}

	return output
}

// New returns a millipede
func New(size uint64) *Millipede {
	return &Millipede{
		Size:      size,
		Reverse:   false,
		Skin:      "default",
		Opposite:  false,
		Width:     3,
		Curve:     4,
		Chameleon: false,
		Rainbow:   false,
		Zalgo:     false,
		Steps:     0,
	}
}

// StringToRuneSlice converts a string to a slice of runes
func StringToRuneSlice(input string) []rune {
	output := make([]rune, utf8.RuneCountInString(input))
	n := 0
	for _, r := range input {
		output[n] = r
		n++
	}
	return output
}
<|endoftext|>


<commit_before>// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.

package miner

import (
	"fmt"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/rpc"
)

// PublicMinerAPI provides an API to control the miner.
// It offers only methods that operate on data that pose no security risk when it is publicly accessible.
type PublicMinerAPI struct {
	miner *Miner
	agent *RemoteAgent
}

// NewPublicMinerAPI create a new PublicMinerAPI instance.
func NewPublicMinerAPI(miner *Miner) *PublicMinerAPI {
	return &PublicMinerAPI{miner, NewRemoteAgent()}
}

// Mining returns an indication if this node is currently mining.
func (s *PublicMinerAPI) Mining() bool {
	return s.miner.Mining()
}

// SubmitWork can be used by external miner to submit their POW solution. It returns an indication if the work was
// accepted. Note, this is not an indication if the provided work was valid!
func (s *PublicMinerAPI) SubmitWork(nonce rpc.HexNumber, solution, digest common.Hash) bool {
	return s.agent.SubmitWork(nonce.Uint64(), digest, solution)
}

// GetWork returns a work package for external miner. The work package consists of 3 strings
// result[0], 32 bytes hex encoded current block header pow-hash
// result[1], 32 bytes hex encoded seed hash used for DAG
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
func (s *PublicMinerAPI) GetWork() ([]string, error) {
	if !s.Mining() {
		s.miner.Start(s.miner.coinbase, 0)
	}
	if work, err := s.agent.GetWork(); err == nil {
		return work[:], nil
	} else {
		glog.Infof("%v\n", err)
	}
	return nil, fmt.Errorf("mining not ready")
}

// SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined
// hash rate of all miners which submit work through this node. It accepts the miner hash rate and an identifier which
// must be unique between nodes.
func (s *PublicMinerAPI) SubmitHashrate(hashrate rpc.HexNumber, id common.Hash) bool {
	s.agent.SubmitHashrate(id, hashrate.Uint64())
	return true
}
<commit_msg>miner: register newly created remote agent in the API<commit_after>// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.

package miner

import (
	"fmt"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/rpc"
)

// PublicMinerAPI provides an API to control the miner.
// It offers only methods that operate on data that pose no security risk when it is publicly accessible.
type PublicMinerAPI struct {
	miner *Miner
	agent *RemoteAgent
}

// NewPublicMinerAPI create a new PublicMinerAPI instance.
func NewPublicMinerAPI(miner *Miner) *PublicMinerAPI {
	agent := NewRemoteAgent()
	miner.Register(agent)

	return &PublicMinerAPI{miner, agent}
}

// Mining returns an indication if this node is currently mining.
func (s *PublicMinerAPI) Mining() bool {
	return s.miner.Mining()
}

// SubmitWork can be used by external miner to submit their POW solution. It returns an indication if the work was
// accepted. Note, this is not an indication if the provided work was valid!
func (s *PublicMinerAPI) SubmitWork(nonce rpc.HexNumber, solution, digest common.Hash) bool {
	return s.agent.SubmitWork(nonce.Uint64(), digest, solution)
}

// GetWork returns a work package for external miner. The work package consists of 3 strings
// result[0], 32 bytes hex encoded current block header pow-hash
// result[1], 32 bytes hex encoded seed hash used for DAG
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
func (s *PublicMinerAPI) GetWork() ([]string, error) {
	if !s.Mining() {
		s.miner.Start(s.miner.coinbase, 0)
	}
	if work, err := s.agent.GetWork(); err == nil {
		return work[:], nil
	} else {
		glog.Infof("%v\n", err)
	}
	return nil, fmt.Errorf("mining not ready")
}

// SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined
// hash rate of all miners which submit work through this node. It accepts the miner hash rate and an identifier which
// must be unique between nodes.
func (s *PublicMinerAPI) SubmitHashrate(hashrate rpc.HexNumber, id common.Hash) bool {
	s.agent.SubmitHashrate(id, hashrate.Uint64())
	return true
}
<|endoftext|>