<commit_before>/*
Copyright 2020 Google LLC

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

    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.
*/

// Code generated by injection-gen. DO NOT EDIT.

package brokercell

import (
	"context"
	"time"

	"go.uber.org/zap"

	brokerv1beta1 "github.com/google/knative-gcp/pkg/apis/broker/v1beta1"
	brokerinformer "github.com/google/knative-gcp/pkg/client/injection/informers/broker/v1beta1/broker"
	triggerinformer "github.com/google/knative-gcp/pkg/client/injection/informers/broker/v1beta1/trigger"
	brokercellinformer "github.com/google/knative-gcp/pkg/client/injection/informers/intevents/v1alpha1/brokercell"
	hpainformer "github.com/google/knative-gcp/pkg/client/injection/kube/informers/autoscaling/v2beta2/horizontalpodautoscaler"
	v1alpha1brokercell "github.com/google/knative-gcp/pkg/client/injection/reconciler/intevents/v1alpha1/brokercell"
	"github.com/google/knative-gcp/pkg/metrics"
	"github.com/google/knative-gcp/pkg/reconciler"
	brokerresources "github.com/google/knative-gcp/pkg/reconciler/broker/resources"
	"github.com/google/knative-gcp/pkg/reconciler/brokercell/resources"
	customresourceutil "github.com/google/knative-gcp/pkg/utils/customresource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/tools/cache"
	"knative.dev/eventing/pkg/logging"
	deploymentinformer "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment"
	configmapinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/configmap"
	endpointsinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/endpoints"
	podinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/pod"
	serviceinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/service"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/controller"
)

const (
	// controllerAgentName is the string used by this controller to identify
	// itself when creating events.
	controllerAgentName = "brokercell-controller"
)

// NewController creates a Reconciler for BrokerCell and returns the result of NewImpl.
func NewController(
	ctx context.Context,
	cmw configmap.Watcher,
) *controller.Impl {
	logger := logging.FromContext(ctx)

	ls := listers{
		brokerLister:     brokerinformer.Get(ctx).Lister(),
		hpaLister:        hpainformer.Get(ctx).Lister(),
		triggerLister:    triggerinformer.Get(ctx).Lister(),
		configMapLister:  configmapinformer.Get(ctx).Lister(),
		serviceLister:    serviceinformer.Get(ctx).Lister(),
		endpointsLister:  endpointsinformer.Get(ctx).Lister(),
		deploymentLister: deploymentinformer.Get(ctx).Lister(),
		podLister:        podinformer.Get(ctx).Lister(),
	}

	base := reconciler.NewBase(ctx, controllerAgentName, cmw)
	r, err := NewReconciler(base, ls)
	if err != nil {
		logger.Fatal("Failed to create BrokerCell reconciler", zap.Error(err))
	}
	impl := v1alpha1brokercell.NewImpl(ctx, r)

	var latencyReporter *metrics.BrokerCellLatencyReporter
	if r.env.InternalMetricsEnabled {
		latencyReporter, err = metrics.NewBrokerCellLatencyReporter()
		if err != nil {
			logger.Error("Failed to create latency reporter", zap.Error(err))
		}
	}

	logger.Info("Setting up event handlers.")

	brokercellinformer.Get(ctx).Informer().AddEventHandlerWithResyncPeriod(controller.HandleAll(impl.Enqueue), reconciler.DefaultResyncPeriod)

	// Watch brokers and triggers to invoke configmap update immediately.
	brokerinformer.Get(ctx).Informer().AddEventHandler(controller.HandleAll(
		func(obj interface{}) {
			if b, ok := obj.(*brokerv1beta1.Broker); ok {
				// TODO(#866) Select the brokercell that's associated with the given broker.
				impl.EnqueueKey(types.NamespacedName{Namespace: b.Namespace, Name: brokerresources.DefaultBrokerCellName})
				reportLatency(ctx, b, latencyReporter, "Broker", b.Name, b.Namespace)
			}
		},
	))
	triggerinformer.Get(ctx).Informer().AddEventHandler(controller.HandleAll(
		func(obj interface{}) {
			if t, ok := obj.(*brokerv1beta1.Trigger); ok {
				b, err := brokerinformer.Get(ctx).Lister().Brokers(t.Namespace).Get(t.Spec.Broker)
				if err != nil {
					logging.FromContext(ctx).Error("Failed to get broker", zap.Error(err))
					return
				}
				// TODO(#866) Select the brokercell that's associated with the given broker.
				impl.EnqueueKey(types.NamespacedName{Namespace: b.Namespace, Name: brokerresources.DefaultBrokerCellName})
				reportLatency(ctx, t, latencyReporter, "Trigger", t.Name, t.Namespace)
			}
		},
	))

	// Watch data plane components created by brokercell so we can update brokercell status immediately.
	// 1. Watch deployments for ingress, fanout and retry
	deploymentinformer.Get(ctx).Informer().AddEventHandler(handleResourceUpdate(impl))
	// 2. Watch ingress endpoints
	endpointsinformer.Get(ctx).Informer().AddEventHandler(handleResourceUpdate(impl))
	// 3. Watch hpa for ingress, fanout and retry deployments
	hpainformer.Get(ctx).Informer().AddEventHandler(handleResourceUpdate(impl))
	// 4. Watch the broker targets configmap.
	configmapinformer.Get(ctx).Informer().AddEventHandler(handleResourceUpdate(impl))

	return impl
}

// handleResourceUpdate returns an event handler for resources created by brokercell such as the ingress deployment.
func handleResourceUpdate(impl *controller.Impl) cache.ResourceEventHandler {
	// Since resources created by brokercell live in the same namespace as the brokercell, we use an
	// empty namespaceLabel so that the same namespace of the given object is used to enqueue.
	namespaceLabel := ""
	// Resources created by the brokercell, including the indirectly created ingress service endpoints,
	// have such a label resources.BrokerCellLabelKey=<brokercellName>. Resources without this label
	// will be skipped by the function.
	return controller.HandleAll(impl.EnqueueLabelOfNamespaceScopedResource(namespaceLabel, resources.BrokerCellLabelKey))
}

// reportLatency estimates the time spent since the last update of the resource object and records it to the latency metric
func reportLatency(ctx context.Context, resourceObj metav1.ObjectMetaAccessor, latencyReporter *metrics.BrokerCellLatencyReporter, resourceKind, resourceName, namespace string) {
	if latencyReporter == nil {
		return
	}
	if latestUpdateTime, err := customresourceutil.RetrieveLatestUpdateTime(resourceObj); err == nil {
		if err := latencyReporter.ReportLatency(ctx, time.Now().Sub(latestUpdateTime), resourceKind, resourceName, namespace); err != nil {
			logging.FromContext(ctx).Error("Failed to report latency", zap.Error(err))
		}
	} else {
		logging.FromContext(ctx).Error("Failed to retrieve the resource update time", zap.Error(err))
	}
}
<commit_msg>Properly set system namespace when reconciling default brokercell (#1705)<commit_after>/*
Copyright 2020 Google LLC

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

    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.
*/

// Code generated by injection-gen. DO NOT EDIT.

package brokercell

import (
	"context"
	"time"

	"go.uber.org/zap"

	brokerv1beta1 "github.com/google/knative-gcp/pkg/apis/broker/v1beta1"
	brokerinformer "github.com/google/knative-gcp/pkg/client/injection/informers/broker/v1beta1/broker"
	triggerinformer "github.com/google/knative-gcp/pkg/client/injection/informers/broker/v1beta1/trigger"
	brokercellinformer "github.com/google/knative-gcp/pkg/client/injection/informers/intevents/v1alpha1/brokercell"
	hpainformer "github.com/google/knative-gcp/pkg/client/injection/kube/informers/autoscaling/v2beta2/horizontalpodautoscaler"
	v1alpha1brokercell "github.com/google/knative-gcp/pkg/client/injection/reconciler/intevents/v1alpha1/brokercell"
	"github.com/google/knative-gcp/pkg/metrics"
	"github.com/google/knative-gcp/pkg/reconciler"
	brokerresources "github.com/google/knative-gcp/pkg/reconciler/broker/resources"
	"github.com/google/knative-gcp/pkg/reconciler/brokercell/resources"
	customresourceutil "github.com/google/knative-gcp/pkg/utils/customresource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/tools/cache"
	"knative.dev/eventing/pkg/logging"
	deploymentinformer "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment"
	configmapinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/configmap"
	endpointsinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/endpoints"
	podinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/pod"
	serviceinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/service"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/system"
)

const (
	// controllerAgentName is the string used by this controller to identify
	// itself when creating events.
	controllerAgentName = "brokercell-controller"
)

// NewController creates a Reconciler for BrokerCell and returns the result of NewImpl.
func NewController(
	ctx context.Context,
	cmw configmap.Watcher,
) *controller.Impl {
	logger := logging.FromContext(ctx)

	ls := listers{
		brokerLister:     brokerinformer.Get(ctx).Lister(),
		hpaLister:        hpainformer.Get(ctx).Lister(),
		triggerLister:    triggerinformer.Get(ctx).Lister(),
		configMapLister:  configmapinformer.Get(ctx).Lister(),
		serviceLister:    serviceinformer.Get(ctx).Lister(),
		endpointsLister:  endpointsinformer.Get(ctx).Lister(),
		deploymentLister: deploymentinformer.Get(ctx).Lister(),
		podLister:        podinformer.Get(ctx).Lister(),
	}

	base := reconciler.NewBase(ctx, controllerAgentName, cmw)
	r, err := NewReconciler(base, ls)
	if err != nil {
		logger.Fatal("Failed to create BrokerCell reconciler", zap.Error(err))
	}
	impl := v1alpha1brokercell.NewImpl(ctx, r)

	var latencyReporter *metrics.BrokerCellLatencyReporter
	if r.env.InternalMetricsEnabled {
		latencyReporter, err = metrics.NewBrokerCellLatencyReporter()
		if err != nil {
			logger.Error("Failed to create latency reporter", zap.Error(err))
		}
	}

	logger.Info("Setting up event handlers.")

	brokercellinformer.Get(ctx).Informer().AddEventHandlerWithResyncPeriod(controller.HandleAll(impl.Enqueue), reconciler.DefaultResyncPeriod)

	// Watch brokers and triggers to invoke configmap update immediately.
	brokerinformer.Get(ctx).Informer().AddEventHandler(controller.HandleAll(
		func(obj interface{}) {
			if b, ok := obj.(*brokerv1beta1.Broker); ok {
				// TODO(#866) Select the brokercell that's associated with the given broker.
				impl.EnqueueKey(types.NamespacedName{Namespace: system.Namespace(), Name: brokerresources.DefaultBrokerCellName})
				reportLatency(ctx, b, latencyReporter, "Broker", b.Name, b.Namespace)
			}
		},
	))
	triggerinformer.Get(ctx).Informer().AddEventHandler(controller.HandleAll(
		func(obj interface{}) {
			if t, ok := obj.(*brokerv1beta1.Trigger); ok {
				// TODO(#866) Select the brokercell that's associated with the given broker.
				impl.EnqueueKey(types.NamespacedName{Namespace: system.Namespace(), Name: brokerresources.DefaultBrokerCellName})
				reportLatency(ctx, t, latencyReporter, "Trigger", t.Name, t.Namespace)
			}
		},
	))

	// Watch data plane components created by brokercell so we can update brokercell status immediately.
	// 1. Watch deployments for ingress, fanout and retry
	deploymentinformer.Get(ctx).Informer().AddEventHandler(handleResourceUpdate(impl))
	// 2. Watch ingress endpoints
	endpointsinformer.Get(ctx).Informer().AddEventHandler(handleResourceUpdate(impl))
	// 3. Watch hpa for ingress, fanout and retry deployments
	hpainformer.Get(ctx).Informer().AddEventHandler(handleResourceUpdate(impl))
	// 4. Watch the broker targets configmap.
	configmapinformer.Get(ctx).Informer().AddEventHandler(handleResourceUpdate(impl))

	return impl
}

// handleResourceUpdate returns an event handler for resources created by brokercell such as the ingress deployment.
func handleResourceUpdate(impl *controller.Impl) cache.ResourceEventHandler {
	// Since resources created by brokercell live in the same namespace as the brokercell, we use an
	// empty namespaceLabel so that the same namespace of the given object is used to enqueue.
	namespaceLabel := ""
	// Resources created by the brokercell, including the indirectly created ingress service endpoints,
	// have such a label resources.BrokerCellLabelKey=<brokercellName>. Resources without this label
	// will be skipped by the function.
	return controller.HandleAll(impl.EnqueueLabelOfNamespaceScopedResource(namespaceLabel, resources.BrokerCellLabelKey))
}

// reportLatency estimates the time spent since the last update of the resource object and records it to the latency metric
func reportLatency(ctx context.Context, resourceObj metav1.ObjectMetaAccessor, latencyReporter *metrics.BrokerCellLatencyReporter, resourceKind, resourceName, namespace string) {
	if latencyReporter == nil {
		return
	}
	if latestUpdateTime, err := customresourceutil.RetrieveLatestUpdateTime(resourceObj); err == nil {
		if err := latencyReporter.ReportLatency(ctx, time.Now().Sub(latestUpdateTime), resourceKind, resourceName, namespace); err != nil {
			logging.FromContext(ctx).Error("Failed to report latency", zap.Error(err))
		}
	} else {
		logging.FromContext(ctx).Error("Failed to retrieve the resource update time", zap.Error(err))
	}
}
<|endoftext|>


<commit_before>// Copyright 2015-present Oursky Ltd.
//
// 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 preprocessor

import (
	"context"
	"net/http"
	"strings"
	"time"

	"github.com/skygeario/skygear-server/pkg/server/asset"
	"github.com/skygeario/skygear-server/pkg/server/logging"
	"github.com/skygeario/skygear-server/pkg/server/plugin/hook"
	"github.com/skygeario/skygear-server/pkg/server/recordutil"
	"github.com/skygeario/skygear-server/pkg/server/router"
	"github.com/skygeario/skygear-server/pkg/server/skydb"
	"github.com/skygeario/skygear-server/pkg/server/skyerr"
)

var timeNow = time.Now().UTC

var log = logging.LoggerEntry("preprocessor")

type InjectAuthIfPresent struct {
}

func isTokenStillValid(token router.AccessToken, authInfo skydb.AuthInfo) bool {
	if authInfo.TokenValidSince == nil {
		return true
	}
	tokenValidSince := *authInfo.TokenValidSince

	// Not all types of access token support this field. The token is
	// still considered if it does not have an issue time.
	if token.IssuedAt().IsZero() {
		return true
	}

	// Due to precision, the issue time of the token can be before
	// AuthInfo.TokenValidSince. We consider the token still valid
	// if the token is issued within 1 second before tokenValidSince.
	return token.IssuedAt().After(tokenValidSince.Add(-1 * time.Second))
}

func (p InjectAuthIfPresent) Preprocess(payload *router.Payload, response *router.Response) int {
	if payload.AuthInfoID == "" {
		if !payload.HasMasterKey() {
			log.Debugln("injectUser: empty AuthInfoID, skipping")
			return http.StatusOK
		}
		payload.AuthInfoID = "_god"
		payload.Context = context.WithValue(payload.Context, router.UserIDContextKey, "_god")
	}

	conn := payload.DBConn
	authinfo := skydb.AuthInfo{}

	if err := conn.GetAuth(payload.AuthInfoID, &authinfo); err != nil {
		if err == skydb.ErrUserNotFound && payload.HasMasterKey() {
			authinfo = skydb.AuthInfo{
				ID: payload.AuthInfoID,
			}
			if err := payload.DBConn.CreateAuth(&authinfo); err != nil && err != skydb.ErrUserDuplicated {
				return http.StatusInternalServerError
			}
		} else {
			log.Errorf("Cannot find AuthInfo.ID = %#v\n", payload.AuthInfoID)
			response.Err = skyerr.NewError(skyerr.UnexpectedAuthInfoNotFound, err.Error())
			return http.StatusInternalServerError
		}
	}

	// If an access token exists checks if the access token has an IssuedAt
	// time that is later than the user's TokenValidSince time. This
	// allows user to invalidate previously issued access token.
	if payload.AccessToken != nil && !isTokenStillValid(payload.AccessToken, authinfo) {
		response.Err = skyerr.NewError(skyerr.AccessTokenNotAccepted, "token does not exist or it has expired")
		return http.StatusUnauthorized
	}

	payload.AuthInfo = &authinfo

	return http.StatusOK
}

// InjectUserIfPresent injects a user record to the payload
//
// An AuthInfo must be injected before this, if it is not found, the preprocessor
// would just skip the injection
//
// If AuthInfo is injected but a user record is not found, the preprocessor would
// create a new user record and inject it to the payload
type InjectUserIfPresent struct {
	HookRegistry *hook.Registry `inject:"HookRegistry"`
	AssetStore   asset.Store    `inject:"AssetStore"`
}

func (p InjectUserIfPresent) Preprocess(payload *router.Payload, response *router.Response) int {
	authInfo := payload.AuthInfo
	db := payload.DBConn.PublicDB()

	if authInfo == nil {
		log.Debugln("injectUser: empty AuthInfo, skipping")
		return http.StatusOK
	}

	user := skydb.Record{}
	err := db.Get(skydb.NewRecordID("user", authInfo.ID), &user)

	if err == skydb.ErrRecordNotFound {
		user, err = p.createUser(payload)
	}

	if err != nil {
		log.Error("injectUser: unable to find or create user record", err)
		response.Err = skyerr.NewError(skyerr.UnexpectedUserNotFound, err.Error())
		return http.StatusInternalServerError
	}

	payload.User = &user

	return http.StatusOK
}

func (p InjectUserIfPresent) createUser(payload *router.Payload) (skydb.Record, error) {
	authInfo := payload.AuthInfo
	db := payload.DBConn.PublicDB()
	txDB, ok := db.(skydb.Transactional)
	if !ok {
		return skydb.Record{}, skyerr.NewError(skyerr.NotSupported, "database impl does not support transaction")
	}

	var user *skydb.Record
	txErr := skydb.WithTransaction(txDB, func() error {
		userRecord := skydb.Record{
			ID: skydb.NewRecordID(db.UserRecordType(), authInfo.ID),
		}

		recordReq := recordutil.RecordModifyRequest{
			Db:           db,
			Conn:         payload.DBConn,
			AssetStore:   p.AssetStore,
			HookRegistry: p.HookRegistry,
			Atomic:       true,
			Context:      payload.Context,
			AuthInfo:     authInfo,
			ModifyAt:     timeNow(),
			RecordsToSave: []*skydb.Record{
				&userRecord,
			},
		}

		recordResp := recordutil.RecordModifyResponse{
			ErrMap: map[skydb.RecordID]skyerr.Error{},
		}

		err := recordutil.RecordSaveHandler(&recordReq, &recordResp)
		if err != nil {
			return err
		}

		user = recordResp.SavedRecords[0]
		return nil
	})

	if txErr != nil {
		return skydb.Record{}, txErr
	}

	return *user, nil
}

type InjectDatabase struct {
}

func (p InjectDatabase) Preprocess(payload *router.Payload, response *router.Response) int {
	conn := payload.DBConn

	databaseID, ok := payload.Data["database_id"].(string)
	if !ok || databaseID == "" {
		databaseID = "_public"
	}

	switch databaseID {
	case "_private":
		if payload.AuthInfo != nil {
			payload.Database = conn.PrivateDB(payload.AuthInfo.ID)
		} else {
			response.Err = skyerr.NewError(skyerr.NotAuthenticated, "Authentication is needed for private DB access")
			return http.StatusUnauthorized
		}
	case "_public":
		payload.Database = conn.PublicDB()
	case "_union":
		if !payload.HasMasterKey() {
			response.Err = skyerr.NewError(skyerr.NotAuthenticated, "Master key is needed for union DB access")
			return http.StatusUnauthorized
		}
		payload.Database = conn.UnionDB()
	default:
		if strings.HasPrefix(databaseID, "_") {
			response.Err = skyerr.NewInvalidArgument("invalid database ID", []string{"database_id"})
			return http.StatusBadRequest
		} else if payload.HasMasterKey() {
			payload.Database = conn.PrivateDB(databaseID)
		} else if payload.AuthInfo != nil && databaseID == payload.AuthInfo.ID {
			payload.Database = conn.PrivateDB(databaseID)
		} else {
			response.Err = skyerr.NewError(skyerr.PermissionDenied, "The selected DB cannot be accessed because permission is denied")
			return http.StatusForbidden
		}
	}

	return http.StatusOK
}

type InjectPublicDatabase struct {
}

func (p InjectPublicDatabase) Preprocess(payload *router.Payload, response *router.Response) int {
	conn := payload.DBConn
	payload.Database = conn.PublicDB()
	return http.StatusOK
}

type RequireAuth struct {
}

func (p RequireAuth) Preprocess(payload *router.Payload, response *router.Response) int {
	if payload.AuthInfo == nil {
		response.Err = skyerr.NewError(skyerr.NotAuthenticated, "Authentication is required for this action, please login.")
		return http.StatusUnauthorized
	}

	return http.StatusOK
}

type RequireAdminOrMasterKey struct {
}

func (p RequireAdminOrMasterKey) Preprocess(payload *router.Payload, response *router.Response) int {
	if payload.HasMasterKey() {
		return http.StatusOK
	}

	if payload.AuthInfo == nil {
		response.Err = skyerr.NewError(
			skyerr.NotAuthenticated,
			"User is required for this action, please login.",
		)
		return http.StatusUnauthorized
	}

	adminRoles, err := payload.DBConn.GetAdminRoles()
	if err != nil {
		response.Err = skyerr.MakeError(err)
		return http.StatusInternalServerError
	}

	if payload.AuthInfo.HasAnyRoles(adminRoles) {
		return http.StatusOK
	}

	response.Err = skyerr.NewError(
		skyerr.PermissionDenied,
		"no permission to perform this action",
	)
	return http.StatusUnauthorized
}

type RequireMasterKey struct {
}

func (p RequireMasterKey) Preprocess(payload *router.Payload, response *router.Response) int {
	if payload.HasMasterKey() == false {
		response.Err = skyerr.NewError(skyerr.PermissionDenied, "no permission to this action")
		return http.StatusUnauthorized
	}

	return http.StatusOK
}
<commit_msg>Fix timeNow does not return the current time<commit_after>// Copyright 2015-present Oursky Ltd.
//
// 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 preprocessor

import (
	"context"
	"net/http"
	"strings"
	"time"

	"github.com/skygeario/skygear-server/pkg/server/asset"
	"github.com/skygeario/skygear-server/pkg/server/logging"
	"github.com/skygeario/skygear-server/pkg/server/plugin/hook"
	"github.com/skygeario/skygear-server/pkg/server/recordutil"
	"github.com/skygeario/skygear-server/pkg/server/router"
	"github.com/skygeario/skygear-server/pkg/server/skydb"
	"github.com/skygeario/skygear-server/pkg/server/skyerr"
)

var timeNow = func() time.Time { return time.Now().UTC() }

var log = logging.LoggerEntry("preprocessor")

type InjectAuthIfPresent struct {
}

func isTokenStillValid(token router.AccessToken, authInfo skydb.AuthInfo) bool {
	if authInfo.TokenValidSince == nil {
		return true
	}
	tokenValidSince := *authInfo.TokenValidSince

	// Not all types of access token support this field. The token is
	// still considered if it does not have an issue time.
	if token.IssuedAt().IsZero() {
		return true
	}

	// Due to precision, the issue time of the token can be before
	// AuthInfo.TokenValidSince. We consider the token still valid
	// if the token is issued within 1 second before tokenValidSince.
	return token.IssuedAt().After(tokenValidSince.Add(-1 * time.Second))
}

func (p InjectAuthIfPresent) Preprocess(payload *router.Payload, response *router.Response) int {
	if payload.AuthInfoID == "" {
		if !payload.HasMasterKey() {
			log.Debugln("injectUser: empty AuthInfoID, skipping")
			return http.StatusOK
		}
		payload.AuthInfoID = "_god"
		payload.Context = context.WithValue(payload.Context, router.UserIDContextKey, "_god")
	}

	conn := payload.DBConn
	authinfo := skydb.AuthInfo{}

	if err := conn.GetAuth(payload.AuthInfoID, &authinfo); err != nil {
		if err == skydb.ErrUserNotFound && payload.HasMasterKey() {
			authinfo = skydb.AuthInfo{
				ID: payload.AuthInfoID,
			}
			if err := payload.DBConn.CreateAuth(&authinfo); err != nil && err != skydb.ErrUserDuplicated {
				return http.StatusInternalServerError
			}
		} else {
			log.Errorf("Cannot find AuthInfo.ID = %#v\n", payload.AuthInfoID)
			response.Err = skyerr.NewError(skyerr.UnexpectedAuthInfoNotFound, err.Error())
			return http.StatusInternalServerError
		}
	}

	// If an access token exists checks if the access token has an IssuedAt
	// time that is later than the user's TokenValidSince time. This
	// allows user to invalidate previously issued access token.
	if payload.AccessToken != nil && !isTokenStillValid(payload.AccessToken, authinfo) {
		response.Err = skyerr.NewError(skyerr.AccessTokenNotAccepted, "token does not exist or it has expired")
		return http.StatusUnauthorized
	}

	payload.AuthInfo = &authinfo

	return http.StatusOK
}

// InjectUserIfPresent injects a user record to the payload
//
// An AuthInfo must be injected before this, if it is not found, the preprocessor
// would just skip the injection
//
// If AuthInfo is injected but a user record is not found, the preprocessor would
// create a new user record and inject it to the payload
type InjectUserIfPresent struct {
	HookRegistry *hook.Registry `inject:"HookRegistry"`
	AssetStore   asset.Store    `inject:"AssetStore"`
}

func (p InjectUserIfPresent) Preprocess(payload *router.Payload, response *router.Response) int {
	authInfo := payload.AuthInfo
	db := payload.DBConn.PublicDB()

	if authInfo == nil {
		log.Debugln("injectUser: empty AuthInfo, skipping")
		return http.StatusOK
	}

	user := skydb.Record{}
	err := db.Get(skydb.NewRecordID("user", authInfo.ID), &user)

	if err == skydb.ErrRecordNotFound {
		user, err = p.createUser(payload)
	}

	if err != nil {
		log.Error("injectUser: unable to find or create user record", err)
		response.Err = skyerr.NewError(skyerr.UnexpectedUserNotFound, err.Error())
		return http.StatusInternalServerError
	}

	payload.User = &user

	return http.StatusOK
}

func (p InjectUserIfPresent) createUser(payload *router.Payload) (skydb.Record, error) {
	authInfo := payload.AuthInfo
	db := payload.DBConn.PublicDB()
	txDB, ok := db.(skydb.Transactional)
	if !ok {
		return skydb.Record{}, skyerr.NewError(skyerr.NotSupported, "database impl does not support transaction")
	}

	var user *skydb.Record
	txErr := skydb.WithTransaction(txDB, func() error {
		userRecord := skydb.Record{
			ID: skydb.NewRecordID(db.UserRecordType(), authInfo.ID),
		}

		recordReq := recordutil.RecordModifyRequest{
			Db:           db,
			Conn:         payload.DBConn,
			AssetStore:   p.AssetStore,
			HookRegistry: p.HookRegistry,
			Atomic:       true,
			Context:      payload.Context,
			AuthInfo:     authInfo,
			ModifyAt:     timeNow(),
			RecordsToSave: []*skydb.Record{
				&userRecord,
			},
		}

		recordResp := recordutil.RecordModifyResponse{
			ErrMap: map[skydb.RecordID]skyerr.Error{},
		}

		err := recordutil.RecordSaveHandler(&recordReq, &recordResp)
		if err != nil {
			return err
		}

		user = recordResp.SavedRecords[0]
		return nil
	})

	if txErr != nil {
		return skydb.Record{}, txErr
	}

	return *user, nil
}

type InjectDatabase struct {
}

func (p InjectDatabase) Preprocess(payload *router.Payload, response *router.Response) int {
	conn := payload.DBConn

	databaseID, ok := payload.Data["database_id"].(string)
	if !ok || databaseID == "" {
		databaseID = "_public"
	}

	switch databaseID {
	case "_private":
		if payload.AuthInfo != nil {
			payload.Database = conn.PrivateDB(payload.AuthInfo.ID)
		} else {
			response.Err = skyerr.NewError(skyerr.NotAuthenticated, "Authentication is needed for private DB access")
			return http.StatusUnauthorized
		}
	case "_public":
		payload.Database = conn.PublicDB()
	case "_union":
		if !payload.HasMasterKey() {
			response.Err = skyerr.NewError(skyerr.NotAuthenticated, "Master key is needed for union DB access")
			return http.StatusUnauthorized
		}
		payload.Database = conn.UnionDB()
	default:
		if strings.HasPrefix(databaseID, "_") {
			response.Err = skyerr.NewInvalidArgument("invalid database ID", []string{"database_id"})
			return http.StatusBadRequest
		} else if payload.HasMasterKey() {
			payload.Database = conn.PrivateDB(databaseID)
		} else if payload.AuthInfo != nil && databaseID == payload.AuthInfo.ID {
			payload.Database = conn.PrivateDB(databaseID)
		} else {
			response.Err = skyerr.NewError(skyerr.PermissionDenied, "The selected DB cannot be accessed because permission is denied")
			return http.StatusForbidden
		}
	}

	return http.StatusOK
}

type InjectPublicDatabase struct {
}

func (p InjectPublicDatabase) Preprocess(payload *router.Payload, response *router.Response) int {
	conn := payload.DBConn
	payload.Database = conn.PublicDB()
	return http.StatusOK
}

type RequireAuth struct {
}

func (p RequireAuth) Preprocess(payload *router.Payload, response *router.Response) int {
	if payload.AuthInfo == nil {
		response.Err = skyerr.NewError(skyerr.NotAuthenticated, "Authentication is required for this action, please login.")
		return http.StatusUnauthorized
	}

	return http.StatusOK
}

type RequireAdminOrMasterKey struct {
}

func (p RequireAdminOrMasterKey) Preprocess(payload *router.Payload, response *router.Response) int {
	if payload.HasMasterKey() {
		return http.StatusOK
	}

	if payload.AuthInfo == nil {
		response.Err = skyerr.NewError(
			skyerr.NotAuthenticated,
			"User is required for this action, please login.",
		)
		return http.StatusUnauthorized
	}

	adminRoles, err := payload.DBConn.GetAdminRoles()
	if err != nil {
		response.Err = skyerr.MakeError(err)
		return http.StatusInternalServerError
	}

	if payload.AuthInfo.HasAnyRoles(adminRoles) {
		return http.StatusOK
	}

	response.Err = skyerr.NewError(
		skyerr.PermissionDenied,
		"no permission to perform this action",
	)
	return http.StatusUnauthorized
}

type RequireMasterKey struct {
}

func (p RequireMasterKey) Preprocess(payload *router.Payload, response *router.Response) int {
	if payload.HasMasterKey() == false {
		response.Err = skyerr.NewError(skyerr.PermissionDenied, "no permission to this action")
		return http.StatusUnauthorized
	}

	return http.StatusOK
}
<|endoftext|>


<commit_before>package dojoBuilder

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"syscall"
	"text/template"
)

const profileTemplate = `var profile = {{.}};`

type BuildConfig struct {
	RemoveUncompressed    bool `json:"removeUncompressed,omitempty"` // Remove uncompressed js files after build
	RemoveConsoleStripped bool `json:"removeConsoleStripped,omitempty"`

	BasePath    string           `json:"basePath"`
	ReleaseDir  string           `json:"releaseDir"`
	ReleaseName string           `json:"releaseName,omitempty"`
	Action      string           `json:"action"`
	Packages    []Package        `json:"packages"`
	Layers      map[string]Layer `json:"layers"`

	LayerOptimize     string             `json:"layerOptimize,omitempty"`
	Optimize          string             `json:"optimize,omitempty"`
	CssOptimize       string             `json:"cssOptimize,omitempty"`
	Mini              bool               `json:"mini,omitempty"`
	StripConsole      string             `json:"stripConsole,omitempty"`
	SelectorEngine    string             `json:"selectorEngine,omitempty"`
	StaticHasFeatures map[string]Feature `json:"staticHasFeatures,omitempty"`
	UseSourceMaps     bool               `json:"useSourceMaps"` // Build generate source maps
}

type Package struct {
	Name     string `json:"name"`
	Location string `json:"location"`
}

type Layer struct {
	Boot       bool     `json:"boot"`
	CustomBase bool     `json:"customBase"`
	Include    []string `json:"include,omitempty"`
	Exclude    []string `json:"exclude,omitempty"`
}

type Feature bool

func (f Feature) MarshalJSON() ([]byte, error) {
	var v uint8 = 0
	if bool(f) {
		v = 1
	}
	return json.Marshal(v)
}

func (c *Config) generateBuildProfile(name string) (profileFullPath string, err error) {
	installDir := c.DestDir + "/tmp"
	bc, ok := c.BuildConfigs[name]
	if !ok {
		return "", errors.New("No build config found with name '" + name + "'")
	}

	if bc.Action == "" {
		bc.Action = "release"
	}

	profilePath := installDir + "/profiles/"
	os.MkdirAll(profilePath, 0754)

	profileFullPath = profilePath + name + ".profile.js"

	bc.BasePath = ".."

	bc.ReleaseDir = c.DestDir
	// if bc.ReleaseDir, err = filepath.Rel(c.SrcDir+`/`+bc.BasePath+"__fakeFile__", c.DestDir); err != nil {
	// 	return "", err
	// }

	j, err := json.Marshal(bc)
	if err != nil {
		return "", err
	}

	f, err := os.OpenFile(profileFullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664)
	if err != nil {
		return "", err
	}

	t := template.Must(template.New("profileTemplate").Parse(profileTemplate))
	err = t.Execute(f, string(j))

	return profileFullPath, err
}

func build(c *Config, names []string) (err error) {
	var profilePath string

	if len(names) == 0 {
		for n, _ := range c.BuildConfigs {
			names = append(names, n)
		}
	}

	for _, n := range names {
		fmt.Printf("Generating %s build\n", n)

		profilePath, err = c.generateBuildProfile(n)
		if err != nil {
			return
		}

		if err = executeBuildProfile(c, profilePath); err != nil {
			return
		}

		bc, _ := c.BuildConfigs[n]

		var removePattern, sep string

		if bc.RemoveUncompressed {
			removePattern += sep + `uncompressed`
			sep = `|`
		}

		if bc.RemoveConsoleStripped {
			removePattern += sep + `consoleStripped`
			sep = `|`
		}

		filepath.Walk(c.DestDir, func(path string, f os.FileInfo, err error) (_err error) {
			originPath := c.SrcDir + path[len(c.DestDir):]

			if fi, err := os.Stat(originPath); err == nil {
				st := fi.Sys().(*syscall.Stat_t)
				os.Chown(path, int(st.Uid), int(st.Gid))
			}

			if removePattern != `` {
				if match, _err := regexp.MatchString(`.*\.js\.(`+removePattern+`)\.js`, f.Name()); _err != nil {
					return _err
				} else if match {
					fmt.Println("Removing " + path)
					_err = os.Remove(path)
				}
			}

			return
		})
		if c.installDir != "" {
			os.Remove(c.installDir)
		}
	}

	return
}

func executeBuildProfile(c *Config, profilePath string) (err error) {
	buildScriptPath := c.SrcDir + "/util/buildscripts/build.sh"

	args := []string{"--profile", profilePath}

	if c.Bin != "" {
		args = append(args, []string{"--bin", c.Bin})
	}

	cmd := exec.Command(buildScriptPath, args...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return
	}

	scanner := bufio.NewScanner(stdout)
	go func() {
		for scanner.Scan() {
			fmt.Println(scanner.Text())
		}
	}()

	err = cmd.Run()
	if err != nil {
		return errors.New("Build command failed")
	}

	return
}
<commit_msg>Add example<commit_after>package dojoBuilder

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"syscall"
	"text/template"
)

const profileTemplate = `var profile = {{.}};`

type BuildConfig struct {
	RemoveUncompressed    bool `json:"removeUncompressed,omitempty"` // Remove uncompressed js files after build
	RemoveConsoleStripped bool `json:"removeConsoleStripped,omitempty"`

	BasePath    string           `json:"basePath"`
	ReleaseDir  string           `json:"releaseDir"`
	ReleaseName string           `json:"releaseName,omitempty"`
	Action      string           `json:"action"`
	Packages    []Package        `json:"packages"`
	Layers      map[string]Layer `json:"layers"`

	LayerOptimize     string             `json:"layerOptimize,omitempty"`
	Optimize          string             `json:"optimize,omitempty"`
	CssOptimize       string             `json:"cssOptimize,omitempty"`
	Mini              bool               `json:"mini,omitempty"`
	StripConsole      string             `json:"stripConsole,omitempty"`
	SelectorEngine    string             `json:"selectorEngine,omitempty"`
	StaticHasFeatures map[string]Feature `json:"staticHasFeatures,omitempty"`
	UseSourceMaps     bool               `json:"useSourceMaps"` // Build generate source maps
}

type Package struct {
	Name     string `json:"name"`
	Location string `json:"location"`
}

type Layer struct {
	Boot       bool     `json:"boot"`
	CustomBase bool     `json:"customBase"`
	Include    []string `json:"include,omitempty"`
	Exclude    []string `json:"exclude,omitempty"`
}

type Feature bool

func (f Feature) MarshalJSON() ([]byte, error) {
	var v uint8 = 0
	if bool(f) {
		v = 1
	}
	return json.Marshal(v)
}

func (c *Config) generateBuildProfile(name string) (profileFullPath string, err error) {
	installDir := c.DestDir + "/tmp"
	bc, ok := c.BuildConfigs[name]
	if !ok {
		return "", errors.New("No build config found with name '" + name + "'")
	}

	if bc.Action == "" {
		bc.Action = "release"
	}

	profilePath := installDir + "/profiles/"
	os.MkdirAll(profilePath, 0754)

	profileFullPath = profilePath + name + ".profile.js"

	bc.BasePath = ".."

	bc.ReleaseDir = c.DestDir
	// if bc.ReleaseDir, err = filepath.Rel(c.SrcDir+`/`+bc.BasePath+"__fakeFile__", c.DestDir); err != nil {
	// 	return "", err
	// }

	j, err := json.Marshal(bc)
	if err != nil {
		return "", err
	}

	f, err := os.OpenFile(profileFullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664)
	if err != nil {
		return "", err
	}

	t := template.Must(template.New("profileTemplate").Parse(profileTemplate))
	err = t.Execute(f, string(j))

	return profileFullPath, err
}

func build(c *Config, names []string) (err error) {
	var profilePath string

	if len(names) == 0 {
		for n, _ := range c.BuildConfigs {
			names = append(names, n)
		}
	}

	for _, n := range names {
		fmt.Printf("Generating %s build\n", n)

		profilePath, err = c.generateBuildProfile(n)
		if err != nil {
			return
		}

		if err = executeBuildProfile(c, profilePath); err != nil {
			return
		}

		bc, _ := c.BuildConfigs[n]

		var removePattern, sep string

		if bc.RemoveUncompressed {
			removePattern += sep + `uncompressed`
			sep = `|`
		}

		if bc.RemoveConsoleStripped {
			removePattern += sep + `consoleStripped`
			sep = `|`
		}

		filepath.Walk(c.DestDir, func(path string, f os.FileInfo, err error) (_err error) {
			originPath := c.SrcDir + path[len(c.DestDir):]

			if fi, err := os.Stat(originPath); err == nil {
				st := fi.Sys().(*syscall.Stat_t)
				os.Chown(path, int(st.Uid), int(st.Gid))
			}

			if removePattern != `` {
				if match, _err := regexp.MatchString(`.*\.js\.(`+removePattern+`)\.js`, f.Name()); _err != nil {
					return _err
				} else if match {
					fmt.Println("Removing " + path)
					_err = os.Remove(path)
				}
			}

			return
		})
		if c.installDir != "" {
			os.Remove(c.installDir)
		}
	}

	return
}

func executeBuildProfile(c *Config, profilePath string) (err error) {
	buildScriptPath := c.SrcDir + "/util/buildscripts/build.sh"

	args := []string{"--profile", profilePath}

	if c.Bin != "" {
		args = append(args, "--bin", c.Bin)
	}

	cmd := exec.Command(buildScriptPath, args...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return
	}

	scanner := bufio.NewScanner(stdout)
	go func() {
		for scanner.Scan() {
			fmt.Println(scanner.Text())
		}
	}()

	err = cmd.Run()
	if err != nil {
		return errors.New("Build command failed")
	}

	return
}
<|endoftext|>


<commit_before>package cluster

import (
	"io"
	"net"

	"github.com/gfandada/gserver/cluster/pb"
	"github.com/gfandada/gserver/logger"
	"github.com/gfandada/gserver/network/protobuff"
	"google.golang.org/grpc"
)

type Service struct {
	Name    string
	Manager *protobuff.MsgManager
}

type ServiceAck struct {
	Stream   pb.ClusterService_RouterServer
	Die      chan struct{}
	UserData interface{}
	Service  *Service
	SendCh   chan protobuff.RawMessage
}

func Start(name string, add string, code *protobuff.MsgManager) {
	listen, err := net.Listen("tcp", add)
	if err != nil {
		logger.Error("service {%s} listen error {%v}", name, err)
	}
	s := new(Service)
	s.Name = name
	s.Manager = code
	serve := grpc.NewServer()
	pb.RegisterClusterServiceServer(serve, s)
	logger.Info("service {%s:%s} run", name, add)
	go serve.Serve(listen)
}

/************************实现ClusterServiceClient接口*************************/

func (s *Service) Router(stream pb.ClusterService_RouterServer) error {
	die := make(chan struct{})
	defer func() {
		die <- struct{}{}
		close(die)
	}()
	recvChan := s.recv(stream, die)
	ack := &ServiceAck{
		Stream:  stream,
		Die:     die,
		Service: s,
	}
	for {
		select {
		case data, ok := <-recvChan:
			if !ok {
				return nil
			}
			msg, err := s.Manager.Deserialize(data.Data)
			if err == nil {
				logger.Debug("cluster service {%s} recv {%d:%v}", s.Name,
					msg.MsgId, msg.MsgData)
				// 异步路由
				s.Manager.Router(msg, ack)
			}
		// 收到关闭信号
		case <-die:
		}
	}
	return nil
}

func (s *Service) send(stream pb.ClusterService_RouterServer, data protobuff.RawMessage) error {
	dataNew, err := s.Manager.Serialize(data)
	if err != nil {
		logger.Error("cluster service {%s} Serialize error : %v", s.Name, err)
		return err
	}
	sendM := &pb.Message{
		Data: dataNew,
		Id:   uint32(data.MsgId),
	}
	logger.Debug("cluster service {%s} ack {%d:%v}", s.Name,
		sendM.Id, sendM.Data)
	if err := stream.Send(sendM); err != nil {
		logger.Error("cluster service {%s} send error: %v", s.Name, err)
		return err
	}
	return nil
}

func (s *Service) recv(stream pb.ClusterService_RouterServer, die chan struct{}) chan *pb.Message {
	recvChan := make(chan *pb.Message, 1)
	go func() {
		defer close(recvChan)
		for {
			in, err := stream.Recv()
			if err == io.EOF {
				return
			}
			if err != nil {
				logger.Error("cluster service {%s} recv error : %v", s.Name, err)
				return
			}
			select {
			case recvChan <- in:
				logger.Debug("cluster service {%s} recv {%d:%v}", s.Name,
					in.Id, in.Data)
			case <-die:
			}
		}
	}()
	return recvChan
}

func (s *Service) ack(id uint64, sack *ServiceAck) {
	sack.SendCh = make(chan protobuff.RawMessage, 512)
	Register(id, sack)
	go func() {
		defer close(sack.SendCh)
		for {
			select {
			case data := <-sack.SendCh:
				if err := s.send(sack.Stream, data); err != nil {
					logger.Error("cluster service {%s} ack error {%v}", s.Name, err)
					return
				}
			case <-sack.Die:
			}
		}
	}()
}

/***********************************实现Iack接口************************************/

func (s *ServiceAck) Ack(data []interface{}) {
	switch len(data) {
	// 同步ack
	case 1:
		s.Service.send(s.Stream, data[0].(protobuff.RawMessage))
	// 同步ack，更新session --> [userid]chan
	case 2:
		s.Service.ack(data[1].(uint64), s)
		s.UserData = data[1].(uint64)
		s.Service.send(s.Stream, data[0].(protobuff.RawMessage))
	}
}
<commit_msg>update slave<commit_after>package cluster

import (
	"io"
	"net"

	"github.com/gfandada/gserver/cluster/pb"
	"github.com/gfandada/gserver/logger"
	"github.com/gfandada/gserver/network/protobuff"
	"google.golang.org/grpc"
)

type Service struct {
	Name    string
	Manager *protobuff.MsgManager
}

type ServiceAck struct {
	Stream   pb.ClusterService_RouterServer
	Die      chan struct{}
	UserData interface{}
	Service  *Service
	SendCh   chan protobuff.RawMessage
}

func Start(name string, add string, code *protobuff.MsgManager) {
	listen, err := net.Listen("tcp", add)
	if err != nil {
		logger.Error("service {%s} listen error {%v}", name, err)
	}
	s := new(Service)
	s.Name = name
	s.Manager = code
	serve := grpc.NewServer()
	pb.RegisterClusterServiceServer(serve, s)
	logger.Info("service {%s:%s} run", name, add)
	go serve.Serve(listen)
}

/************************实现ClusterServiceClient接口*************************/

func (s *Service) Router(stream pb.ClusterService_RouterServer) error {
	die := make(chan struct{})
	defer func() {
		die <- struct{}{}
		close(die)
	}()
	recvChan := s.recv(stream, die)
	ack := &ServiceAck{
		Stream:  stream,
		Die:     die,
		Service: s,
	}
	for {
		select {
		case data, ok := <-recvChan:
			if !ok {
				return nil
			}
			msg, err := s.Manager.Deserialize(data.Data)
			if err == nil {
				logger.Debug("cluster service {%s} recv {%d:%v}", s.Name,
					msg.MsgId, msg.MsgData)
				s.Manager.Router(msg, ack)
			}
		case <-die:
		}
	}
	return nil
}

func (s *Service) send(stream pb.ClusterService_RouterServer, data protobuff.RawMessage) error {
	dataNew, err := s.Manager.Serialize(data)
	if err != nil {
		logger.Error("cluster service {%s} Serialize error : %v", s.Name, err)
		return err
	}
	sendM := &pb.Message{
		Data: dataNew,
		Id:   uint32(data.MsgId),
	}
	logger.Debug("cluster service {%s} ack {%d:%v}", s.Name,
		sendM.Id, sendM.Data)
	if err := stream.Send(sendM); err != nil {
		logger.Error("cluster service {%s} send error: %v", s.Name, err)
		return err
	}
	return nil
}

func (s *Service) recv(stream pb.ClusterService_RouterServer, die chan struct{}) chan *pb.Message {
	recvChan := make(chan *pb.Message, 1)
	go func() {
		defer close(recvChan)
		for {
			in, err := stream.Recv()
			if err == io.EOF {
				return
			}
			if err != nil {
				logger.Error("cluster service {%s} recv error : %v", s.Name, err)
				return
			}
			select {
			case recvChan <- in:
				logger.Debug("cluster service {%s} recv {%d:%v}", s.Name,
					in.Id, in.Data)
			case <-die:
			}
		}
	}()
	return recvChan
}

func (s *Service) ack(id uint64, sack *ServiceAck) {
	sack.SendCh = make(chan protobuff.RawMessage, 512)
	Register(id, sack)
	go func() {
		defer close(sack.SendCh)
		for {
			select {
			case data := <-sack.SendCh:
				if err := s.send(sack.Stream, data); err != nil {
					logger.Error("cluster service {%s} ack error {%v}", s.Name, err)
					return
				}
			case <-sack.Die:
			}
		}
	}()
}

/***********************************实现Iack接口************************************/

func (s *ServiceAck) Ack(data []interface{}) {
	switch len(data) {
	// ack自己
	case 1:
		s.Service.send(s.Stream, data[0].(protobuff.RawMessage))
	// ack自己，更新session --> [userid]chan
	case 2:
		s.Service.ack(data[1].(uint64), s)
		s.UserData = data[1].(uint64)
		s.Service.send(s.Stream, data[0].(protobuff.RawMessage))
	}
}
<|endoftext|>


<commit_before>package cmap

import (
	"testing"
)

func TestMapCreation(t *testing.T) {
	m := NewLongConcurrentHashMap()

	if m == nil {
		t.Error("map is null")
	}

	if m.Size() != 0 {
		t.Error("map is not empty")
	}
}
<commit_msg>update the map_test.go....<commit_after>package cmap

import (
	"testing"
	"strconv"
)

type Girl struct {
	name string
}

func TestMapCreation(t *testing.T) {
	m := NewLongConcurrentHashMap()

	if m == nil {
		t.Error("map is null")
	}

	if m.Size() != 0 {
		t.Error("map is not empty")
	}
}

func TestPut(t *testing.T) {
	m := NewLongConcurrentHashMap()

   	lily := Girl{"Lily"}
	lucy := Girl{"Lucy"}

	m.Put(2, lily)
	m.Put(3, lucy)

	if m.Size() != 2 {
		t.Error("map should contain only two elements")
	}
}

func TestGet(t *testing.T) {
	m := NewLongConcurrentHashMap()

	value, ok := m.Get(1)

	if ok == true {
		t.Error("ok should be false")
	}
	if value != nil {
		t.Error("value should be nil")
	}

	clair := Girl{"Clair"}

	m.Put(1, clair)
	temp, ok := m.Get(1)

	value := temp.(Girl)

	if ok == false {
		t.Error("ok should be true")
	}
	if &value == nil {
		t.Error("value should not be null")
	}
	if value.name != "Clair" {
		t.Error("value is modified")
	}
}

func TestRemove(t *testing.T) {
	m := NewLongConcurrentHashMap()

	alice := Girl{"Alice"}
	m.Put(3, alice)

	temp, ok := m.Get(3)
	if ok == false {
		t.Error("ok should be true")
	}

	m.Remove(3)
	temp, ok := m.Get(3)
	if ok == true {
		t.Error("ok should be false")
	}
	if temp != nil {
		t.Error("temp should be null")
	}
}

func TestSize(t *testing.T) {
	m := NewLongConcurrentHashMap()

	if m.Size() != 0 {
		t.Error("map should be empty")
	}

	alma := Girl{"Alma"}
	alva := Girl{"Alva"}
	m.Put(1, alma)
	m.Put(2, alva)

	if m.Size() != 2 {
		t.Error("map should just contain only two elements")
	}
}

func TestContains(t *testing.T) {
	m := NewLongConcurrentHashMap()

	if m.Contains(1) == true {
		t.Error("map should not contain this key")
	}

	amy := Girl{"Amy"}
	m.Put(7, amy)

	if m.Contains(7) == false {
		t.Error("map should contain this key")
	}
}

func TestIsEmpty(t *testing.T) {
	m := NewLongConcurrentHashMap()

	if m.IsEmpty() == false {
		t.Error("map should be empty")
	}

	m.Put(1, Girl{"Andrea"})
	if m.IsEmpty() == true {
		t.Error("map should not be empty")
	}
}

func TestClear(t *testing.T) {
	m := NewLongConcurrentHashMap()

	m.Clear()
	if m.Size() != 0 {
		t.Error("map should be empty")
	}

	m.Put(1, Girl{"Amanda"})
	m.Clear()
	if m.Size() != 0 {
		t.Error("expect an emtpy map")
	}
}

func TestConcurrent(t *testing.T) {
	m := NewLongConcurrentHashMap()
	ch := make(chan string)
	const loop = 2000
	var s [loop]string

	go func() {
		for i := 0; i < loop/2; i++ {
			m.Put(i, strconv.Itoa(i))

			value, _ := m.Get(i)

			ch <- value.(string)
		}
	}()

	go func() {
		for i := loop/2; i < loop; i++ {
			m.Put(i, strconv.Itoa(i))

			value, _ := m.Get(i)

			ch <- value.(string)
		}
	}()

	// wait
	counter := 0
	for ele := range ch {
		s[counter] = ele;
		counter ++;
		if counter == loop {
			break;
		}
	}

	if m.Size() != loop {
		t.Error("map should contain 2000 elements")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"os"
	"strings"
	"time"

	"code.google.com/p/go.net/idna"
	"github.com/domainr/dnsr"
	"github.com/miekg/dns"
	"github.com/wsxiaoys/terminal/color"
)

const (
	timeout = 2000 * time.Millisecond
)

var (
	verbose  bool
	resolver = dnsr.New(10000)
)

func init() {
	flag.BoolVar(
		&verbose,
		"v",
		false,
		"print verbose info to the console",
	)
}

func logV(fmt string, args ...interface{}) {
	if !verbose {
		return
	}
	color.Printf(fmt, args...)
}

func main() {
	flag.Usage = func() {
		color.Fprintf(os.Stderr, "Usage: %s [arguments] <name> [type]\n\nAvailable arguments:\n", os.Args[0])
		flag.PrintDefaults()
		os.Exit(1)
	}
	flag.Parse()
	rrType := "A"
	args := flag.Args()
	if len(args) == 0 {
		flag.Usage()
	} else if len(args) > 1 {
		rrType, args = args[len(args)-1], args[:len(args)-1]
	}
	start := time.Now()
	for _, name := range args {
		query(name, rrType)
	}
	logV("\n@{w};; Total elapsed: %s\n", time.Since(start).String())
}

func query(name, rrType string) {
	start := time.Now()
	qname, err := idna.ToASCII(name)
	if err != nil {
		color.Fprintf(os.Stderr, "Invalid IDN domain name: %s\n", name)
		os.Exit(1)
	}
	qtype := dns.StringToType[strings.ToUpper(rrType)]

	// q := dns.Question{qname, qtype, dns.ClassINET}
	// rrs := exchange(q)
	rrc := resolver.Resolve(qname, qtype)
	rrs := []dns.RR{}
	for rr := range rrc {
		if rr == nil {
			logV("@{r}\n;; NIL RR!\n")
			continue
		}
		rrs = append(rrs, rr)
	}

	color.Printf("\n")
	if len(rrs) > 0 {
		color.Printf("@{g};; RESULTS:\n")
	}
	for _, rr := range rrs {
		color.Printf("@{g}%s\n", rr.String())
	}

	if rrs == nil {
		color.Printf("@{y};; NIL   %s\n", name)
	} else if len(rrs) > 0 {
		color.Printf("@{g};; TRUE  %s\n", name)
	} else {
		color.Printf("@{r};; FALSE %s\n", name)
	}

	logV("@{.w};; Elapsed: %s\n", time.Since(start).String())
}
<commit_msg>concurrent execution<commit_after>package main

import (
	"flag"
	"os"
	"strings"
	"sync"
	"time"

	"code.google.com/p/go.net/idna"
	"github.com/domainr/dnsr"
	"github.com/miekg/dns"
	"github.com/wsxiaoys/terminal/color"
)

const (
	timeout = 2000 * time.Millisecond
)

var (
	verbose  bool
	resolver = dnsr.New(10000)
)

func init() {
	flag.BoolVar(
		&verbose,
		"v",
		false,
		"print verbose info to the console",
	)
}

func logV(fmt string, args ...interface{}) {
	if !verbose {
		return
	}
	color.Printf(fmt, args...)
}

func main() {
	flag.Usage = func() {
		color.Fprintf(os.Stderr, "Usage: %s [arguments] <name> [type]\n\nAvailable arguments:\n", os.Args[0])
		flag.PrintDefaults()
		os.Exit(1)
	}
	flag.Parse()
	rrType := "A"
	args := flag.Args()
	if len(args) == 0 {
		flag.Usage()
	} else if len(args) > 1 {
		rrType, args = args[len(args)-1], args[:len(args)-1]
	}
	var wg sync.WaitGroup
	start := time.Now()
	for _, name := range args {
		wg.Add(1)
		go func(name string, rrType string) {
			query(name, rrType)
			wg.Done()
		}(name, rrType)
	}
	wg.Wait()
	logV("\n@{w};; Total elapsed: %s\n", time.Since(start).String())
}

func query(name, rrType string) {
	start := time.Now()
	qname, err := idna.ToASCII(name)
	if err != nil {
		color.Fprintf(os.Stderr, "Invalid IDN domain name: %s\n", name)
		os.Exit(1)
	}
	qtype := dns.StringToType[strings.ToUpper(rrType)]

	// q := dns.Question{qname, qtype, dns.ClassINET}
	// rrs := exchange(q)
	rrc := resolver.Resolve(qname, qtype)
	rrs := []dns.RR{}
	for rr := range rrc {
		if rr == nil {
			logV("@{r}\n;; NIL RR!\n")
			continue
		}
		rrs = append(rrs, rr)
	}

	color.Printf("\n")
	if len(rrs) > 0 {
		color.Printf("@{g};; RESULTS:\n")
	}
	for _, rr := range rrs {
		color.Printf("@{g}%s\n", rr.String())
	}

	if rrs == nil {
		color.Printf("@{y};; NIL   %s\n", name)
	} else if len(rrs) > 0 {
		color.Printf("@{g};; TRUE  %s\n", name)
	} else {
		color.Printf("@{r};; FALSE %s\n", name)
	}

	logV("@{.w};; Elapsed: %s\n", time.Since(start).String())
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"image"
	"io"
	"os"
	"path"

	log "github.com/Sirupsen/logrus"
	"github.com/plorefice/develed/imconv"
)

func blitImage(img image.Image, w io.Writer) error {
	var err error

	data := imconv.FromImage(img)

	for total, last := 0, 0; total < len(data); total += last {
		if last, err = w.Write(data[total:]); err != nil {
			return err
		}
	}
	return nil
}

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "Usage: %s FIFO\n", path.Base(os.Args[0]))
		os.Exit(1)
	}

	// Actually read-only, write flag required to avoid blocking on open()
	fifo, err := os.OpenFile(os.Args[1], os.O_RDWR, os.ModeNamedPipe)
	if err != nil {
		log.Fatalln(err)
	}

	dsp, err := os.OpenFile("prova.dat", os.O_WRONLY, os.ModeCharDevice)
	if err != nil {
		log.Fatalln(err)
	}

	src := NewGobImageSource(fifo)
	for {
		img, err := src.Read()
		if err != nil {
			log.Errorln(err)
			continue
		}

		if err := blitImage(img, dsp); err != nil {
			log.Errorln(err)
		}
	}
}
<commit_msg>dspd: handle data stream correctly<commit_after>package main

import (
	"fmt"
	"image"
	"io"
	"os"
	"path"
	"syscall"
	"unsafe"

	log "github.com/Sirupsen/logrus"
	"github.com/plorefice/develed/imconv"
)

const (
	cResetDuration = 60 // [us]
	cBytePerUSec   = 5

	cSampleFormatIoctl = 0xc0045005
)

var resetCmd [cResetDuration * cBytePerUSec]byte

func writeFull(w io.Writer, data []byte) (err error) {
	for total, last := 0, 0; total < len(data); total += last {
		if last, err = w.Write(data[total:]); err != nil {
			return
		}
	}
	return
}

func sendResetCmd(w io.Writer) error {
	return writeFull(w, resetCmd[:])
}

func blitImage(img image.Image, w io.Writer) error {
	if err := writeFull(w, imconv.FromImage(img)); err != nil {
		return err
	}
	return sendResetCmd(w)
}

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "Usage: %s FIFO\n", path.Base(os.Args[0]))
		os.Exit(1)
	}

	// Actually read-only, write flag required to avoid blocking on open()
	fifo, err := os.OpenFile(os.Args[1], os.O_RDWR, os.ModeNamedPipe)
	if err != nil {
		log.Fatalln(err)
	}
	defer fifo.Close()

	dsp, err := os.OpenFile("/dev/dsp", os.O_WRONLY, os.ModeCharDevice)
	if err != nil {
		log.Fatalln(err)
	}
	defer dsp.Close()

	// configure /dev/dsp sample format
	sampleSize := 0x00002000 /* AFMT_S32_BE */
	syscall.Syscall(
		syscall.SYS_IOCTL,
		dsp.Fd(),
		cSampleFormatIoctl,
		uintptr(unsafe.Pointer(&sampleSize)))

	src := NewGobImageSource(fifo)
	for {
		img, err := src.Read()
		if err != nil {
			log.Errorln(err)
			continue
		}

		if err := blitImage(img, dsp); err != nil {
			log.Errorln(err)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2015 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 main

import (
	"context"
	"fmt"
	"math/rand"
	"os"
	"time"

	managedcertificatesclient "github.com/GoogleCloudPlatform/gke-managed-certs/pkg/clientgen/clientset/versioned"
	"github.com/golang/glog"
	flag "github.com/spf13/pflag"

	crdclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	"k8s.io/client-go/kubernetes"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/client-go/tools/record"
	backendconfigclient "k8s.io/ingress-gce/pkg/backendconfig/client/clientset/versioned"

	ingctx "k8s.io/ingress-gce/pkg/context"
	"k8s.io/ingress-gce/pkg/controller"
	neg "k8s.io/ingress-gce/pkg/neg"

	"k8s.io/ingress-gce/cmd/glbc/app"
	"k8s.io/ingress-gce/pkg/backendconfig"
	"k8s.io/ingress-gce/pkg/crd"
	"k8s.io/ingress-gce/pkg/firewalls"
	"k8s.io/ingress-gce/pkg/flags"
	"k8s.io/ingress-gce/pkg/version"
)

func main() {
	flags.Register()
	rand.Seed(time.Now().UTC().UnixNano())

	flag.Parse()
	if flags.F.Verbose {
		flag.Set("v", "3")
	}

	// TODO: remove this when we do a release so the -logtostderr can be
	// used as a proper argument.
	flag.Lookup("logtostderr").Value.Set("true")

	if flags.F.Version {
		fmt.Printf("Controller version: %s\n", version.Version)
		os.Exit(0)
	}

	glog.V(0).Infof("Starting GLBC image: %q, cluster name %q", version.Version, flags.F.ClusterName)
	glog.V(0).Infof("Latest commit hash: %q", version.GitCommit)
	for i, a := range os.Args {
		glog.V(0).Infof("argv[%d]: %q", i, a)
	}

	glog.V(2).Infof("Flags = %+v", flags.F)
	defer glog.Flush()
	kubeConfig, err := app.NewKubeConfig()
	if err != nil {
		glog.Fatalf("Failed to create kubernetes client config: %v", err)
	}

	kubeClient, err := kubernetes.NewForConfig(kubeConfig)
	if err != nil {
		glog.Fatalf("Failed to create kubernetes client: %v", err)
	}

	// Ingress only reads status of ManagedCertificate CR which is set in another component.
	mcrtClient, err := managedcertificatesclient.NewForConfig(kubeConfig)
	if err != nil {
		glog.Fatalf("Failed to create Managed Certificates client: %v", err)
	}

	var backendConfigClient backendconfigclient.Interface
	if flags.F.EnableBackendConfig {
		crdClient, err := crdclient.NewForConfig(kubeConfig)
		if err != nil {
			glog.Fatalf("Failed to create kubernetes CRD client: %v", err)
		}
		// TODO(rramkumar): Reuse this CRD handler for other CRD's coming.
		crdHandler := crd.NewCRDHandler(crdClient)
		backendConfigCRDMeta := backendconfig.CRDMeta()
		if _, err := crdHandler.EnsureCRD(backendConfigCRDMeta); err != nil {
			glog.Fatalf("Failed to ensure BackendConfig CRD: %v", err)
		}

		backendConfigClient, err = backendconfigclient.NewForConfig(kubeConfig)
		if err != nil {
			glog.Fatalf("Failed to create BackendConfig client: %v", err)
		}
	}

	namer, err := app.NewNamer(kubeClient, flags.F.ClusterName, firewalls.DefaultFirewallName)
	if err != nil {
		glog.Fatalf("app.NewNamer(ctx.KubeClient, %q, %q) = %v", flags.F.ClusterName, firewalls.DefaultFirewallName, err)
	}
	if namer.UID() != "" {
		glog.V(0).Infof("Cluster name: %+v", namer.UID())
	}

	cloud := app.NewGCEClient()
	enableNEG := flags.F.Features.NEG
	defaultBackendServicePortID := app.DefaultBackendServicePortID(kubeClient)
	ctxConfig := ingctx.ControllerContextConfig{
		NEGEnabled:                    enableNEG,
		BackendConfigEnabled:          flags.F.EnableBackendConfig,
		ManagedCertificateEnabled:     flags.F.Features.ManagedCertificates,
		Namespace:                     flags.F.WatchNamespace,
		ResyncPeriod:                  flags.F.ResyncPeriod,
		DefaultBackendSvcPortID:       defaultBackendServicePortID,
		HealthCheckPath:               flags.F.HealthCheckPath,
		DefaultBackendHealthCheckPath: flags.F.DefaultSvcHealthCheckPath,
	}
	ctx := ingctx.NewControllerContext(kubeClient, backendConfigClient, mcrtClient, cloud, namer, ctxConfig)
	go app.RunHTTPServer(ctx.HealthCheck)

	if !flags.F.LeaderElection.LeaderElect {
		runControllers(ctx)
		return
	}

	electionConfig, err := makeLeaderElectionConfig(kubeClient, ctx.Recorder(flags.F.LeaderElection.LockObjectNamespace), func() {
		runControllers(ctx)
	})
	if err != nil {
		glog.Fatalf("%v", err)
	}
	leaderelection.RunOrDie(context.Background(), *electionConfig)
}

// makeLeaderElectionConfig builds a leader election configuration. It will
// create a new resource lock associated with the configuration.
func makeLeaderElectionConfig(client clientset.Interface, recorder record.EventRecorder, run func()) (*leaderelection.LeaderElectionConfig, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, fmt.Errorf("unable to get hostname: %v", err)
	}
	// add a uniquifier so that two processes on the same host don't accidentally both become active
	id := fmt.Sprintf("%v_%x", hostname, rand.Intn(1e6))
	rl, err := resourcelock.New(resourcelock.ConfigMapsResourceLock,
		flags.F.LeaderElection.LockObjectNamespace,
		flags.F.LeaderElection.LockObjectName,
		client.CoreV1(),
		resourcelock.ResourceLockConfig{
			Identity:      id,
			EventRecorder: recorder,
		})
	if err != nil {
		return nil, fmt.Errorf("couldn't create resource lock: %v", err)
	}

	return &leaderelection.LeaderElectionConfig{
		Lock:          rl,
		LeaseDuration: flags.F.LeaderElection.LeaseDuration.Duration,
		RenewDeadline: flags.F.LeaderElection.RenewDeadline.Duration,
		RetryPeriod:   flags.F.LeaderElection.RetryPeriod.Duration,
		Callbacks: leaderelection.LeaderCallbacks{
			OnStartedLeading: func(context.Context) {
				// Since we are committing a suicide after losing
				// mastership, we can safely ignore the argument.
				run()
			},
			OnStoppedLeading: func() {
				glog.Fatalf("lost master")
			},
		},
	}, nil
}

func runControllers(ctx *ingctx.ControllerContext) {
	stopCh := make(chan struct{})
	lbc := controller.NewLoadBalancerController(ctx, stopCh)

	fwc := firewalls.NewFirewallController(ctx, flags.F.NodePortRanges.Values())

	if ctx.NEGEnabled {
		// TODO: Refactor NEG to use cloud mocks so ctx.Cloud can be referenced within NewController.
		negController := neg.NewController(neg.NewAdapter(ctx.Cloud), ctx, lbc.Translator, ctx.ClusterNamer, flags.F.ResyncPeriod, flags.F.NegGCPeriod, neg.NegSyncerType(flags.F.NegSyncerType))
		go negController.Run(stopCh)
		glog.V(0).Infof("negController started")
	}

	go app.RunSIGTERMHandler(lbc, flags.F.DeleteAllOnQuit)

	go fwc.Run()
	glog.V(0).Infof("firewall controller started")

	ctx.Start(stopCh)
	lbc.Init()
	lbc.Run()

	for {
		glog.Infof("Handled quit, awaiting pod deletion.")
		time.Sleep(30 * time.Second)
	}
}
<commit_msg>Configure leader election with completely separate k8s client<commit_after>/*
Copyright 2015 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 main

import (
	"context"
	"fmt"
	"math/rand"
	"os"
	"time"

	managedcertificatesclient "github.com/GoogleCloudPlatform/gke-managed-certs/pkg/clientgen/clientset/versioned"
	"github.com/golang/glog"
	flag "github.com/spf13/pflag"

	crdclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	"k8s.io/client-go/kubernetes"
	clientset "k8s.io/client-go/kubernetes"
	restclient "k8s.io/client-go/rest"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/client-go/tools/record"
	backendconfigclient "k8s.io/ingress-gce/pkg/backendconfig/client/clientset/versioned"

	ingctx "k8s.io/ingress-gce/pkg/context"
	"k8s.io/ingress-gce/pkg/controller"
	neg "k8s.io/ingress-gce/pkg/neg"

	"k8s.io/ingress-gce/cmd/glbc/app"
	"k8s.io/ingress-gce/pkg/backendconfig"
	"k8s.io/ingress-gce/pkg/crd"
	"k8s.io/ingress-gce/pkg/firewalls"
	"k8s.io/ingress-gce/pkg/flags"
	"k8s.io/ingress-gce/pkg/version"
)

func main() {
	flags.Register()
	rand.Seed(time.Now().UTC().UnixNano())

	flag.Parse()
	if flags.F.Verbose {
		flag.Set("v", "3")
	}

	// TODO: remove this when we do a release so the -logtostderr can be
	// used as a proper argument.
	flag.Lookup("logtostderr").Value.Set("true")

	if flags.F.Version {
		fmt.Printf("Controller version: %s\n", version.Version)
		os.Exit(0)
	}

	glog.V(0).Infof("Starting GLBC image: %q, cluster name %q", version.Version, flags.F.ClusterName)
	glog.V(0).Infof("Latest commit hash: %q", version.GitCommit)
	for i, a := range os.Args {
		glog.V(0).Infof("argv[%d]: %q", i, a)
	}

	glog.V(2).Infof("Flags = %+v", flags.F)
	defer glog.Flush()
	kubeConfig, err := app.NewKubeConfig()
	if err != nil {
		glog.Fatalf("Failed to create kubernetes client config: %v", err)
	}

	kubeClient, err := kubernetes.NewForConfig(kubeConfig)
	if err != nil {
		glog.Fatalf("Failed to create kubernetes client: %v", err)
	}

	// Due to scaling issues, leader election must be configured with a separate k8s client.
	leaderElectKubeClient, err := kubernetes.NewForConfig(restclient.AddUserAgent(kubeConfig, "leader-election"))
	if err != nil {
		glog.Fatalf("Failed to create kubernetes client for leader election: %v", err)
	}

	// Ingress only reads status of ManagedCertificate CR which is set in another component.
	mcrtClient, err := managedcertificatesclient.NewForConfig(kubeConfig)
	if err != nil {
		glog.Fatalf("Failed to create Managed Certificates client: %v", err)
	}

	var backendConfigClient backendconfigclient.Interface
	if flags.F.EnableBackendConfig {
		crdClient, err := crdclient.NewForConfig(kubeConfig)
		if err != nil {
			glog.Fatalf("Failed to create kubernetes CRD client: %v", err)
		}
		// TODO(rramkumar): Reuse this CRD handler for other CRD's coming.
		crdHandler := crd.NewCRDHandler(crdClient)
		backendConfigCRDMeta := backendconfig.CRDMeta()
		if _, err := crdHandler.EnsureCRD(backendConfigCRDMeta); err != nil {
			glog.Fatalf("Failed to ensure BackendConfig CRD: %v", err)
		}

		backendConfigClient, err = backendconfigclient.NewForConfig(kubeConfig)
		if err != nil {
			glog.Fatalf("Failed to create BackendConfig client: %v", err)
		}
	}

	namer, err := app.NewNamer(kubeClient, flags.F.ClusterName, firewalls.DefaultFirewallName)
	if err != nil {
		glog.Fatalf("app.NewNamer(ctx.KubeClient, %q, %q) = %v", flags.F.ClusterName, firewalls.DefaultFirewallName, err)
	}
	if namer.UID() != "" {
		glog.V(0).Infof("Cluster name: %+v", namer.UID())
	}

	cloud := app.NewGCEClient()
	enableNEG := flags.F.Features.NEG
	defaultBackendServicePortID := app.DefaultBackendServicePortID(kubeClient)
	ctxConfig := ingctx.ControllerContextConfig{
		NEGEnabled:                    enableNEG,
		BackendConfigEnabled:          flags.F.EnableBackendConfig,
		ManagedCertificateEnabled:     flags.F.Features.ManagedCertificates,
		Namespace:                     flags.F.WatchNamespace,
		ResyncPeriod:                  flags.F.ResyncPeriod,
		DefaultBackendSvcPortID:       defaultBackendServicePortID,
		HealthCheckPath:               flags.F.HealthCheckPath,
		DefaultBackendHealthCheckPath: flags.F.DefaultSvcHealthCheckPath,
	}
	ctx := ingctx.NewControllerContext(kubeClient, backendConfigClient, mcrtClient, cloud, namer, ctxConfig)
	go app.RunHTTPServer(ctx.HealthCheck)

	if !flags.F.LeaderElection.LeaderElect {
		runControllers(ctx)
		return
	}

	electionConfig, err := makeLeaderElectionConfig(leaderElectKubeClient, ctx.Recorder(flags.F.LeaderElection.LockObjectNamespace), func() {
		runControllers(ctx)
	})
	if err != nil {
		glog.Fatalf("%v", err)
	}
	leaderelection.RunOrDie(context.Background(), *electionConfig)
}

// makeLeaderElectionConfig builds a leader election configuration. It will
// create a new resource lock associated with the configuration.
func makeLeaderElectionConfig(client clientset.Interface, recorder record.EventRecorder, run func()) (*leaderelection.LeaderElectionConfig, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, fmt.Errorf("unable to get hostname: %v", err)
	}
	// add a uniquifier so that two processes on the same host don't accidentally both become active
	id := fmt.Sprintf("%v_%x", hostname, rand.Intn(1e6))
	rl, err := resourcelock.New(resourcelock.ConfigMapsResourceLock,
		flags.F.LeaderElection.LockObjectNamespace,
		flags.F.LeaderElection.LockObjectName,
		client.CoreV1(),
		resourcelock.ResourceLockConfig{
			Identity:      id,
			EventRecorder: recorder,
		})
	if err != nil {
		return nil, fmt.Errorf("couldn't create resource lock: %v", err)
	}

	return &leaderelection.LeaderElectionConfig{
		Lock:          rl,
		LeaseDuration: flags.F.LeaderElection.LeaseDuration.Duration,
		RenewDeadline: flags.F.LeaderElection.RenewDeadline.Duration,
		RetryPeriod:   flags.F.LeaderElection.RetryPeriod.Duration,
		Callbacks: leaderelection.LeaderCallbacks{
			OnStartedLeading: func(context.Context) {
				// Since we are committing a suicide after losing
				// mastership, we can safely ignore the argument.
				run()
			},
			OnStoppedLeading: func() {
				glog.Fatalf("lost master")
			},
		},
	}, nil
}

func runControllers(ctx *ingctx.ControllerContext) {
	stopCh := make(chan struct{})
	lbc := controller.NewLoadBalancerController(ctx, stopCh)

	fwc := firewalls.NewFirewallController(ctx, flags.F.NodePortRanges.Values())

	if ctx.NEGEnabled {
		// TODO: Refactor NEG to use cloud mocks so ctx.Cloud can be referenced within NewController.
		negController := neg.NewController(neg.NewAdapter(ctx.Cloud), ctx, lbc.Translator, ctx.ClusterNamer, flags.F.ResyncPeriod, flags.F.NegGCPeriod, neg.NegSyncerType(flags.F.NegSyncerType))
		go negController.Run(stopCh)
		glog.V(0).Infof("negController started")
	}

	go app.RunSIGTERMHandler(lbc, flags.F.DeleteAllOnQuit)

	go fwc.Run()
	glog.V(0).Infof("firewall controller started")

	ctx.Start(stopCh)
	lbc.Init()
	lbc.Run()

	for {
		glog.Infof("Handled quit, awaiting pod deletion.")
		time.Sleep(30 * time.Second)
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The Go 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 main

// TODO(adonovan): new queries
// - show all statements that may update the selected lvalue
//   (local, global, field, etc).
// - show all places where an object of type T is created
//   (&T{}, var t T, new(T), new(struct{array [3]T}), etc.

import (
	"encoding/json"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"log"
	"path/filepath"
	"strings"

	"golang.org/x/tools/go/ast/astutil"
	"golang.org/x/tools/go/buildutil"
	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/pointer"
	"golang.org/x/tools/go/ssa"
)

type printfFunc func(pos interface{}, format string, args ...interface{})

// A QueryResult is an item of output.  Each query produces a stream of
// query results, calling Query.Output for each one.
type QueryResult interface {
	// JSON returns the QueryResult in JSON form.
	JSON(fset *token.FileSet) []byte

	// PrintPlain prints the QueryResult in plain text form.
	// The implementation calls printfFunc to print each line of output.
	PrintPlain(printf printfFunc)
}

// A QueryPos represents the position provided as input to a query:
// a textual extent in the program's source code, the AST node it
// corresponds to, and the package to which it belongs.
// Instances are created by parseQueryPos.
type queryPos struct {
	fset       *token.FileSet
	start, end token.Pos           // source extent of query
	path       []ast.Node          // AST path from query node to root of ast.File
	exact      bool                // 2nd result of PathEnclosingInterval
	info       *loader.PackageInfo // type info for the queried package (nil for fastQueryPos)
}

// TypeString prints type T relative to the query position.
func (qpos *queryPos) typeString(T types.Type) string {
	return types.TypeString(T, types.RelativeTo(qpos.info.Pkg))
}

// ObjectString prints object obj relative to the query position.
func (qpos *queryPos) objectString(obj types.Object) string {
	return types.ObjectString(obj, types.RelativeTo(qpos.info.Pkg))
}

// SelectionString prints selection sel relative to the query position.
func (qpos *queryPos) selectionString(sel *types.Selection) string {
	return types.SelectionString(sel, types.RelativeTo(qpos.info.Pkg))
}

// A Query specifies a single guru query.
type Query struct {
	Pos   string         // query position
	Build *build.Context // package loading configuration

	// pointer analysis options
	Scope      []string  // main packages in (*loader.Config).FromArgs syntax
	PTALog     io.Writer // (optional) pointer-analysis log file
	Reflection bool      // model reflection soundly (currently slow).

	// result-printing function
	Output func(*token.FileSet, QueryResult)
}

// Run runs an guru query and populates its Fset and Result.
func Run(mode string, q *Query) error {
	switch mode {
	case "callees":
		return callees(q)
	case "callers":
		return callers(q)
	case "callstack":
		return callstack(q)
	case "peers":
		return peers(q)
	case "pointsto":
		return pointsto(q)
	case "whicherrs":
		return whicherrs(q)
	case "definition":
		return definition(q)
	case "describe":
		return describe(q)
	case "freevars":
		return freevars(q)
	case "implements":
		return implements(q)
	case "referrers":
		return referrers(q)
	case "what":
		return what(q)
	default:
		return fmt.Errorf("invalid mode: %q", mode)
	}
}

func setPTAScope(lconf *loader.Config, scope []string) error {
	pkgs := buildutil.ExpandPatterns(lconf.Build, scope)
	if len(pkgs) == 0 {
		return fmt.Errorf("no packages specified for pointer analysis scope")
	}
	// The value of each entry in pkgs is true,
	// giving ImportWithTests (not Import) semantics.
	lconf.ImportPkgs = pkgs
	return nil
}

// Create a pointer.Config whose scope is the initial packages of lprog
// and their dependencies.
func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflection bool) (*pointer.Config, error) {
	// For each initial package (specified on the command line),
	// if it has a main function, analyze that,
	// otherwise analyze its tests, if any.
	var mains []*ssa.Package
	for _, info := range lprog.InitialPackages() {
		p := prog.Package(info.Pkg)

		// Add package to the pointer analysis scope.
		if p.Pkg.Name() == "main" && p.Func("main") != nil {
			mains = append(mains, p)
		} else if main := prog.CreateTestMainPackage(p); main != nil {
			mains = append(mains, main)
		}
	}
	if mains == nil {
		return nil, fmt.Errorf("analysis scope has no main and no tests")
	}
	return &pointer.Config{
		Log:        ptaLog,
		Reflection: reflection,
		Mains:      mains,
	}, nil
}

// importQueryPackage finds the package P containing the
// query position and tells conf to import it.
// It returns the package's path.
func importQueryPackage(pos string, conf *loader.Config) (string, error) {
	fqpos, err := fastQueryPos(conf.Build, pos)
	if err != nil {
		return "", err // bad query
	}
	filename := fqpos.fset.File(fqpos.start).Name()

	_, importPath, err := guessImportPath(filename, conf.Build)
	if err != nil {
		// Can't find GOPATH dir.
		// Treat the query file as its own package.
		importPath = "command-line-arguments"
		conf.CreateFromFilenames(importPath, filename)
	} else {
		// Check that it's possible to load the queried package.
		// (e.g. guru tests contain different 'package' decls in same dir.)
		// Keep consistent with logic in loader/util.go!
		cfg2 := *conf.Build
		cfg2.CgoEnabled = false
		bp, err := cfg2.Import(importPath, "", 0)
		if err != nil {
			return "", err // no files for package
		}

		switch pkgContainsFile(bp, filename) {
		case 'T':
			conf.ImportWithTests(importPath)
		case 'X':
			conf.ImportWithTests(importPath)
			importPath += "_test" // for TypeCheckFuncBodies
		case 'G':
			conf.Import(importPath)
		default:
			// This happens for ad-hoc packages like
			// $GOROOT/src/net/http/triv.go.
			return "", fmt.Errorf("package %q doesn't contain file %s",
				importPath, filename)
		}
	}

	conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath }

	return importPath, nil
}

// pkgContainsFile reports whether file was among the packages Go
// files, Test files, eXternal test files, or not found.
func pkgContainsFile(bp *build.Package, filename string) byte {
	for i, files := range [][]string{bp.GoFiles, bp.TestGoFiles, bp.XTestGoFiles} {
		for _, file := range files {
			if sameFile(filepath.Join(bp.Dir, file), filename) {
				return "GTX"[i]
			}
		}
	}
	return 0 // not found
}

// ParseQueryPos parses the source query position pos and returns the
// AST node of the loaded program lprog that it identifies.
// If needExact, it must identify a single AST subtree;
// this is appropriate for queries that allow fairly arbitrary syntax,
// e.g. "describe".
//
func parseQueryPos(lprog *loader.Program, pos string, needExact bool) (*queryPos, error) {
	filename, startOffset, endOffset, err := parsePos(pos)
	if err != nil {
		return nil, err
	}

	// Find the named file among those in the loaded program.
	var file *token.File
	lprog.Fset.Iterate(func(f *token.File) bool {
		if sameFile(filename, f.Name()) {
			file = f
			return false // done
		}
		return true // continue
	})
	if file == nil {
		return nil, fmt.Errorf("file %s not found in loaded program", filename)
	}

	start, end, err := fileOffsetToPos(file, startOffset, endOffset)
	if err != nil {
		return nil, err
	}
	info, path, exact := lprog.PathEnclosingInterval(start, end)
	if path == nil {
		return nil, fmt.Errorf("no syntax here")
	}
	if needExact && !exact {
		return nil, fmt.Errorf("ambiguous selection within %s", astutil.NodeDescription(path[0]))
	}
	return &queryPos{lprog.Fset, start, end, path, exact, info}, nil
}

// ---------- Utilities ----------

// loadWithSoftErrors calls lconf.Load, suppressing "soft" errors.  (See Go issue 16530.)
// TODO(adonovan): Once the loader has an option to allow soft errors,
// replace calls to loadWithSoftErrors with loader calls with that parameter.
func loadWithSoftErrors(lconf *loader.Config) (*loader.Program, error) {
	lconf.AllowErrors = true

	// Ideally we would just return conf.Load() here, but go/types
	// reports certain "soft" errors that gc does not (Go issue 14596).
	// As a workaround, we set AllowErrors=true and then duplicate
	// the loader's error checking but allow soft errors.
	// It would be nice if the loader API permitted "AllowErrors: soft".
	prog, err := lconf.Load()
	if err != nil {
		return nil, err
	}
	var errpkgs []string
	// Report hard errors in indirectly imported packages.
	for _, info := range prog.AllPackages {
		if containsHardErrors(info.Errors) {
			errpkgs = append(errpkgs, info.Pkg.Path())
		} else {
			// Enable SSA construction for packages containing only soft errors.
			info.TransitivelyErrorFree = true
		}
	}
	if errpkgs != nil {
		var more string
		if len(errpkgs) > 3 {
			more = fmt.Sprintf(" and %d more", len(errpkgs)-3)
			errpkgs = errpkgs[:3]
		}
		return nil, fmt.Errorf("couldn't load packages due to errors: %s%s",
			strings.Join(errpkgs, ", "), more)
	}
	return prog, err
}

func containsHardErrors(errors []error) bool {
	for _, err := range errors {
		if err, ok := err.(types.Error); ok && err.Soft {
			continue
		}
		return true
	}
	return false
}

// allowErrors causes type errors to be silently ignored.
// (Not suitable if SSA construction follows.)
func allowErrors(lconf *loader.Config) {
	ctxt := *lconf.Build // copy
	ctxt.CgoEnabled = false
	lconf.Build = &ctxt
	lconf.AllowErrors = true
	// AllErrors makes the parser always return an AST instead of
	// bailing out after 10 errors and returning an empty ast.File.
	lconf.ParserMode = parser.AllErrors
	lconf.TypeChecker.Error = func(err error) {}
}

// ptrAnalysis runs the pointer analysis and returns its result.
func ptrAnalysis(conf *pointer.Config) *pointer.Result {
	result, err := pointer.Analyze(conf)
	if err != nil {
		panic(err) // pointer analysis internal error
	}
	return result
}

func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }

// deref returns a pointer's element type; otherwise it returns typ.
func deref(typ types.Type) types.Type {
	if p, ok := typ.Underlying().(*types.Pointer); ok {
		return p.Elem()
	}
	return typ
}

// fprintf prints to w a message of the form "location: message\n"
// where location is derived from pos.
//
// pos must be one of:
//    - a token.Pos, denoting a position
//    - an ast.Node, denoting an interval
//    - anything with a Pos() method:
//         ssa.Member, ssa.Value, ssa.Instruction, types.Object, pointer.Label, etc.
//    - a QueryPos, denoting the extent of the user's query.
//    - nil, meaning no position at all.
//
// The output format is is compatible with the 'gnu'
// compilation-error-regexp in Emacs' compilation mode.
//
func fprintf(w io.Writer, fset *token.FileSet, pos interface{}, format string, args ...interface{}) {
	var start, end token.Pos
	switch pos := pos.(type) {
	case ast.Node:
		start = pos.Pos()
		end = pos.End()
	case token.Pos:
		start = pos
		end = start
	case *types.PkgName:
		// The Pos of most PkgName objects does not coincide with an identifier,
		// so we suppress the usual start+len(name) heuristic for types.Objects.
		start = pos.Pos()
		end = start
	case types.Object:
		start = pos.Pos()
		end = start + token.Pos(len(pos.Name())) // heuristic
	case interface {
		Pos() token.Pos
	}:
		start = pos.Pos()
		end = start
	case *queryPos:
		start = pos.start
		end = pos.end
	case nil:
		// no-op
	default:
		panic(fmt.Sprintf("invalid pos: %T", pos))
	}

	if sp := fset.Position(start); start == end {
		// (prints "-: " for token.NoPos)
		fmt.Fprintf(w, "%s: ", sp)
	} else {
		ep := fset.Position(end)
		// The -1 below is a concession to Emacs's broken use of
		// inclusive (not half-open) intervals.
		// Other editors may not want it.
		// TODO(adonovan): add an -editor=vim|emacs|acme|auto
		// flag; auto uses EMACS=t / VIM=... / etc env vars.
		fmt.Fprintf(w, "%s:%d.%d-%d.%d: ",
			sp.Filename, sp.Line, sp.Column, ep.Line, ep.Column-1)
	}
	fmt.Fprintf(w, format, args...)
	io.WriteString(w, "\n")
}

func toJSON(x interface{}) []byte {
	b, err := json.MarshalIndent(x, "", "\t")
	if err != nil {
		log.Fatalf("JSON error: %v", err)
	}
	return b
}
<commit_msg>x/tools/cmd/guru: remove unused method.<commit_after>// Copyright 2014 The Go 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 main

// TODO(adonovan): new queries
// - show all statements that may update the selected lvalue
//   (local, global, field, etc).
// - show all places where an object of type T is created
//   (&T{}, var t T, new(T), new(struct{array [3]T}), etc.

import (
	"encoding/json"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"log"
	"path/filepath"
	"strings"

	"golang.org/x/tools/go/ast/astutil"
	"golang.org/x/tools/go/buildutil"
	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/pointer"
	"golang.org/x/tools/go/ssa"
)

type printfFunc func(pos interface{}, format string, args ...interface{})

// A QueryResult is an item of output.  Each query produces a stream of
// query results, calling Query.Output for each one.
type QueryResult interface {
	// JSON returns the QueryResult in JSON form.
	JSON(fset *token.FileSet) []byte

	// PrintPlain prints the QueryResult in plain text form.
	// The implementation calls printfFunc to print each line of output.
	PrintPlain(printf printfFunc)
}

// A QueryPos represents the position provided as input to a query:
// a textual extent in the program's source code, the AST node it
// corresponds to, and the package to which it belongs.
// Instances are created by parseQueryPos.
type queryPos struct {
	fset       *token.FileSet
	start, end token.Pos           // source extent of query
	path       []ast.Node          // AST path from query node to root of ast.File
	exact      bool                // 2nd result of PathEnclosingInterval
	info       *loader.PackageInfo // type info for the queried package (nil for fastQueryPos)
}

// TypeString prints type T relative to the query position.
func (qpos *queryPos) typeString(T types.Type) string {
	return types.TypeString(T, types.RelativeTo(qpos.info.Pkg))
}

// ObjectString prints object obj relative to the query position.
func (qpos *queryPos) objectString(obj types.Object) string {
	return types.ObjectString(obj, types.RelativeTo(qpos.info.Pkg))
}

// A Query specifies a single guru query.
type Query struct {
	Pos   string         // query position
	Build *build.Context // package loading configuration

	// pointer analysis options
	Scope      []string  // main packages in (*loader.Config).FromArgs syntax
	PTALog     io.Writer // (optional) pointer-analysis log file
	Reflection bool      // model reflection soundly (currently slow).

	// result-printing function
	Output func(*token.FileSet, QueryResult)
}

// Run runs an guru query and populates its Fset and Result.
func Run(mode string, q *Query) error {
	switch mode {
	case "callees":
		return callees(q)
	case "callers":
		return callers(q)
	case "callstack":
		return callstack(q)
	case "peers":
		return peers(q)
	case "pointsto":
		return pointsto(q)
	case "whicherrs":
		return whicherrs(q)
	case "definition":
		return definition(q)
	case "describe":
		return describe(q)
	case "freevars":
		return freevars(q)
	case "implements":
		return implements(q)
	case "referrers":
		return referrers(q)
	case "what":
		return what(q)
	default:
		return fmt.Errorf("invalid mode: %q", mode)
	}
}

func setPTAScope(lconf *loader.Config, scope []string) error {
	pkgs := buildutil.ExpandPatterns(lconf.Build, scope)
	if len(pkgs) == 0 {
		return fmt.Errorf("no packages specified for pointer analysis scope")
	}
	// The value of each entry in pkgs is true,
	// giving ImportWithTests (not Import) semantics.
	lconf.ImportPkgs = pkgs
	return nil
}

// Create a pointer.Config whose scope is the initial packages of lprog
// and their dependencies.
func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflection bool) (*pointer.Config, error) {
	// For each initial package (specified on the command line),
	// if it has a main function, analyze that,
	// otherwise analyze its tests, if any.
	var mains []*ssa.Package
	for _, info := range lprog.InitialPackages() {
		p := prog.Package(info.Pkg)

		// Add package to the pointer analysis scope.
		if p.Pkg.Name() == "main" && p.Func("main") != nil {
			mains = append(mains, p)
		} else if main := prog.CreateTestMainPackage(p); main != nil {
			mains = append(mains, main)
		}
	}
	if mains == nil {
		return nil, fmt.Errorf("analysis scope has no main and no tests")
	}
	return &pointer.Config{
		Log:        ptaLog,
		Reflection: reflection,
		Mains:      mains,
	}, nil
}

// importQueryPackage finds the package P containing the
// query position and tells conf to import it.
// It returns the package's path.
func importQueryPackage(pos string, conf *loader.Config) (string, error) {
	fqpos, err := fastQueryPos(conf.Build, pos)
	if err != nil {
		return "", err // bad query
	}
	filename := fqpos.fset.File(fqpos.start).Name()

	_, importPath, err := guessImportPath(filename, conf.Build)
	if err != nil {
		// Can't find GOPATH dir.
		// Treat the query file as its own package.
		importPath = "command-line-arguments"
		conf.CreateFromFilenames(importPath, filename)
	} else {
		// Check that it's possible to load the queried package.
		// (e.g. guru tests contain different 'package' decls in same dir.)
		// Keep consistent with logic in loader/util.go!
		cfg2 := *conf.Build
		cfg2.CgoEnabled = false
		bp, err := cfg2.Import(importPath, "", 0)
		if err != nil {
			return "", err // no files for package
		}

		switch pkgContainsFile(bp, filename) {
		case 'T':
			conf.ImportWithTests(importPath)
		case 'X':
			conf.ImportWithTests(importPath)
			importPath += "_test" // for TypeCheckFuncBodies
		case 'G':
			conf.Import(importPath)
		default:
			// This happens for ad-hoc packages like
			// $GOROOT/src/net/http/triv.go.
			return "", fmt.Errorf("package %q doesn't contain file %s",
				importPath, filename)
		}
	}

	conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath }

	return importPath, nil
}

// pkgContainsFile reports whether file was among the packages Go
// files, Test files, eXternal test files, or not found.
func pkgContainsFile(bp *build.Package, filename string) byte {
	for i, files := range [][]string{bp.GoFiles, bp.TestGoFiles, bp.XTestGoFiles} {
		for _, file := range files {
			if sameFile(filepath.Join(bp.Dir, file), filename) {
				return "GTX"[i]
			}
		}
	}
	return 0 // not found
}

// ParseQueryPos parses the source query position pos and returns the
// AST node of the loaded program lprog that it identifies.
// If needExact, it must identify a single AST subtree;
// this is appropriate for queries that allow fairly arbitrary syntax,
// e.g. "describe".
//
func parseQueryPos(lprog *loader.Program, pos string, needExact bool) (*queryPos, error) {
	filename, startOffset, endOffset, err := parsePos(pos)
	if err != nil {
		return nil, err
	}

	// Find the named file among those in the loaded program.
	var file *token.File
	lprog.Fset.Iterate(func(f *token.File) bool {
		if sameFile(filename, f.Name()) {
			file = f
			return false // done
		}
		return true // continue
	})
	if file == nil {
		return nil, fmt.Errorf("file %s not found in loaded program", filename)
	}

	start, end, err := fileOffsetToPos(file, startOffset, endOffset)
	if err != nil {
		return nil, err
	}
	info, path, exact := lprog.PathEnclosingInterval(start, end)
	if path == nil {
		return nil, fmt.Errorf("no syntax here")
	}
	if needExact && !exact {
		return nil, fmt.Errorf("ambiguous selection within %s", astutil.NodeDescription(path[0]))
	}
	return &queryPos{lprog.Fset, start, end, path, exact, info}, nil
}

// ---------- Utilities ----------

// loadWithSoftErrors calls lconf.Load, suppressing "soft" errors.  (See Go issue 16530.)
// TODO(adonovan): Once the loader has an option to allow soft errors,
// replace calls to loadWithSoftErrors with loader calls with that parameter.
func loadWithSoftErrors(lconf *loader.Config) (*loader.Program, error) {
	lconf.AllowErrors = true

	// Ideally we would just return conf.Load() here, but go/types
	// reports certain "soft" errors that gc does not (Go issue 14596).
	// As a workaround, we set AllowErrors=true and then duplicate
	// the loader's error checking but allow soft errors.
	// It would be nice if the loader API permitted "AllowErrors: soft".
	prog, err := lconf.Load()
	if err != nil {
		return nil, err
	}
	var errpkgs []string
	// Report hard errors in indirectly imported packages.
	for _, info := range prog.AllPackages {
		if containsHardErrors(info.Errors) {
			errpkgs = append(errpkgs, info.Pkg.Path())
		} else {
			// Enable SSA construction for packages containing only soft errors.
			info.TransitivelyErrorFree = true
		}
	}
	if errpkgs != nil {
		var more string
		if len(errpkgs) > 3 {
			more = fmt.Sprintf(" and %d more", len(errpkgs)-3)
			errpkgs = errpkgs[:3]
		}
		return nil, fmt.Errorf("couldn't load packages due to errors: %s%s",
			strings.Join(errpkgs, ", "), more)
	}
	return prog, err
}

func containsHardErrors(errors []error) bool {
	for _, err := range errors {
		if err, ok := err.(types.Error); ok && err.Soft {
			continue
		}
		return true
	}
	return false
}

// allowErrors causes type errors to be silently ignored.
// (Not suitable if SSA construction follows.)
func allowErrors(lconf *loader.Config) {
	ctxt := *lconf.Build // copy
	ctxt.CgoEnabled = false
	lconf.Build = &ctxt
	lconf.AllowErrors = true
	// AllErrors makes the parser always return an AST instead of
	// bailing out after 10 errors and returning an empty ast.File.
	lconf.ParserMode = parser.AllErrors
	lconf.TypeChecker.Error = func(err error) {}
}

// ptrAnalysis runs the pointer analysis and returns its result.
func ptrAnalysis(conf *pointer.Config) *pointer.Result {
	result, err := pointer.Analyze(conf)
	if err != nil {
		panic(err) // pointer analysis internal error
	}
	return result
}

func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }

// deref returns a pointer's element type; otherwise it returns typ.
func deref(typ types.Type) types.Type {
	if p, ok := typ.Underlying().(*types.Pointer); ok {
		return p.Elem()
	}
	return typ
}

// fprintf prints to w a message of the form "location: message\n"
// where location is derived from pos.
//
// pos must be one of:
//    - a token.Pos, denoting a position
//    - an ast.Node, denoting an interval
//    - anything with a Pos() method:
//         ssa.Member, ssa.Value, ssa.Instruction, types.Object, pointer.Label, etc.
//    - a QueryPos, denoting the extent of the user's query.
//    - nil, meaning no position at all.
//
// The output format is is compatible with the 'gnu'
// compilation-error-regexp in Emacs' compilation mode.
//
func fprintf(w io.Writer, fset *token.FileSet, pos interface{}, format string, args ...interface{}) {
	var start, end token.Pos
	switch pos := pos.(type) {
	case ast.Node:
		start = pos.Pos()
		end = pos.End()
	case token.Pos:
		start = pos
		end = start
	case *types.PkgName:
		// The Pos of most PkgName objects does not coincide with an identifier,
		// so we suppress the usual start+len(name) heuristic for types.Objects.
		start = pos.Pos()
		end = start
	case types.Object:
		start = pos.Pos()
		end = start + token.Pos(len(pos.Name())) // heuristic
	case interface {
		Pos() token.Pos
	}:
		start = pos.Pos()
		end = start
	case *queryPos:
		start = pos.start
		end = pos.end
	case nil:
		// no-op
	default:
		panic(fmt.Sprintf("invalid pos: %T", pos))
	}

	if sp := fset.Position(start); start == end {
		// (prints "-: " for token.NoPos)
		fmt.Fprintf(w, "%s: ", sp)
	} else {
		ep := fset.Position(end)
		// The -1 below is a concession to Emacs's broken use of
		// inclusive (not half-open) intervals.
		// Other editors may not want it.
		// TODO(adonovan): add an -editor=vim|emacs|acme|auto
		// flag; auto uses EMACS=t / VIM=... / etc env vars.
		fmt.Fprintf(w, "%s:%d.%d-%d.%d: ",
			sp.Filename, sp.Line, sp.Column, ep.Line, ep.Column-1)
	}
	fmt.Fprintf(w, format, args...)
	io.WriteString(w, "\n")
}

func toJSON(x interface{}) []byte {
	b, err := json.MarshalIndent(x, "", "\t")
	if err != nil {
		log.Fatalf("JSON error: %v", err)
	}
	return b
}
<|endoftext|>