<commit_before>// Copyright 2022 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.

package planner

import (
	"context"
	"fmt"
	"reflect"

	planv1alpha1 "github.com/GoogleContainerTools/kpt/pkg/api/plan/v1alpha1"
	"github.com/GoogleContainerTools/kpt/pkg/live"
	"github.com/GoogleContainerTools/kpt/pkg/status"
	"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/apply"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/client-go/dynamic"
	"k8s.io/kubectl/pkg/cmd/util"
	"sigs.k8s.io/cli-utils/pkg/apply/event"
	"sigs.k8s.io/cli-utils/pkg/common"
	"sigs.k8s.io/cli-utils/pkg/inventory"
	"sigs.k8s.io/cli-utils/pkg/object"
)

func NewClusterPlanner(f util.Factory) (*ClusterPlanner, error) {
	fetcher, err := NewResourceFetcher(f)
	if err != nil {
		return nil, err
	}

	invClient, err := inventory.NewClient(f, live.WrapInventoryObj, live.InvToUnstructuredFunc, inventory.StatusPolicyNone)
	if err != nil {
		return nil, err
	}

	statusPoller, err := status.NewStatusPoller(f)
	if err != nil {
		return nil, err
	}

	applier, err := apply.NewApplierBuilder().
		WithFactory(f).
		WithInventoryClient(invClient).
		WithStatusPoller(statusPoller).
		Build()
	if err != nil {
		return nil, err
	}

	return &ClusterPlanner{
		applier:         applier,
		resourceFetcher: fetcher,
	}, nil
}

type Applier interface {
	Run(ctx context.Context, invInfo inventory.Info, objects object.UnstructuredSet, options apply.ApplierOptions) <-chan event.Event
}

type ResourceFetcher interface {
	FetchResource(ctx context.Context, id object.ObjMetadata) (*unstructured.Unstructured, bool, error)
}

type ClusterPlanner struct {
	applier         Applier
	resourceFetcher ResourceFetcher
}

type Options struct {
	ServerSideOptions common.ServerSideOptions
}

func (r *ClusterPlanner) BuildPlan(ctx context.Context, inv inventory.Info, objects []*unstructured.Unstructured, o Options) (*planv1alpha1.Plan, error) {
	actions, err := r.dryRunForPlan(ctx, inv, objects, o)
	if err != nil {
		return nil, err
	}
	return &planv1alpha1.Plan{
		ResourceMeta: planv1alpha1.ResourceMeta,
		Spec: planv1alpha1.PlanSpec{
			Actions: actions,
		},
	}, nil
}

func (r *ClusterPlanner) dryRunForPlan(ctx context.Context, inv inventory.Info,
	objects []*unstructured.Unstructured, o Options) ([]planv1alpha1.Action, error) {

	eventCh := r.applier.Run(ctx, inv, objects, apply.ApplierOptions{
		DryRunStrategy:    common.DryRunServer,
		ServerSideOptions: o.ServerSideOptions,
	})

	var actions []planv1alpha1.Action
	var err error
	for e := range eventCh {
		if e.Type == event.InitType {
			// This event includes all resources that will be applied, pruned or deleted, so
			// we make sure we fetch all the resources from the cluster.
			// TODO: See if we can update the actuation library to provide the pre-actuation
			// versions of the resources as part of the regular run. This solution is not great
			// as fetching all resources will take time.
			a, err := r.fetchResources(ctx, e)
			if err != nil {
				return nil, err
			}
			actions = a
		}
		if e.Type == event.ErrorType {
			// Update the err variable here, but wait for the channel to close
			// before we return from the function.
			// Since ErrorEvents are considered fatal, there should only be sent
			// and it will be followed by the channel being closed.
			err = e.ErrorEvent.Err
		}
		// For the Apply, Prune and Delete event types, we just capture the result
		// of the dry-run operation for the specific resource.
		switch e.Type {
		case event.ApplyType:
			id := e.ApplyEvent.Identifier
			index := indexForIdentifier(id, actions)
			a := actions[index]
			actions[index] = handleApplyEvent(e, a)
		case event.PruneType:
			id := e.PruneEvent.Identifier
			index := indexForIdentifier(id, actions)
			a := actions[index]
			actions[index] = handlePruneEvent(e, a)
		// Prune and Delete are essentially the same thing, but the actuation
		// library return Prune events when resources are deleted by omission
		// during apply, and Delete events from the destroyer. Supporting both
		// here for completeness.
		case event.DeleteType:
			id := e.DeleteEvent.Identifier
			index := indexForIdentifier(id, actions)
			a := actions[index]
			actions[index] = handleDeleteEvent(e, a)
		}
	}
	return actions, err
}

func handleApplyEvent(e event.Event, a planv1alpha1.Action) planv1alpha1.Action {
	if e.ApplyEvent.Error != nil {
		a.Type = planv1alpha1.Error
		a.Error = e.ApplyEvent.Error.Error()
	} else {
		switch e.ApplyEvent.Operation {
		case event.Unchanged:
			a.Type = planv1alpha1.Skip
		case event.ServersideApplied:
			a.After = e.ApplyEvent.Resource
			if a.Before != nil {
				// TODO: Unclear if we should diff the full resources here. It doesn't work
				// well with client-side apply as the managedFields property shows up as
				// changes. It also means there is a race with controllers that might change
				// the status of resources.
				if reflect.DeepEqual(a.Before, a.After) {
					a.Type = planv1alpha1.Unchanged
				} else {
					a.Type = planv1alpha1.Update
				}
			} else {
				a.Type = planv1alpha1.Create
			}
		}
	}
	return a
}

func handlePruneEvent(e event.Event, a planv1alpha1.Action) planv1alpha1.Action {
	if e.PruneEvent.Error != nil {
		a.Type = planv1alpha1.Error
		a.Error = e.PruneEvent.Error.Error()
	} else {
		switch e.PruneEvent.Operation {
		case event.Pruned:
			a.Type = planv1alpha1.Delete
		// Lifecycle directives can cause resources to remain in the
		// live state even if they would normally be pruned.
		// TODO: Handle reason for skipped resources that has recently
		// been added to the actuation library.
		case event.PruneSkipped:
			a.Type = planv1alpha1.Skip
		}
	}
	return a
}

func handleDeleteEvent(e event.Event, a planv1alpha1.Action) planv1alpha1.Action {
	if e.DeleteEvent.Error != nil {
		a.Type = planv1alpha1.Error
		a.Error = e.DeleteEvent.Error.Error()
	} else {
		switch e.DeleteEvent.Operation {
		case event.Deleted:
			a.Type = planv1alpha1.Delete
		case event.DeleteSkipped:
			a.Type = planv1alpha1.Skip
		}
	}
	return a
}

func (r *ClusterPlanner) fetchResources(ctx context.Context, e event.Event) ([]planv1alpha1.Action, error) {
	var actions []planv1alpha1.Action
	for _, ag := range e.InitEvent.ActionGroups {
		// We only care about the Apply, Prune and Delete actions.
		if !(ag.Action == event.ApplyAction || ag.Action == event.PruneAction || ag.Action == event.DeleteAction) {
			continue
		}
		for _, id := range ag.Identifiers {
			u, _, err := r.resourceFetcher.FetchResource(ctx, id)
			// If the type doesn't exist in the cluster, then the resource itself doesn't exist.
			if err != nil && !meta.IsNoMatchError(err) {
				return nil, err
			}
			actions = append(actions, planv1alpha1.Action{
				Group:     id.GroupKind.Group,
				Kind:      id.GroupKind.Kind,
				Name:      id.Name,
				Namespace: id.Namespace,
				Before:    u,
			})
		}
	}
	return actions, nil
}

type resourceFetcher struct {
	dynamicClient dynamic.Interface
	mapper        meta.RESTMapper
}

func NewResourceFetcher(f util.Factory) (ResourceFetcher, error) {
	dc, err := f.DynamicClient()
	if err != nil {
		return nil, err
	}

	mapper, err := f.ToRESTMapper()
	if err != nil {
		return nil, err
	}
	return &resourceFetcher{
		dynamicClient: dc,
		mapper:        mapper,
	}, nil
}

func (rf *resourceFetcher) FetchResource(ctx context.Context, id object.ObjMetadata) (*unstructured.Unstructured, bool, error) {
	mapping, err := rf.mapper.RESTMapping(id.GroupKind)
	if err != nil {
		return nil, false, err
	}
	var r dynamic.ResourceInterface
	if mapping.Scope == meta.RESTScopeRoot {
		r = rf.dynamicClient.Resource(mapping.Resource)
	} else {
		r = rf.dynamicClient.Resource(mapping.Resource).Namespace(id.Namespace)
	}
	u, err := r.Get(ctx, id.Name, metav1.GetOptions{})
	if err != nil && !apierrors.IsNotFound(err) {
		return nil, false, err
	}

	if apierrors.IsNotFound(err) {
		return nil, false, nil
	}
	return u, true, nil
}

func indexForIdentifier(id object.ObjMetadata, actions []planv1alpha1.Action) int {
	for i := range actions {
		a := actions[i]
		if a.Group == id.GroupKind.Group &&
			a.Kind == id.GroupKind.Kind &&
			a.Name == id.Name &&
			a.Namespace == id.Namespace {
			return i
		}
	}
	panic(fmt.Errorf("unknown identifier %s", id.String()))
}
<commit_msg>chore: fix lint whitespace failure (#3171)<commit_after>// Copyright 2022 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.

package planner

import (
	"context"
	"fmt"
	"reflect"

	planv1alpha1 "github.com/GoogleContainerTools/kpt/pkg/api/plan/v1alpha1"
	"github.com/GoogleContainerTools/kpt/pkg/live"
	"github.com/GoogleContainerTools/kpt/pkg/status"
	"github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/apply"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/client-go/dynamic"
	"k8s.io/kubectl/pkg/cmd/util"
	"sigs.k8s.io/cli-utils/pkg/apply/event"
	"sigs.k8s.io/cli-utils/pkg/common"
	"sigs.k8s.io/cli-utils/pkg/inventory"
	"sigs.k8s.io/cli-utils/pkg/object"
)

func NewClusterPlanner(f util.Factory) (*ClusterPlanner, error) {
	fetcher, err := NewResourceFetcher(f)
	if err != nil {
		return nil, err
	}

	invClient, err := inventory.NewClient(f, live.WrapInventoryObj, live.InvToUnstructuredFunc, inventory.StatusPolicyNone)
	if err != nil {
		return nil, err
	}

	statusPoller, err := status.NewStatusPoller(f)
	if err != nil {
		return nil, err
	}

	applier, err := apply.NewApplierBuilder().
		WithFactory(f).
		WithInventoryClient(invClient).
		WithStatusPoller(statusPoller).
		Build()
	if err != nil {
		return nil, err
	}

	return &ClusterPlanner{
		applier:         applier,
		resourceFetcher: fetcher,
	}, nil
}

type Applier interface {
	Run(ctx context.Context, invInfo inventory.Info, objects object.UnstructuredSet, options apply.ApplierOptions) <-chan event.Event
}

type ResourceFetcher interface {
	FetchResource(ctx context.Context, id object.ObjMetadata) (*unstructured.Unstructured, bool, error)
}

type ClusterPlanner struct {
	applier         Applier
	resourceFetcher ResourceFetcher
}

type Options struct {
	ServerSideOptions common.ServerSideOptions
}

func (r *ClusterPlanner) BuildPlan(ctx context.Context, inv inventory.Info, objects []*unstructured.Unstructured, o Options) (*planv1alpha1.Plan, error) {
	actions, err := r.dryRunForPlan(ctx, inv, objects, o)
	if err != nil {
		return nil, err
	}
	return &planv1alpha1.Plan{
		ResourceMeta: planv1alpha1.ResourceMeta,
		Spec: planv1alpha1.PlanSpec{
			Actions: actions,
		},
	}, nil
}

func (r *ClusterPlanner) dryRunForPlan(
	ctx context.Context,
	inv inventory.Info,
	objects []*unstructured.Unstructured,
	o Options,
) ([]planv1alpha1.Action, error) {
	eventCh := r.applier.Run(ctx, inv, objects, apply.ApplierOptions{
		DryRunStrategy:    common.DryRunServer,
		ServerSideOptions: o.ServerSideOptions,
	})

	var actions []planv1alpha1.Action
	var err error
	for e := range eventCh {
		if e.Type == event.InitType {
			// This event includes all resources that will be applied, pruned or deleted, so
			// we make sure we fetch all the resources from the cluster.
			// TODO: See if we can update the actuation library to provide the pre-actuation
			// versions of the resources as part of the regular run. This solution is not great
			// as fetching all resources will take time.
			a, err := r.fetchResources(ctx, e)
			if err != nil {
				return nil, err
			}
			actions = a
		}
		if e.Type == event.ErrorType {
			// Update the err variable here, but wait for the channel to close
			// before we return from the function.
			// Since ErrorEvents are considered fatal, there should only be sent
			// and it will be followed by the channel being closed.
			err = e.ErrorEvent.Err
		}
		// For the Apply, Prune and Delete event types, we just capture the result
		// of the dry-run operation for the specific resource.
		switch e.Type {
		case event.ApplyType:
			id := e.ApplyEvent.Identifier
			index := indexForIdentifier(id, actions)
			a := actions[index]
			actions[index] = handleApplyEvent(e, a)
		case event.PruneType:
			id := e.PruneEvent.Identifier
			index := indexForIdentifier(id, actions)
			a := actions[index]
			actions[index] = handlePruneEvent(e, a)
		// Prune and Delete are essentially the same thing, but the actuation
		// library return Prune events when resources are deleted by omission
		// during apply, and Delete events from the destroyer. Supporting both
		// here for completeness.
		case event.DeleteType:
			id := e.DeleteEvent.Identifier
			index := indexForIdentifier(id, actions)
			a := actions[index]
			actions[index] = handleDeleteEvent(e, a)
		}
	}
	return actions, err
}

func handleApplyEvent(e event.Event, a planv1alpha1.Action) planv1alpha1.Action {
	if e.ApplyEvent.Error != nil {
		a.Type = planv1alpha1.Error
		a.Error = e.ApplyEvent.Error.Error()
	} else {
		switch e.ApplyEvent.Operation {
		case event.Unchanged:
			a.Type = planv1alpha1.Skip
		case event.ServersideApplied:
			a.After = e.ApplyEvent.Resource
			if a.Before != nil {
				// TODO: Unclear if we should diff the full resources here. It doesn't work
				// well with client-side apply as the managedFields property shows up as
				// changes. It also means there is a race with controllers that might change
				// the status of resources.
				if reflect.DeepEqual(a.Before, a.After) {
					a.Type = planv1alpha1.Unchanged
				} else {
					a.Type = planv1alpha1.Update
				}
			} else {
				a.Type = planv1alpha1.Create
			}
		}
	}
	return a
}

func handlePruneEvent(e event.Event, a planv1alpha1.Action) planv1alpha1.Action {
	if e.PruneEvent.Error != nil {
		a.Type = planv1alpha1.Error
		a.Error = e.PruneEvent.Error.Error()
	} else {
		switch e.PruneEvent.Operation {
		case event.Pruned:
			a.Type = planv1alpha1.Delete
		// Lifecycle directives can cause resources to remain in the
		// live state even if they would normally be pruned.
		// TODO: Handle reason for skipped resources that has recently
		// been added to the actuation library.
		case event.PruneSkipped:
			a.Type = planv1alpha1.Skip
		}
	}
	return a
}

func handleDeleteEvent(e event.Event, a planv1alpha1.Action) planv1alpha1.Action {
	if e.DeleteEvent.Error != nil {
		a.Type = planv1alpha1.Error
		a.Error = e.DeleteEvent.Error.Error()
	} else {
		switch e.DeleteEvent.Operation {
		case event.Deleted:
			a.Type = planv1alpha1.Delete
		case event.DeleteSkipped:
			a.Type = planv1alpha1.Skip
		}
	}
	return a
}

func (r *ClusterPlanner) fetchResources(ctx context.Context, e event.Event) ([]planv1alpha1.Action, error) {
	var actions []planv1alpha1.Action
	for _, ag := range e.InitEvent.ActionGroups {
		// We only care about the Apply, Prune and Delete actions.
		if !(ag.Action == event.ApplyAction || ag.Action == event.PruneAction || ag.Action == event.DeleteAction) {
			continue
		}
		for _, id := range ag.Identifiers {
			u, _, err := r.resourceFetcher.FetchResource(ctx, id)
			// If the type doesn't exist in the cluster, then the resource itself doesn't exist.
			if err != nil && !meta.IsNoMatchError(err) {
				return nil, err
			}
			actions = append(actions, planv1alpha1.Action{
				Group:     id.GroupKind.Group,
				Kind:      id.GroupKind.Kind,
				Name:      id.Name,
				Namespace: id.Namespace,
				Before:    u,
			})
		}
	}
	return actions, nil
}

type resourceFetcher struct {
	dynamicClient dynamic.Interface
	mapper        meta.RESTMapper
}

func NewResourceFetcher(f util.Factory) (ResourceFetcher, error) {
	dc, err := f.DynamicClient()
	if err != nil {
		return nil, err
	}

	mapper, err := f.ToRESTMapper()
	if err != nil {
		return nil, err
	}
	return &resourceFetcher{
		dynamicClient: dc,
		mapper:        mapper,
	}, nil
}

func (rf *resourceFetcher) FetchResource(ctx context.Context, id object.ObjMetadata) (*unstructured.Unstructured, bool, error) {
	mapping, err := rf.mapper.RESTMapping(id.GroupKind)
	if err != nil {
		return nil, false, err
	}
	var r dynamic.ResourceInterface
	if mapping.Scope == meta.RESTScopeRoot {
		r = rf.dynamicClient.Resource(mapping.Resource)
	} else {
		r = rf.dynamicClient.Resource(mapping.Resource).Namespace(id.Namespace)
	}
	u, err := r.Get(ctx, id.Name, metav1.GetOptions{})
	if err != nil && !apierrors.IsNotFound(err) {
		return nil, false, err
	}

	if apierrors.IsNotFound(err) {
		return nil, false, nil
	}
	return u, true, nil
}

func indexForIdentifier(id object.ObjMetadata, actions []planv1alpha1.Action) int {
	for i := range actions {
		a := actions[i]
		if a.Group == id.GroupKind.Group &&
			a.Kind == id.GroupKind.Kind &&
			a.Name == id.Name &&
			a.Namespace == id.Namespace {
			return i
		}
	}
	panic(fmt.Errorf("unknown identifier %s", id.String()))
}
<|endoftext|>


<commit_before>// Copyright 2017 zebra project
//
// 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 (
	"fmt"

	"github.com/coreswitch/component"
	"github.com/coreswitch/zebra/pkg/packet/bgp"
	"github.com/coreswitch/zebra/pkg/server/module"
)

func main() {
	fmt.Println("Starting bgpd")
	module.Init()

	server := bgp.NewServer(65100)

	serverComponent := &bgp.ServerComponent{
		Server: server,
	}
	grpcComponent := &bgp.GrpcComponent{
		Server: server,
	}

	systemMap := component.SystemMap{
		"server": serverComponent,
		"grpc":   component.ComponentWith(grpcComponent, "server"),
	}
	systemMap.Start()

	err := server.RouterIdSet("192.168.55.1")
	if err != nil {
		fmt.Println(err)
	}

	err = server.NeighborAdd("192.168.55.2")
	if err != nil {
		fmt.Println(err)
	}
	err = server.NeighborRemoteAsSet("192.168.55.2", 65520)
	if err != nil {
		fmt.Println(err)
	}
	err = server.NeighborAfiSafiSet("192.168.55.2", bgp.AFI_IP, bgp.SAFI_UNICAST)
	if err != nil {
		fmt.Println(err)
	}
	err = server.NeighborAfiSafiSet("192.168.55.2", bgp.AFI_IP6, bgp.SAFI_UNICAST)
	if err != nil {
		fmt.Println(err)
	}

	ch := make(chan struct{})
	<-ch
}
<commit_msg>Remove old bgpd's main.<commit_after><|endoftext|>


<commit_before>// Copyright 2018 Google Inc.
//
// 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 gofer

import (
	"fmt"
	"syscall"

	"gvisor.googlesource.com/gvisor/pkg/p9"
	"gvisor.googlesource.com/gvisor/pkg/sentry/context"
	"gvisor.googlesource.com/gvisor/pkg/sentry/device"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
	"gvisor.googlesource.com/gvisor/pkg/syserror"
	"gvisor.googlesource.com/gvisor/pkg/tcpip/transport/unix"
)

// Lookup loads an Inode at name into a Dirent based on the session's cache
// policy.
func (i *inodeOperations) Lookup(ctx context.Context, dir *fs.Inode, name string) (*fs.Dirent, error) {
	if i.session().cachePolicy != cacheNone {
		// Check to see if we have readdirCache that indicates the
		// child does not exist.  Avoid holding readdirMu longer than
		// we need to.
		i.readdirMu.Lock()
		if i.readdirCache != nil && !i.readdirCache.Contains(name) {
			// No such child.  Return a negative dirent.
			i.readdirMu.Unlock()
			return fs.NewNegativeDirent(name), nil
		}
		i.readdirMu.Unlock()
	}

	// Get a p9.File for name.
	qids, newFile, mask, p9attr, err := i.fileState.file.walkGetAttr(ctx, []string{name})
	if err != nil {
		if err == syscall.ENOENT {
			if i.session().cachePolicy != cacheNone {
				// Return a negative Dirent. It will stay cached until something
				// is created over it.
				return fs.NewNegativeDirent(name), nil
			}
			return nil, syserror.ENOENT
		}
		return nil, err
	}

	// Construct the Inode operations.
	sattr, node := newInodeOperations(ctx, i.fileState.s, newFile, qids[0], mask, p9attr)

	// Construct a positive Dirent.
	return fs.NewDirent(fs.NewInode(node, dir.MountSource, sattr), name), nil
}

// Creates a new Inode at name and returns its File based on the session's cache policy.
//
// Ownership is currently ignored.
func (i *inodeOperations) Create(ctx context.Context, dir *fs.Inode, name string, flags fs.FileFlags, perm fs.FilePermissions) (*fs.File, error) {
	// Create replaces the directory fid with the newly created/opened
	// file, so clone this directory so it doesn't change out from under
	// this node.
	_, newFile, err := i.fileState.file.walk(ctx, nil)
	if err != nil {
		return nil, err
	}

	// Map the FileFlags to p9 OpenFlags.
	var openFlags p9.OpenFlags
	switch {
	case flags.Read && flags.Write:
		openFlags = p9.ReadWrite
	case flags.Read:
		openFlags = p9.ReadOnly
	case flags.Write:
		openFlags = p9.WriteOnly
	default:
		panic(fmt.Sprintf("Create called with unknown or unset open flags: %v", flags))
	}

	owner := fs.FileOwnerFromContext(ctx)
	hostFile, err := newFile.create(ctx, name, openFlags, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID))
	if err != nil {
		// Could not create the file.
		return nil, err
	}

	i.touchModificationTime(ctx)

	// Get the attributes of the file.
	qid, mask, p9attr, err := getattr(ctx, newFile)
	if err != nil {
		newFile.close(ctx)
		return nil, err
	}

	// Get an unopened p9.File for the file we created so that it can be
	// cloned and re-opened multiple times after creation.
	_, unopened, err := i.fileState.file.walk(ctx, []string{name})
	if err != nil {
		newFile.close(ctx)
		return nil, err
	}

	// Construct the InodeOperations.
	sattr, iops := newInodeOperations(ctx, i.fileState.s, unopened, qid, mask, p9attr)

	// Construct the positive Dirent.
	d := fs.NewDirent(fs.NewInode(iops, dir.MountSource, sattr), name)
	defer d.DecRef()

	// Construct the new file, caching the handles if allowed.
	h := &handles{
		File: newFile,
		Host: hostFile,
	}
	if isFileCachable(iops.session(), d.Inode) {
		iops.fileState.setHandlesForCachedIO(flags, h)
	}
	return NewFile(ctx, d, flags, iops, h), nil
}

// CreateLink uses Create to create a symlink between oldname and newname.
func (i *inodeOperations) CreateLink(ctx context.Context, dir *fs.Inode, oldname string, newname string) error {
	owner := fs.FileOwnerFromContext(ctx)
	if _, err := i.fileState.file.symlink(ctx, oldname, newname, p9.UID(owner.UID), p9.GID(owner.GID)); err != nil {
		return err
	}
	i.touchModificationTime(ctx)
	return nil
}

// CreateHardLink implements InodeOperations.CreateHardLink.
func (i *inodeOperations) CreateHardLink(ctx context.Context, _ *fs.Inode, target *fs.Inode, newName string) error {
	targetOpts, ok := target.InodeOperations.(*inodeOperations)
	if !ok {
		return syscall.EXDEV
	}

	if err := i.fileState.file.link(ctx, &targetOpts.fileState.file, newName); err != nil {
		return err
	}
	// TODO: Don't increase link count because we can't properly accounts for links
	// with gofers.
	i.touchModificationTime(ctx)
	return nil
}

// CreateDirectory uses Create to create a directory named s under inodeOperations.
func (i *inodeOperations) CreateDirectory(ctx context.Context, dir *fs.Inode, s string, perm fs.FilePermissions) error {
	owner := fs.FileOwnerFromContext(ctx)
	if _, err := i.fileState.file.mkdir(ctx, s, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID)); err != nil {
		return err
	}
	if i.session().cachePolicy == cacheAll {
		// Increase link count.
		i.cachingInodeOps.IncLinks(ctx)

		// Invalidate readdir cache.
		i.markDirectoryDirty()
	}
	return nil
}

// Bind implements InodeOperations.
func (i *inodeOperations) Bind(ctx context.Context, dir *fs.Inode, name string, ep unix.BoundEndpoint, perm fs.FilePermissions) error {
	if i.session().endpoints == nil {
		return syscall.EOPNOTSUPP
	}

	// Create replaces the directory fid with the newly created/opened
	// file, so clone this directory so it doesn't change out from under
	// this node.
	_, newFile, err := i.fileState.file.walk(ctx, nil)
	if err != nil {
		return err
	}

	// Stabilize the endpoint map while creation is in progress.
	unlock := i.session().endpoints.lock()
	defer unlock()

	// Create a regular file in the gofer and then mark it as a socket by
	// adding this inode key in the 'endpoints' map.
	owner := fs.FileOwnerFromContext(ctx)
	hostFile, err := newFile.create(ctx, name, p9.ReadWrite, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID))
	if err != nil {
		return err
	}
	// We're not going to use this file.
	hostFile.Close()

	i.touchModificationTime(ctx)

	// Get the attributes of the file to create inode key.
	qid, _, attr, err := getattr(ctx, newFile)
	if err != nil {
		newFile.close(ctx)
		return err
	}

	key := device.MultiDeviceKey{
		Device:          attr.RDev,
		SecondaryDevice: i.session().connID,
		Inode:           qid.Path,
	}
	i.session().endpoints.add(key, ep)

	return nil
}

// CreateFifo implements fs.InodeOperations.CreateFifo. Gofer nodes do not support the
// creation of fifos and always returns EOPNOTSUPP.
func (*inodeOperations) CreateFifo(context.Context, *fs.Inode, string, fs.FilePermissions) error {
	return syscall.EOPNOTSUPP
}

// Remove implements InodeOperations.Remove.
func (i *inodeOperations) Remove(ctx context.Context, dir *fs.Inode, name string) error {
	var key device.MultiDeviceKey
	removeSocket := false
	if i.session().endpoints != nil {
		// Find out if file being deleted is a socket that needs to be
		// removed from endpoint map.
		if d, err := i.Lookup(ctx, dir, name); err == nil {
			defer d.DecRef()
			if fs.IsSocket(d.Inode.StableAttr) {
				child := d.Inode.InodeOperations.(*inodeOperations)
				key = child.fileState.key
				removeSocket = true

				// Stabilize the endpoint map while deletion is in progress.
				unlock := i.session().endpoints.lock()
				defer unlock()
			}
		}
	}

	if err := i.fileState.file.unlinkAt(ctx, name, 0); err != nil {
		return err
	}
	if removeSocket {
		i.session().endpoints.remove(key)
	}
	i.touchModificationTime(ctx)

	return nil
}

// Remove implements InodeOperations.RemoveDirectory.
func (i *inodeOperations) RemoveDirectory(ctx context.Context, dir *fs.Inode, name string) error {
	// 0x200 = AT_REMOVEDIR.
	if err := i.fileState.file.unlinkAt(ctx, name, 0x200); err != nil {
		return err
	}
	if i.session().cachePolicy == cacheAll {
		// Decrease link count and updates atime.
		i.cachingInodeOps.DecLinks(ctx)

		// Invalidate readdir cache.
		i.markDirectoryDirty()
	}
	return nil
}

// Rename renames this node.
func (i *inodeOperations) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
	// Unwrap the new parent to a *inodeOperations.
	newParentInodeOperations, ok := newParent.InodeOperations.(*inodeOperations)
	if !ok {
		return syscall.EXDEV
	}

	// Unwrap the old parent to a *inodeOperations.
	oldParentInodeOperations, ok := oldParent.InodeOperations.(*inodeOperations)
	if !ok {
		return syscall.EXDEV
	}

	// Do the rename.
	if err := i.fileState.file.rename(ctx, newParentInodeOperations.fileState.file, newName); err != nil {
		return err
	}

	// Update cached state.
	if i.session().cachePolicy == cacheAll {
		// Is the renamed entity a directory? Fix link counts.
		if fs.IsDir(i.fileState.sattr) {
			oldParentInodeOperations.cachingInodeOps.DecLinks(ctx)
			newParentInodeOperations.cachingInodeOps.IncLinks(ctx)
		}

		// Mark old directory dirty.
		oldParentInodeOperations.markDirectoryDirty()
		if oldParent != newParent {
			// Mark new directory dirty.
			newParentInodeOperations.markDirectoryDirty()
		}
	}
	return nil
}

func (i *inodeOperations) touchModificationTime(ctx context.Context) {
	if i.session().cachePolicy == cacheAll {
		i.cachingInodeOps.TouchModificationTime(ctx)

		// Invalidate readdir cache.
		i.markDirectoryDirty()
	}
}

// markDirectoryDirty marks any cached data dirty for this directory. This is necessary in order
// to ensure that this node does not retain stale state throughout its lifetime across multiple
// open directory handles.
//
// Currently this means invalidating any readdir caches.
func (i *inodeOperations) markDirectoryDirty() {
	i.readdirMu.Lock()
	defer i.readdirMu.Unlock()
	i.readdirCache = nil
}
<commit_msg>Increment link count in CreateHardlink<commit_after>// Copyright 2018 Google Inc.
//
// 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 gofer

import (
	"fmt"
	"syscall"

	"gvisor.googlesource.com/gvisor/pkg/p9"
	"gvisor.googlesource.com/gvisor/pkg/sentry/context"
	"gvisor.googlesource.com/gvisor/pkg/sentry/device"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
	"gvisor.googlesource.com/gvisor/pkg/syserror"
	"gvisor.googlesource.com/gvisor/pkg/tcpip/transport/unix"
)

// Lookup loads an Inode at name into a Dirent based on the session's cache
// policy.
func (i *inodeOperations) Lookup(ctx context.Context, dir *fs.Inode, name string) (*fs.Dirent, error) {
	if i.session().cachePolicy != cacheNone {
		// Check to see if we have readdirCache that indicates the
		// child does not exist.  Avoid holding readdirMu longer than
		// we need to.
		i.readdirMu.Lock()
		if i.readdirCache != nil && !i.readdirCache.Contains(name) {
			// No such child.  Return a negative dirent.
			i.readdirMu.Unlock()
			return fs.NewNegativeDirent(name), nil
		}
		i.readdirMu.Unlock()
	}

	// Get a p9.File for name.
	qids, newFile, mask, p9attr, err := i.fileState.file.walkGetAttr(ctx, []string{name})
	if err != nil {
		if err == syscall.ENOENT {
			if i.session().cachePolicy != cacheNone {
				// Return a negative Dirent. It will stay cached until something
				// is created over it.
				return fs.NewNegativeDirent(name), nil
			}
			return nil, syserror.ENOENT
		}
		return nil, err
	}

	// Construct the Inode operations.
	sattr, node := newInodeOperations(ctx, i.fileState.s, newFile, qids[0], mask, p9attr)

	// Construct a positive Dirent.
	return fs.NewDirent(fs.NewInode(node, dir.MountSource, sattr), name), nil
}

// Creates a new Inode at name and returns its File based on the session's cache policy.
//
// Ownership is currently ignored.
func (i *inodeOperations) Create(ctx context.Context, dir *fs.Inode, name string, flags fs.FileFlags, perm fs.FilePermissions) (*fs.File, error) {
	// Create replaces the directory fid with the newly created/opened
	// file, so clone this directory so it doesn't change out from under
	// this node.
	_, newFile, err := i.fileState.file.walk(ctx, nil)
	if err != nil {
		return nil, err
	}

	// Map the FileFlags to p9 OpenFlags.
	var openFlags p9.OpenFlags
	switch {
	case flags.Read && flags.Write:
		openFlags = p9.ReadWrite
	case flags.Read:
		openFlags = p9.ReadOnly
	case flags.Write:
		openFlags = p9.WriteOnly
	default:
		panic(fmt.Sprintf("Create called with unknown or unset open flags: %v", flags))
	}

	owner := fs.FileOwnerFromContext(ctx)
	hostFile, err := newFile.create(ctx, name, openFlags, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID))
	if err != nil {
		// Could not create the file.
		return nil, err
	}

	i.touchModificationTime(ctx)

	// Get the attributes of the file.
	qid, mask, p9attr, err := getattr(ctx, newFile)
	if err != nil {
		newFile.close(ctx)
		return nil, err
	}

	// Get an unopened p9.File for the file we created so that it can be
	// cloned and re-opened multiple times after creation.
	_, unopened, err := i.fileState.file.walk(ctx, []string{name})
	if err != nil {
		newFile.close(ctx)
		return nil, err
	}

	// Construct the InodeOperations.
	sattr, iops := newInodeOperations(ctx, i.fileState.s, unopened, qid, mask, p9attr)

	// Construct the positive Dirent.
	d := fs.NewDirent(fs.NewInode(iops, dir.MountSource, sattr), name)
	defer d.DecRef()

	// Construct the new file, caching the handles if allowed.
	h := &handles{
		File: newFile,
		Host: hostFile,
	}
	if isFileCachable(iops.session(), d.Inode) {
		iops.fileState.setHandlesForCachedIO(flags, h)
	}
	return NewFile(ctx, d, flags, iops, h), nil
}

// CreateLink uses Create to create a symlink between oldname and newname.
func (i *inodeOperations) CreateLink(ctx context.Context, dir *fs.Inode, oldname string, newname string) error {
	owner := fs.FileOwnerFromContext(ctx)
	if _, err := i.fileState.file.symlink(ctx, oldname, newname, p9.UID(owner.UID), p9.GID(owner.GID)); err != nil {
		return err
	}
	i.touchModificationTime(ctx)
	return nil
}

// CreateHardLink implements InodeOperations.CreateHardLink.
func (i *inodeOperations) CreateHardLink(ctx context.Context, _ *fs.Inode, target *fs.Inode, newName string) error {
	targetOpts, ok := target.InodeOperations.(*inodeOperations)
	if !ok {
		return syscall.EXDEV
	}

	if err := i.fileState.file.link(ctx, &targetOpts.fileState.file, newName); err != nil {
		return err
	}
	if i.session().cachePolicy == cacheAll {
		// Increase link count.
		targetOpts.cachingInodeOps.IncLinks(ctx)
	}
	i.touchModificationTime(ctx)
	return nil
}

// CreateDirectory uses Create to create a directory named s under inodeOperations.
func (i *inodeOperations) CreateDirectory(ctx context.Context, dir *fs.Inode, s string, perm fs.FilePermissions) error {
	owner := fs.FileOwnerFromContext(ctx)
	if _, err := i.fileState.file.mkdir(ctx, s, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID)); err != nil {
		return err
	}
	if i.session().cachePolicy == cacheAll {
		// Increase link count.
		i.cachingInodeOps.IncLinks(ctx)

		// Invalidate readdir cache.
		i.markDirectoryDirty()
	}
	return nil
}

// Bind implements InodeOperations.
func (i *inodeOperations) Bind(ctx context.Context, dir *fs.Inode, name string, ep unix.BoundEndpoint, perm fs.FilePermissions) error {
	if i.session().endpoints == nil {
		return syscall.EOPNOTSUPP
	}

	// Create replaces the directory fid with the newly created/opened
	// file, so clone this directory so it doesn't change out from under
	// this node.
	_, newFile, err := i.fileState.file.walk(ctx, nil)
	if err != nil {
		return err
	}

	// Stabilize the endpoint map while creation is in progress.
	unlock := i.session().endpoints.lock()
	defer unlock()

	// Create a regular file in the gofer and then mark it as a socket by
	// adding this inode key in the 'endpoints' map.
	owner := fs.FileOwnerFromContext(ctx)
	hostFile, err := newFile.create(ctx, name, p9.ReadWrite, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID))
	if err != nil {
		return err
	}
	// We're not going to use this file.
	hostFile.Close()

	i.touchModificationTime(ctx)

	// Get the attributes of the file to create inode key.
	qid, _, attr, err := getattr(ctx, newFile)
	if err != nil {
		newFile.close(ctx)
		return err
	}

	key := device.MultiDeviceKey{
		Device:          attr.RDev,
		SecondaryDevice: i.session().connID,
		Inode:           qid.Path,
	}
	i.session().endpoints.add(key, ep)

	return nil
}

// CreateFifo implements fs.InodeOperations.CreateFifo. Gofer nodes do not support the
// creation of fifos and always returns EOPNOTSUPP.
func (*inodeOperations) CreateFifo(context.Context, *fs.Inode, string, fs.FilePermissions) error {
	return syscall.EOPNOTSUPP
}

// Remove implements InodeOperations.Remove.
func (i *inodeOperations) Remove(ctx context.Context, dir *fs.Inode, name string) error {
	var key device.MultiDeviceKey
	removeSocket := false
	if i.session().endpoints != nil {
		// Find out if file being deleted is a socket that needs to be
		// removed from endpoint map.
		if d, err := i.Lookup(ctx, dir, name); err == nil {
			defer d.DecRef()
			if fs.IsSocket(d.Inode.StableAttr) {
				child := d.Inode.InodeOperations.(*inodeOperations)
				key = child.fileState.key
				removeSocket = true

				// Stabilize the endpoint map while deletion is in progress.
				unlock := i.session().endpoints.lock()
				defer unlock()
			}
		}
	}

	if err := i.fileState.file.unlinkAt(ctx, name, 0); err != nil {
		return err
	}
	if removeSocket {
		i.session().endpoints.remove(key)
	}
	i.touchModificationTime(ctx)

	return nil
}

// Remove implements InodeOperations.RemoveDirectory.
func (i *inodeOperations) RemoveDirectory(ctx context.Context, dir *fs.Inode, name string) error {
	// 0x200 = AT_REMOVEDIR.
	if err := i.fileState.file.unlinkAt(ctx, name, 0x200); err != nil {
		return err
	}
	if i.session().cachePolicy == cacheAll {
		// Decrease link count and updates atime.
		i.cachingInodeOps.DecLinks(ctx)

		// Invalidate readdir cache.
		i.markDirectoryDirty()
	}
	return nil
}

// Rename renames this node.
func (i *inodeOperations) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
	// Unwrap the new parent to a *inodeOperations.
	newParentInodeOperations, ok := newParent.InodeOperations.(*inodeOperations)
	if !ok {
		return syscall.EXDEV
	}

	// Unwrap the old parent to a *inodeOperations.
	oldParentInodeOperations, ok := oldParent.InodeOperations.(*inodeOperations)
	if !ok {
		return syscall.EXDEV
	}

	// Do the rename.
	if err := i.fileState.file.rename(ctx, newParentInodeOperations.fileState.file, newName); err != nil {
		return err
	}

	// Update cached state.
	if i.session().cachePolicy == cacheAll {
		// Is the renamed entity a directory? Fix link counts.
		if fs.IsDir(i.fileState.sattr) {
			oldParentInodeOperations.cachingInodeOps.DecLinks(ctx)
			newParentInodeOperations.cachingInodeOps.IncLinks(ctx)
		}

		// Mark old directory dirty.
		oldParentInodeOperations.markDirectoryDirty()
		if oldParent != newParent {
			// Mark new directory dirty.
			newParentInodeOperations.markDirectoryDirty()
		}
	}
	return nil
}

func (i *inodeOperations) touchModificationTime(ctx context.Context) {
	if i.session().cachePolicy == cacheAll {
		i.cachingInodeOps.TouchModificationTime(ctx)

		// Invalidate readdir cache.
		i.markDirectoryDirty()
	}
}

// markDirectoryDirty marks any cached data dirty for this directory. This is necessary in order
// to ensure that this node does not retain stale state throughout its lifetime across multiple
// open directory handles.
//
// Currently this means invalidating any readdir caches.
func (i *inodeOperations) markDirectoryDirty() {
	i.readdirMu.Lock()
	defer i.readdirMu.Unlock()
	i.readdirCache = nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Skaffold 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 event

import (
	"context"
	"fmt"
	"sync"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/event/proto"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/version"
	"github.com/golang/protobuf/ptypes"
	"github.com/pkg/errors"
	"google.golang.org/grpc"
)

const (
	NotStarted = "Not Started"
	InProgress = "In Progress"
	Complete   = "Complete"
	Failed     = "Failed"
)

var (
	ev         *eventHandler
	once       sync.Once
	pluginMode bool

	cli proto.SkaffoldServiceClient // for plugin RPC connections
)

type eventHandler struct {
	eventLog []proto.LogEntry
	logLock  sync.Mutex

	state     *proto.State
	stateLock sync.Mutex

	listeners []chan proto.LogEntry
}

func (ev *eventHandler) RegisterListener(listener chan proto.LogEntry) {
	ev.listeners = append(ev.listeners, listener)
}

func (ev *eventHandler) logEvent(entry proto.LogEntry) {
	ev.logLock.Lock()

	for _, c := range ev.listeners {
		c <- entry
	}
	ev.eventLog = append(ev.eventLog, entry)

	ev.logLock.Unlock()
}

func (ev *eventHandler) forEachEvent(callback func(*proto.LogEntry) error) error {
	c := make(chan proto.LogEntry)

	ev.logLock.Lock()

	for _, entry := range ev.eventLog {
		if err := callback(&entry); err != nil {
			return err
		}
	}
	ev.RegisterListener(c)

	ev.logLock.Unlock()

	for {
		entry := <-c
		if err := callback(&entry); err != nil {
			return err
		}
	}
}

// InitializeState instantiates the global state of the skaffold runner, as well as the event log.
// It returns a shutdown callback for tearing down the grpc server, which the runner is responsible for calling.
// This function can only be called once.
func InitializeState(build *latest.BuildConfig, deploy *latest.DeployConfig, opts *config.SkaffoldOptions) (func() error, error) {
	var err error
	serverShutdown := func() error { return nil }
	once.Do(func() {
		builds := map[string]string{}
		deploys := map[string]string{}
		if build != nil {
			for _, a := range build.Artifacts {
				builds[a.ImageName] = NotStarted
				deploys[a.ImageName] = NotStarted
			}
		}

		ev = &eventHandler{
			state: &proto.State{
				BuildState: &proto.BuildState{
					Artifacts: builds,
				},
				DeployState: &proto.DeployState{
					Status: NotStarted,
				},
				ForwardedPorts: make(map[string]*proto.PortEvent),
			},
		}

		if opts.EnableRPC {
			serverShutdown, err = newStatusServer(opts.RPCPort)
			if err != nil {
				err = errors.Wrap(err, "creating status server")
				return
			}
		}
	})
	return serverShutdown, err
}

func SetupRPCClient(opts *config.SkaffoldOptions) error {
	pluginMode = true
	conn, err := grpc.Dial(fmt.Sprintf(":%d", opts.RPCPort), grpc.WithInsecure())
	if err != nil {
		return errors.Wrap(err, "opening gRPC connection to remote skaffold process")
	}
	cli = proto.NewSkaffoldServiceClient(conn)
	return nil
}

func Handle(event *proto.Event) {
	if pluginMode {
		go cli.Handle(context.Background(), event)
	} else {
		go handle(event)
	}
}

func handle(event *proto.Event) {
	logEntry := &proto.LogEntry{
		Timestamp: ptypes.TimestampNow(),
		Event:     event,
	}

	switch e := event.GetEventType().(type) {
	case *proto.Event_BuildEvent:
		be := e.BuildEvent
		ev.stateLock.Lock()
		ev.state.BuildState.Artifacts[be.Artifact] = be.Status
		ev.stateLock.Unlock()
		switch be.Status {
		case InProgress:
			logEntry.Entry = fmt.Sprintf("Build started for artifact %s", be.Artifact)
		case Complete:
			logEntry.Entry = fmt.Sprintf("Build completed for artifact %s", be.Artifact)
		case Failed:
			logEntry.Entry = fmt.Sprintf("Build failed for artifact %s", be.Artifact)
			// logEntry.Err = be.Err
		default:
		}
	case *proto.Event_DeployEvent:
		de := e.DeployEvent
		ev.stateLock.Lock()
		ev.state.DeployState.Status = de.Status
		ev.stateLock.Unlock()
		switch de.Status {
		case InProgress:
			logEntry.Entry = "Deploy started"
		case Complete:
			logEntry.Entry = "Deploy complete"
		case Failed:
			logEntry.Entry = "Deploy failed"
			// logEntry.Err = de.Err
		default:
		}
	case *proto.Event_PortEvent:
		pe := e.PortEvent
		ev.stateLock.Lock()
		ev.state.ForwardedPorts[pe.ContainerName] = pe
		ev.stateLock.Unlock()
		logEntry.Entry = fmt.Sprintf("Forwarding container %s to local port %d", pe.ContainerName, pe.LocalPort)
	default:
		return
	}

	ev.logEvent(*logEntry)
}

func LogSkaffoldMetadata(info *version.Info) {
	ev.logEvent(proto.LogEntry{
		Timestamp: ptypes.TimestampNow(),
		Event: &proto.Event{
			EventType: &proto.Event_MetaEvent{
				MetaEvent: &proto.MetaEvent{
					Entry: fmt.Sprintf("Starting Skaffold: %+v", info),
				},
			},
		},
	})
}
<commit_msg>Don’t block while sending old events<commit_after>/*
Copyright 2019 The Skaffold 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 event

import (
	"context"
	"fmt"
	"sync"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/event/proto"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/version"
	"github.com/golang/protobuf/ptypes"
	"github.com/pkg/errors"
	"google.golang.org/grpc"
)

const (
	NotStarted = "Not Started"
	InProgress = "In Progress"
	Complete   = "Complete"
	Failed     = "Failed"
)

var (
	ev         *eventHandler
	once       sync.Once
	pluginMode bool

	cli proto.SkaffoldServiceClient // for plugin RPC connections
)

type eventHandler struct {
	eventLog []proto.LogEntry
	logLock  sync.Mutex

	state     *proto.State
	stateLock sync.Mutex

	listeners []chan proto.LogEntry
}

func (ev *eventHandler) RegisterListener(listener chan proto.LogEntry) {
	ev.listeners = append(ev.listeners, listener)
}

func (ev *eventHandler) logEvent(entry proto.LogEntry) {
	ev.logLock.Lock()

	for _, c := range ev.listeners {
		c <- entry
	}
	ev.eventLog = append(ev.eventLog, entry)

	ev.logLock.Unlock()
}

func (ev *eventHandler) forEachEvent(callback func(*proto.LogEntry) error) error {
	c := make(chan proto.LogEntry)

	ev.logLock.Lock()

	oldEvents := make([]proto.LogEntry, len(ev.eventLog))
	copy(oldEvents, ev.eventLog)
	ev.RegisterListener(c)

	ev.logLock.Unlock()

	for _, entry := range oldEvents {
		if err := callback(&entry); err != nil {
			return err
		}
	}

	for {
		entry := <-c
		if err := callback(&entry); err != nil {
			return err
		}
	}
}

// InitializeState instantiates the global state of the skaffold runner, as well as the event log.
// It returns a shutdown callback for tearing down the grpc server, which the runner is responsible for calling.
// This function can only be called once.
func InitializeState(build *latest.BuildConfig, deploy *latest.DeployConfig, opts *config.SkaffoldOptions) (func() error, error) {
	var err error
	serverShutdown := func() error { return nil }
	once.Do(func() {
		builds := map[string]string{}
		deploys := map[string]string{}
		if build != nil {
			for _, a := range build.Artifacts {
				builds[a.ImageName] = NotStarted
				deploys[a.ImageName] = NotStarted
			}
		}

		ev = &eventHandler{
			state: &proto.State{
				BuildState: &proto.BuildState{
					Artifacts: builds,
				},
				DeployState: &proto.DeployState{
					Status: NotStarted,
				},
				ForwardedPorts: make(map[string]*proto.PortEvent),
			},
		}

		if opts.EnableRPC {
			serverShutdown, err = newStatusServer(opts.RPCPort)
			if err != nil {
				err = errors.Wrap(err, "creating status server")
				return
			}
		}
	})
	return serverShutdown, err
}

func SetupRPCClient(opts *config.SkaffoldOptions) error {
	pluginMode = true
	conn, err := grpc.Dial(fmt.Sprintf(":%d", opts.RPCPort), grpc.WithInsecure())
	if err != nil {
		return errors.Wrap(err, "opening gRPC connection to remote skaffold process")
	}
	cli = proto.NewSkaffoldServiceClient(conn)
	return nil
}

func Handle(event *proto.Event) {
	if pluginMode {
		go cli.Handle(context.Background(), event)
	} else {
		go handle(event)
	}
}

func handle(event *proto.Event) {
	logEntry := &proto.LogEntry{
		Timestamp: ptypes.TimestampNow(),
		Event:     event,
	}

	switch e := event.GetEventType().(type) {
	case *proto.Event_BuildEvent:
		be := e.BuildEvent
		ev.stateLock.Lock()
		ev.state.BuildState.Artifacts[be.Artifact] = be.Status
		ev.stateLock.Unlock()
		switch be.Status {
		case InProgress:
			logEntry.Entry = fmt.Sprintf("Build started for artifact %s", be.Artifact)
		case Complete:
			logEntry.Entry = fmt.Sprintf("Build completed for artifact %s", be.Artifact)
		case Failed:
			logEntry.Entry = fmt.Sprintf("Build failed for artifact %s", be.Artifact)
			// logEntry.Err = be.Err
		default:
		}
	case *proto.Event_DeployEvent:
		de := e.DeployEvent
		ev.stateLock.Lock()
		ev.state.DeployState.Status = de.Status
		ev.stateLock.Unlock()
		switch de.Status {
		case InProgress:
			logEntry.Entry = "Deploy started"
		case Complete:
			logEntry.Entry = "Deploy complete"
		case Failed:
			logEntry.Entry = "Deploy failed"
			// logEntry.Err = de.Err
		default:
		}
	case *proto.Event_PortEvent:
		pe := e.PortEvent
		ev.stateLock.Lock()
		ev.state.ForwardedPorts[pe.ContainerName] = pe
		ev.stateLock.Unlock()
		logEntry.Entry = fmt.Sprintf("Forwarding container %s to local port %d", pe.ContainerName, pe.LocalPort)
	default:
		return
	}

	ev.logEvent(*logEntry)
}

func LogSkaffoldMetadata(info *version.Info) {
	ev.logEvent(proto.LogEntry{
		Timestamp: ptypes.TimestampNow(),
		Event: &proto.Event{
			EventType: &proto.Event_MetaEvent{
				MetaEvent: &proto.MetaEvent{
					Entry: fmt.Sprintf("Starting Skaffold: %+v", info),
				},
			},
		},
	})
}
<|endoftext|>


<commit_before>//  Copyright (c) 2017 Couchbase, Inc.
//
// 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 zap

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"math"
	"reflect"
	"sort"

	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/index/scorch/segment"
	"github.com/blevesearch/bleve/size"
	"github.com/golang/snappy"
)

var reflectStaticSizedocValueReader int

func init() {
	var dvi docValueReader
	reflectStaticSizedocValueReader = int(reflect.TypeOf(dvi).Size())
}

type docNumTermsVisitor func(docNum uint64, terms []byte) error

type docVisitState struct {
	dvrs    map[uint16]*docValueReader
	segment *Segment
}

type docValueReader struct {
	field          string
	curChunkNum    uint64
	chunkOffsets   []uint64
	dvDataLoc      uint64
	curChunkHeader []MetaData
	curChunkData   []byte // compressed data cache
	uncompressed   []byte // temp buf for snappy decompression
}

func (di *docValueReader) size() int {
	return reflectStaticSizedocValueReader + size.SizeOfPtr +
		len(di.field) +
		len(di.chunkOffsets)*size.SizeOfUint64 +
		len(di.curChunkHeader)*reflectStaticSizeMetaData +
		len(di.curChunkData)
}

func (di *docValueReader) cloneInto(rv *docValueReader) *docValueReader {
	if rv == nil {
		rv = &docValueReader{}
	}

	rv.field = di.field
	rv.curChunkNum = math.MaxUint64
	rv.chunkOffsets = di.chunkOffsets // immutable, so it's sharable
	rv.dvDataLoc = di.dvDataLoc
	rv.curChunkHeader = nil
	rv.curChunkData = nil
	rv.uncompressed = nil

	return rv
}

func (di *docValueReader) fieldName() string {
	return di.field
}

func (di *docValueReader) curChunkNumber() uint64 {
	return di.curChunkNum
}

func (s *SegmentBase) loadFieldDocValueReader(field string,
	fieldDvLocStart, fieldDvLocEnd uint64) (*docValueReader, error) {
	// get the docValue offset for the given fields
	if fieldDvLocStart == fieldNotUninverted {
		return nil, fmt.Errorf("loadFieldDocValueReader: "+
			"no docValues found for field: %s", field)
	}

	// read the number of chunks, and chunk offsets position
	var numChunks, chunkOffsetsPosition uint64

	if fieldDvLocEnd-fieldDvLocStart > 16 {
		numChunks = binary.BigEndian.Uint64(s.mem[fieldDvLocEnd-8 : fieldDvLocEnd])
		// read the length of chunk offsets
		chunkOffsetsLen := binary.BigEndian.Uint64(s.mem[fieldDvLocEnd-16 : fieldDvLocEnd-8])
		// acquire position of chunk offsets
		chunkOffsetsPosition = (fieldDvLocEnd - 16) - chunkOffsetsLen
	}

	fdvIter := &docValueReader{
		curChunkNum:  math.MaxUint64,
		field:        field,
		chunkOffsets: make([]uint64, int(numChunks)),
	}

	// read the chunk offsets
	var offset uint64
	for i := 0; i < int(numChunks); i++ {
		loc, read := binary.Uvarint(s.mem[chunkOffsetsPosition+offset : chunkOffsetsPosition+offset+binary.MaxVarintLen64])
		if read <= 0 {
			return nil, fmt.Errorf("corrupted chunk offset during segment load")
		}
		fdvIter.chunkOffsets[i] = loc
		offset += uint64(read)
	}

	// set the data offset
	fdvIter.dvDataLoc = fieldDvLocStart

	return fdvIter, nil
}

func (di *docValueReader) loadDvChunk(chunkNumber uint64, s *SegmentBase) error {
	// advance to the chunk where the docValues
	// reside for the given docNum
	destChunkDataLoc, curChunkEnd := di.dvDataLoc, di.dvDataLoc
	start, end := readChunkBoundary(int(chunkNumber), di.chunkOffsets)
	if start >= end {
		di.curChunkHeader = di.curChunkHeader[:0]
		di.curChunkData = nil
		di.curChunkNum = chunkNumber
		di.uncompressed = di.uncompressed[:0]
		return nil
	}

	destChunkDataLoc += start
	curChunkEnd += end

	// read the number of docs reside in the chunk
	numDocs, read := binary.Uvarint(s.mem[destChunkDataLoc : destChunkDataLoc+binary.MaxVarintLen64])
	if read <= 0 {
		return fmt.Errorf("failed to read the chunk")
	}
	chunkMetaLoc := destChunkDataLoc + uint64(read)

	offset := uint64(0)
	di.curChunkHeader = make([]MetaData, int(numDocs))
	for i := 0; i < int(numDocs); i++ {
		di.curChunkHeader[i].DocNum, read = binary.Uvarint(s.mem[chunkMetaLoc+offset : chunkMetaLoc+offset+binary.MaxVarintLen64])
		offset += uint64(read)
		di.curChunkHeader[i].DocDvOffset, read = binary.Uvarint(s.mem[chunkMetaLoc+offset : chunkMetaLoc+offset+binary.MaxVarintLen64])
		offset += uint64(read)
	}

	compressedDataLoc := chunkMetaLoc + offset
	dataLength := curChunkEnd - compressedDataLoc
	di.curChunkData = s.mem[compressedDataLoc : compressedDataLoc+dataLength]
	di.curChunkNum = chunkNumber
	di.uncompressed = di.uncompressed[:0]
	return nil
}

func (di *docValueReader) iterateAllDocValues(s *SegmentBase, visitor docNumTermsVisitor) error {
	for i := 0; i < len(di.chunkOffsets); i++ {
		err := di.loadDvChunk(uint64(i), s)
		if err != nil {
			return err
		}
		if di.curChunkData == nil || len(di.curChunkHeader) <= 0 {
			continue
		}

		// uncompress the already loaded data
		uncompressed, err := snappy.Decode(di.uncompressed[:cap(di.uncompressed)], di.curChunkData)
		if err != nil {
			return err
		}
		di.uncompressed = uncompressed

		start := uint64(0)
		for _, entry := range di.curChunkHeader {
			err = visitor(entry.DocNum, uncompressed[start:entry.DocDvOffset])
			if err != nil {
				return err
			}

			start = entry.DocDvOffset
		}
	}

	return nil
}

func (di *docValueReader) visitDocValues(docNum uint64,
	visitor index.DocumentFieldTermVisitor) error {
	// binary search the term locations for the docNum
	start, end := di.getDocValueLocs(docNum)
	if start == math.MaxUint64 || end == math.MaxUint64 || start == end {
		return nil
	}

	var uncompressed []byte
	var err error
	// use the uncompressed copy if available
	if len(di.uncompressed) > 0 {
		uncompressed = di.uncompressed
	} else {
		// uncompress the already loaded data
		uncompressed, err = snappy.Decode(di.uncompressed[:cap(di.uncompressed)], di.curChunkData)
		if err != nil {
			return err
		}
		di.uncompressed = uncompressed
	}

	// pick the terms for the given docNum
	uncompressed = uncompressed[start:end]
	for {
		i := bytes.Index(uncompressed, termSeparatorSplitSlice)
		if i < 0 {
			break
		}

		visitor(di.field, uncompressed[0:i])
		uncompressed = uncompressed[i+1:]
	}

	return nil
}

func (di *docValueReader) getDocValueLocs(docNum uint64) (uint64, uint64) {
	i := sort.Search(len(di.curChunkHeader), func(i int) bool {
		return di.curChunkHeader[i].DocNum >= docNum
	})
	if i < len(di.curChunkHeader) && di.curChunkHeader[i].DocNum == docNum {
		return ReadDocValueBoundary(i, di.curChunkHeader)
	}
	return math.MaxUint64, math.MaxUint64
}

// VisitDocumentFieldTerms is an implementation of the
// DocumentFieldTermVisitable interface
func (s *Segment) VisitDocumentFieldTerms(localDocNum uint64, fields []string,
	visitor index.DocumentFieldTermVisitor, dvsIn segment.DocVisitState) (
	segment.DocVisitState, error) {
	dvs, ok := dvsIn.(*docVisitState)
	if !ok || dvs == nil {
		dvs = &docVisitState{}
	} else {
		if dvs.segment != s {
			dvs.segment = s
			dvs.dvrs = nil
		}
	}

	var fieldIDPlus1 uint16
	if dvs.dvrs == nil {
		dvs.dvrs = make(map[uint16]*docValueReader, len(fields))
		for _, field := range fields {
			if fieldIDPlus1, ok = s.fieldsMap[field]; !ok {
				continue
			}
			fieldID := fieldIDPlus1 - 1
			if dvIter, exists := s.fieldDvReaders[fieldID]; exists &&
				dvIter != nil {
				dvs.dvrs[fieldID] = dvIter.cloneInto(dvs.dvrs[fieldID])
			}
		}
	}

	// find the chunkNumber where the docValues are stored
	docInChunk := localDocNum / uint64(s.chunkFactor)
	var dvr *docValueReader
	for _, field := range fields {
		if fieldIDPlus1, ok = s.fieldsMap[field]; !ok {
			continue
		}
		fieldID := fieldIDPlus1 - 1
		if dvr, ok = dvs.dvrs[fieldID]; ok && dvr != nil {
			// check if the chunk is already loaded
			if docInChunk != dvr.curChunkNumber() {
				err := dvr.loadDvChunk(docInChunk, &s.SegmentBase)
				if err != nil {
					return dvs, err
				}
			}

			_ = dvr.visitDocValues(localDocNum, visitor)
		}
	}
	return dvs, nil
}

// VisitableDocValueFields returns the list of fields with
// persisted doc value terms ready to be visitable using the
// VisitDocumentFieldTerms method.
func (s *Segment) VisitableDocValueFields() ([]string, error) {
	return s.fieldDvNames, nil
}
<commit_msg>scorch zap optimize docValueReader curChunkHeader slice reuse<commit_after>//  Copyright (c) 2017 Couchbase, Inc.
//
// 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 zap

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"math"
	"reflect"
	"sort"

	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/index/scorch/segment"
	"github.com/blevesearch/bleve/size"
	"github.com/golang/snappy"
)

var reflectStaticSizedocValueReader int

func init() {
	var dvi docValueReader
	reflectStaticSizedocValueReader = int(reflect.TypeOf(dvi).Size())
}

type docNumTermsVisitor func(docNum uint64, terms []byte) error

type docVisitState struct {
	dvrs    map[uint16]*docValueReader
	segment *Segment
}

type docValueReader struct {
	field          string
	curChunkNum    uint64
	chunkOffsets   []uint64
	dvDataLoc      uint64
	curChunkHeader []MetaData
	curChunkData   []byte // compressed data cache
	uncompressed   []byte // temp buf for snappy decompression
}

func (di *docValueReader) size() int {
	return reflectStaticSizedocValueReader + size.SizeOfPtr +
		len(di.field) +
		len(di.chunkOffsets)*size.SizeOfUint64 +
		len(di.curChunkHeader)*reflectStaticSizeMetaData +
		len(di.curChunkData)
}

func (di *docValueReader) cloneInto(rv *docValueReader) *docValueReader {
	if rv == nil {
		rv = &docValueReader{}
	}

	rv.field = di.field
	rv.curChunkNum = math.MaxUint64
	rv.chunkOffsets = di.chunkOffsets // immutable, so it's sharable
	rv.dvDataLoc = di.dvDataLoc
	rv.curChunkHeader = rv.curChunkHeader[:0]
	rv.curChunkData = nil
	rv.uncompressed = rv.uncompressed[:0]

	return rv
}

func (di *docValueReader) fieldName() string {
	return di.field
}

func (di *docValueReader) curChunkNumber() uint64 {
	return di.curChunkNum
}

func (s *SegmentBase) loadFieldDocValueReader(field string,
	fieldDvLocStart, fieldDvLocEnd uint64) (*docValueReader, error) {
	// get the docValue offset for the given fields
	if fieldDvLocStart == fieldNotUninverted {
		return nil, fmt.Errorf("loadFieldDocValueReader: "+
			"no docValues found for field: %s", field)
	}

	// read the number of chunks, and chunk offsets position
	var numChunks, chunkOffsetsPosition uint64

	if fieldDvLocEnd-fieldDvLocStart > 16 {
		numChunks = binary.BigEndian.Uint64(s.mem[fieldDvLocEnd-8 : fieldDvLocEnd])
		// read the length of chunk offsets
		chunkOffsetsLen := binary.BigEndian.Uint64(s.mem[fieldDvLocEnd-16 : fieldDvLocEnd-8])
		// acquire position of chunk offsets
		chunkOffsetsPosition = (fieldDvLocEnd - 16) - chunkOffsetsLen
	}

	fdvIter := &docValueReader{
		curChunkNum:  math.MaxUint64,
		field:        field,
		chunkOffsets: make([]uint64, int(numChunks)),
	}

	// read the chunk offsets
	var offset uint64
	for i := 0; i < int(numChunks); i++ {
		loc, read := binary.Uvarint(s.mem[chunkOffsetsPosition+offset : chunkOffsetsPosition+offset+binary.MaxVarintLen64])
		if read <= 0 {
			return nil, fmt.Errorf("corrupted chunk offset during segment load")
		}
		fdvIter.chunkOffsets[i] = loc
		offset += uint64(read)
	}

	// set the data offset
	fdvIter.dvDataLoc = fieldDvLocStart

	return fdvIter, nil
}

func (di *docValueReader) loadDvChunk(chunkNumber uint64, s *SegmentBase) error {
	// advance to the chunk where the docValues
	// reside for the given docNum
	destChunkDataLoc, curChunkEnd := di.dvDataLoc, di.dvDataLoc
	start, end := readChunkBoundary(int(chunkNumber), di.chunkOffsets)
	if start >= end {
		di.curChunkHeader = di.curChunkHeader[:0]
		di.curChunkData = nil
		di.curChunkNum = chunkNumber
		di.uncompressed = di.uncompressed[:0]
		return nil
	}

	destChunkDataLoc += start
	curChunkEnd += end

	// read the number of docs reside in the chunk
	numDocs, read := binary.Uvarint(s.mem[destChunkDataLoc : destChunkDataLoc+binary.MaxVarintLen64])
	if read <= 0 {
		return fmt.Errorf("failed to read the chunk")
	}
	chunkMetaLoc := destChunkDataLoc + uint64(read)

	offset := uint64(0)
	if cap(di.curChunkHeader) < int(numDocs) {
		di.curChunkHeader = make([]MetaData, int(numDocs))
	} else {
		di.curChunkHeader = di.curChunkHeader[:int(numDocs)]
	}
	for i := 0; i < int(numDocs); i++ {
		di.curChunkHeader[i].DocNum, read = binary.Uvarint(s.mem[chunkMetaLoc+offset : chunkMetaLoc+offset+binary.MaxVarintLen64])
		offset += uint64(read)
		di.curChunkHeader[i].DocDvOffset, read = binary.Uvarint(s.mem[chunkMetaLoc+offset : chunkMetaLoc+offset+binary.MaxVarintLen64])
		offset += uint64(read)
	}

	compressedDataLoc := chunkMetaLoc + offset
	dataLength := curChunkEnd - compressedDataLoc
	di.curChunkData = s.mem[compressedDataLoc : compressedDataLoc+dataLength]
	di.curChunkNum = chunkNumber
	di.uncompressed = di.uncompressed[:0]
	return nil
}

func (di *docValueReader) iterateAllDocValues(s *SegmentBase, visitor docNumTermsVisitor) error {
	for i := 0; i < len(di.chunkOffsets); i++ {
		err := di.loadDvChunk(uint64(i), s)
		if err != nil {
			return err
		}
		if di.curChunkData == nil || len(di.curChunkHeader) <= 0 {
			continue
		}

		// uncompress the already loaded data
		uncompressed, err := snappy.Decode(di.uncompressed[:cap(di.uncompressed)], di.curChunkData)
		if err != nil {
			return err
		}
		di.uncompressed = uncompressed

		start := uint64(0)
		for _, entry := range di.curChunkHeader {
			err = visitor(entry.DocNum, uncompressed[start:entry.DocDvOffset])
			if err != nil {
				return err
			}

			start = entry.DocDvOffset
		}
	}

	return nil
}

func (di *docValueReader) visitDocValues(docNum uint64,
	visitor index.DocumentFieldTermVisitor) error {
	// binary search the term locations for the docNum
	start, end := di.getDocValueLocs(docNum)
	if start == math.MaxUint64 || end == math.MaxUint64 || start == end {
		return nil
	}

	var uncompressed []byte
	var err error
	// use the uncompressed copy if available
	if len(di.uncompressed) > 0 {
		uncompressed = di.uncompressed
	} else {
		// uncompress the already loaded data
		uncompressed, err = snappy.Decode(di.uncompressed[:cap(di.uncompressed)], di.curChunkData)
		if err != nil {
			return err
		}
		di.uncompressed = uncompressed
	}

	// pick the terms for the given docNum
	uncompressed = uncompressed[start:end]
	for {
		i := bytes.Index(uncompressed, termSeparatorSplitSlice)
		if i < 0 {
			break
		}

		visitor(di.field, uncompressed[0:i])
		uncompressed = uncompressed[i+1:]
	}

	return nil
}

func (di *docValueReader) getDocValueLocs(docNum uint64) (uint64, uint64) {
	i := sort.Search(len(di.curChunkHeader), func(i int) bool {
		return di.curChunkHeader[i].DocNum >= docNum
	})
	if i < len(di.curChunkHeader) && di.curChunkHeader[i].DocNum == docNum {
		return ReadDocValueBoundary(i, di.curChunkHeader)
	}
	return math.MaxUint64, math.MaxUint64
}

// VisitDocumentFieldTerms is an implementation of the
// DocumentFieldTermVisitable interface
func (s *Segment) VisitDocumentFieldTerms(localDocNum uint64, fields []string,
	visitor index.DocumentFieldTermVisitor, dvsIn segment.DocVisitState) (
	segment.DocVisitState, error) {
	dvs, ok := dvsIn.(*docVisitState)
	if !ok || dvs == nil {
		dvs = &docVisitState{}
	} else {
		if dvs.segment != s {
			dvs.segment = s
			dvs.dvrs = nil
		}
	}

	var fieldIDPlus1 uint16
	if dvs.dvrs == nil {
		dvs.dvrs = make(map[uint16]*docValueReader, len(fields))
		for _, field := range fields {
			if fieldIDPlus1, ok = s.fieldsMap[field]; !ok {
				continue
			}
			fieldID := fieldIDPlus1 - 1
			if dvIter, exists := s.fieldDvReaders[fieldID]; exists &&
				dvIter != nil {
				dvs.dvrs[fieldID] = dvIter.cloneInto(dvs.dvrs[fieldID])
			}
		}
	}

	// find the chunkNumber where the docValues are stored
	docInChunk := localDocNum / uint64(s.chunkFactor)
	var dvr *docValueReader
	for _, field := range fields {
		if fieldIDPlus1, ok = s.fieldsMap[field]; !ok {
			continue
		}
		fieldID := fieldIDPlus1 - 1
		if dvr, ok = dvs.dvrs[fieldID]; ok && dvr != nil {
			// check if the chunk is already loaded
			if docInChunk != dvr.curChunkNumber() {
				err := dvr.loadDvChunk(docInChunk, &s.SegmentBase)
				if err != nil {
					return dvs, err
				}
			}

			_ = dvr.visitDocValues(localDocNum, visitor)
		}
	}
	return dvs, nil
}

// VisitableDocValueFields returns the list of fields with
// persisted doc value terms ready to be visitable using the
// VisitDocumentFieldTerms method.
func (s *Segment) VisitableDocValueFields() ([]string, error) {
	return s.fieldDvNames, nil
}
<|endoftext|>


<commit_before>package transporter

import (
	"log"
	"time"

	"github.com/compose/transporter/pkg/adaptor"
	"github.com/compose/transporter/pkg/events"
	"github.com/compose/transporter/pkg/state"
)

// VERSION the library
const (
	VERSION = "0.0.2"
)

// A Pipeline is a the end to end description of a transporter data flow.
// including the source, sink, and all the transformers along the way
type Pipeline struct {
	source        *Node
	emitter       events.Emitter
	sessionStore  state.SessionStore
	metricsTicker *time.Ticker

	// Err is the fatal error that was sent from the adaptor
	// that caused us to stop this process.  If this is nil, then
	// the transporter is running
	Err           error
	sessionTicker *time.Ticker
}

// NewDefaultPipeline returns a new Transporter Pipeline with the given node tree, and
// uses the events.HttpPostEmitter to deliver metrics.
// eg.
//   source :=
//   	transporter.NewNode("source", "mongo", adaptor.Config{"uri": "mongodb://localhost/", "namespace": "boom.foo", "debug": false, "tail": true}).
// 	  	Add(transporter.NewNode("out", "file", adaptor.Config{"uri": "stdout://"}))
//   pipeline, err := transporter.NewDefaultPipeline(source, events.Api{URI: "http://localhost/endpoint"}, 1*time.Second)
//   if err != nil {
// 	  fmt.Println(err)
// 	  os.Exit(1)
//   }
// pipeline.Run()
func NewDefaultPipeline(source *Node, uri, key, pid string, interval time.Duration) (*Pipeline, error) {
	emitter := events.NewHTTPPostEmitter(uri, key, pid)
	sessionStore := state.NewFilestore(pid, "/tmp/transporter.state")
	return NewPipeline(source, emitter, interval, sessionStore, 10*time.Second)
}

// NewPipeline creates a new Transporter Pipeline using the given tree of nodes, and Event Emitter
// eg.
//   source :=
//   	transporter.NewNode("source", "mongo", adaptor.Config{"uri": "mongodb://localhost/", "namespace": "boom.foo", "debug": false, "tail": true}).
// 	  	Add(transporter.NewNode("out", "file", adaptor.Config{"uri": "stdout://"}))
//   pipeline, err := transporter.NewPipeline(source, events.NewNoopEmitter(), 1*time.Second, state.NewFilestore(pid, "/tmp/transporter.state"), 10*time.Second)
//   if err != nil {
// 	  fmt.Println(err)
// 	  os.Exit(1)
//   }
// pipeline.Run()
func NewPipeline(source *Node, emitter events.Emitter, interval time.Duration, sessionStore state.SessionStore, sessionInterval time.Duration) (*Pipeline, error) {
	pipeline := &Pipeline{
		source:        source,
		emitter:       emitter,
		metricsTicker: time.NewTicker(interval),
	}

	if sessionStore != nil {
		pipeline.sessionStore = sessionStore
		pipeline.sessionTicker = time.NewTicker(sessionInterval)
	}

	// init the pipeline
	err := pipeline.source.Init(interval)
	if err != nil {
		return pipeline, err
	}

	// init the emitter with the right chan
	pipeline.emitter.Init(source.pipe.Event)

	// start the emitters
	go pipeline.startErrorListener(source.pipe.Err)
	go pipeline.startMetricsGatherer()
	if sessionStore != nil {
		go pipeline.startStateSaver()
	}
	pipeline.emitter.Start()

	pipeline.initState()

	return pipeline, nil
}

func (pipeline *Pipeline) String() string {
	out := pipeline.source.String()
	return out
}

// Stop sends a stop signal to the emitter and all the nodes, whether they are running or not.
// the node's database adaptors are expected to clean up after themselves, and stop will block until
// all nodes have stopped successfully
func (pipeline *Pipeline) Stop() {
	pipeline.source.Stop()
	pipeline.emitter.Stop()
	if pipeline.sessionStore != nil {
		pipeline.sessionTicker.Stop()
	}
	pipeline.metricsTicker.Stop()
}

// Run the pipeline
func (pipeline *Pipeline) Run() error {
	endpoints := pipeline.source.Endpoints()
	// send a boot event
	pipeline.source.pipe.Event <- events.NewBootEvent(time.Now().Unix(), VERSION, endpoints)

	// start the source
	err := pipeline.source.Start()
	if err != nil && pipeline.Err == nil {
		pipeline.Err = err // only set it if it hasn't been set already.
	}

	// pipeline has stopped, emit one last round of metrics and send the exit event
	pipeline.emitMetrics()
	pipeline.setState()
	pipeline.source.pipe.Event <- events.NewExitEvent(time.Now().Unix(), VERSION, endpoints)

	// the source has exited, stop all the other nodes
	pipeline.Stop()

	return pipeline.Err
}

// start error listener consumes all the events on the pipe's Err channel, and stops the pipeline
// when it receives one
func (pipeline *Pipeline) startErrorListener(cherr chan error) {
	for err := range cherr {
		if aerr, ok := err.(adaptor.Error); ok {
			pipeline.source.pipe.Event <- events.NewErrorEvent(time.Now().Unix(), aerr.Path, aerr.Record, aerr.Error())
			if aerr.Lvl == adaptor.ERROR || aerr.Lvl == adaptor.CRITICAL {
				log.Println(aerr)
			}
		} else {
			if pipeline.Err == nil {
				pipeline.Err = err
			}
			pipeline.Stop()
		}
	}
}

func (pipeline *Pipeline) startMetricsGatherer() {
	for _ = range pipeline.metricsTicker.C {
		pipeline.emitMetrics()
	}
}

// emit the metrics
func (pipeline *Pipeline) emitMetrics() {

	frontier := make([]*Node, 1)
	frontier[0] = pipeline.source

	for {
		// pop the first item
		node := frontier[0]
		frontier = frontier[1:]

		// do something with the node
		pipeline.source.pipe.Event <- events.NewMetricsEvent(time.Now().Unix(), node.Path(), node.pipe.MessageCount)

		// add this nodes children to the frontier
		for _, child := range node.Children {
			frontier = append(frontier, child)
		}

		// if we're empty
		if len(frontier) == 0 {
			break
		}
	}
}

func (pipeline *Pipeline) startStateSaver() {
	for _ = range pipeline.sessionTicker.C {
		pipeline.setState()
	}
}

func (pipeline *Pipeline) setState() {
	frontier := make([]*Node, 1)
	frontier[0] = pipeline.source

	for {
		// pop the first item
		node := frontier[0]
		frontier = frontier[1:]

		// do something with the node
		if node.Type != "transformer" {
			pipeline.sessionStore.Set(node.Path(), &state.MsgState{Msg: node.pipe.LastMsg, Extra: node.pipe.ExtraState})
		}

		// add this nodes children to the frontier
		for _, child := range node.Children {
			frontier = append(frontier, child)
		}

		// if we're empty
		if len(frontier) == 0 {
			break
		}
	}
}

func (pipeline *Pipeline) initState() {
	frontier := make([]*Node, 1)
	frontier[0] = pipeline.source

	for {
		// pop the first item
		node := frontier[0]
		frontier = frontier[1:]

		// do something with the node
		if node.Type != "transformer" {
			nodeState, _ := pipeline.sessionStore.Get(node.Path())
			if nodeState != nil {
				node.pipe.LastMsg = nodeState.Msg
				node.pipe.ExtraState = nodeState.Extra
			}
		}

		// add this nodes children to the frontier
		for _, child := range node.Children {
			frontier = append(frontier, child)
		}

		// if we're empty
		if len(frontier) == 0 {
			break
		}
	}
}
<commit_msg>dont store state unless LastMsg isnt nil<commit_after>package transporter

import (
	"log"
	"time"

	"github.com/compose/transporter/pkg/adaptor"
	"github.com/compose/transporter/pkg/events"
	"github.com/compose/transporter/pkg/state"
)

// VERSION the library
const (
	VERSION = "0.0.2"
)

// A Pipeline is a the end to end description of a transporter data flow.
// including the source, sink, and all the transformers along the way
type Pipeline struct {
	source        *Node
	emitter       events.Emitter
	sessionStore  state.SessionStore
	metricsTicker *time.Ticker

	// Err is the fatal error that was sent from the adaptor
	// that caused us to stop this process.  If this is nil, then
	// the transporter is running
	Err           error
	sessionTicker *time.Ticker
}

// NewDefaultPipeline returns a new Transporter Pipeline with the given node tree, and
// uses the events.HttpPostEmitter to deliver metrics.
// eg.
//   source :=
//   	transporter.NewNode("source", "mongo", adaptor.Config{"uri": "mongodb://localhost/", "namespace": "boom.foo", "debug": false, "tail": true}).
// 	  	Add(transporter.NewNode("out", "file", adaptor.Config{"uri": "stdout://"}))
//   pipeline, err := transporter.NewDefaultPipeline(source, events.Api{URI: "http://localhost/endpoint"}, 1*time.Second)
//   if err != nil {
// 	  fmt.Println(err)
// 	  os.Exit(1)
//   }
// pipeline.Run()
func NewDefaultPipeline(source *Node, uri, key, pid string, interval time.Duration) (*Pipeline, error) {
	emitter := events.NewHTTPPostEmitter(uri, key, pid)
	sessionStore := state.NewFilestore(pid, "/tmp/transporter.state")
	return NewPipeline(source, emitter, interval, sessionStore, 10*time.Second)
}

// NewPipeline creates a new Transporter Pipeline using the given tree of nodes, and Event Emitter
// eg.
//   source :=
//   	transporter.NewNode("source", "mongo", adaptor.Config{"uri": "mongodb://localhost/", "namespace": "boom.foo", "debug": false, "tail": true}).
// 	  	Add(transporter.NewNode("out", "file", adaptor.Config{"uri": "stdout://"}))
//   pipeline, err := transporter.NewPipeline(source, events.NewNoopEmitter(), 1*time.Second, state.NewFilestore(pid, "/tmp/transporter.state"), 10*time.Second)
//   if err != nil {
// 	  fmt.Println(err)
// 	  os.Exit(1)
//   }
// pipeline.Run()
func NewPipeline(source *Node, emitter events.Emitter, interval time.Duration, sessionStore state.SessionStore, sessionInterval time.Duration) (*Pipeline, error) {
	pipeline := &Pipeline{
		source:        source,
		emitter:       emitter,
		metricsTicker: time.NewTicker(interval),
	}

	if sessionStore != nil {
		pipeline.sessionStore = sessionStore
		pipeline.sessionTicker = time.NewTicker(sessionInterval)
	}

	// init the pipeline
	err := pipeline.source.Init(interval)
	if err != nil {
		return pipeline, err
	}

	// init the emitter with the right chan
	pipeline.emitter.Init(source.pipe.Event)

	// start the emitters
	go pipeline.startErrorListener(source.pipe.Err)
	go pipeline.startMetricsGatherer()
	if sessionStore != nil {
		go pipeline.startStateSaver()
	}
	pipeline.emitter.Start()

	pipeline.initState()

	return pipeline, nil
}

func (pipeline *Pipeline) String() string {
	out := pipeline.source.String()
	return out
}

// Stop sends a stop signal to the emitter and all the nodes, whether they are running or not.
// the node's database adaptors are expected to clean up after themselves, and stop will block until
// all nodes have stopped successfully
func (pipeline *Pipeline) Stop() {
	pipeline.source.Stop()
	pipeline.emitter.Stop()
	if pipeline.sessionStore != nil {
		pipeline.sessionTicker.Stop()
	}
	pipeline.metricsTicker.Stop()
}

// Run the pipeline
func (pipeline *Pipeline) Run() error {
	endpoints := pipeline.source.Endpoints()
	// send a boot event
	pipeline.source.pipe.Event <- events.NewBootEvent(time.Now().Unix(), VERSION, endpoints)

	// start the source
	err := pipeline.source.Start()
	if err != nil && pipeline.Err == nil {
		pipeline.Err = err // only set it if it hasn't been set already.
	}

	// pipeline has stopped, emit one last round of metrics and send the exit event
	pipeline.emitMetrics()
	pipeline.setState()
	pipeline.source.pipe.Event <- events.NewExitEvent(time.Now().Unix(), VERSION, endpoints)

	// the source has exited, stop all the other nodes
	pipeline.Stop()

	return pipeline.Err
}

// start error listener consumes all the events on the pipe's Err channel, and stops the pipeline
// when it receives one
func (pipeline *Pipeline) startErrorListener(cherr chan error) {
	for err := range cherr {
		if aerr, ok := err.(adaptor.Error); ok {
			pipeline.source.pipe.Event <- events.NewErrorEvent(time.Now().Unix(), aerr.Path, aerr.Record, aerr.Error())
			if aerr.Lvl == adaptor.ERROR || aerr.Lvl == adaptor.CRITICAL {
				log.Println(aerr)
			}
		} else {
			if pipeline.Err == nil {
				pipeline.Err = err
			}
			pipeline.Stop()
		}
	}
}

func (pipeline *Pipeline) startMetricsGatherer() {
	for _ = range pipeline.metricsTicker.C {
		pipeline.emitMetrics()
	}
}

// emit the metrics
func (pipeline *Pipeline) emitMetrics() {

	frontier := make([]*Node, 1)
	frontier[0] = pipeline.source

	for {
		// pop the first item
		node := frontier[0]
		frontier = frontier[1:]

		// do something with the node
		pipeline.source.pipe.Event <- events.NewMetricsEvent(time.Now().Unix(), node.Path(), node.pipe.MessageCount)

		// add this nodes children to the frontier
		for _, child := range node.Children {
			frontier = append(frontier, child)
		}

		// if we're empty
		if len(frontier) == 0 {
			break
		}
	}
}

func (pipeline *Pipeline) startStateSaver() {
	for _ = range pipeline.sessionTicker.C {
		pipeline.setState()
	}
}

func (pipeline *Pipeline) setState() {
	frontier := make([]*Node, 1)
	frontier[0] = pipeline.source

	for {
		// pop the first item
		node := frontier[0]
		frontier = frontier[1:]

		// do something with the node
		if node.Type != "transformer" && node.pipe.LastMsg != nil {
			pipeline.sessionStore.Set(node.Path(), &state.MsgState{Msg: node.pipe.LastMsg, Extra: node.pipe.ExtraState})
		}

		// add this nodes children to the frontier
		for _, child := range node.Children {
			frontier = append(frontier, child)
		}

		// if we're empty
		if len(frontier) == 0 {
			break
		}
	}
}

func (pipeline *Pipeline) initState() {
	frontier := make([]*Node, 1)
	frontier[0] = pipeline.source

	for {
		// pop the first item
		node := frontier[0]
		frontier = frontier[1:]

		// do something with the node
		if node.Type != "transformer" {
			nodeState, _ := pipeline.sessionStore.Get(node.Path())
			if nodeState != nil {
				node.pipe.LastMsg = nodeState.Msg
				node.pipe.ExtraState = nodeState.Extra
			}
		}

		// add this nodes children to the frontier
		for _, child := range node.Children {
			frontier = append(frontier, child)
		}

		// if we're empty
		if len(frontier) == 0 {
			break
		}
	}
}
<|endoftext|>


<commit_before>package relay

import (
	"time"

	"gopkg.in/dedis/onet.v1/log"
	"strconv"
)

/*
This first timeout happens after a short delay. Clients will not be considered disconnected yet,
but if we use UDP, it can mean that a client missed a broadcast, and we re-sent the message.
If the round was *not* done, we do another timeout (Phase 2), and then, clients/trustees will be considered
online if they didn't answer by that time.
*/
func (p *PriFiLibRelayInstance) checkIfRoundHasEndedAfterTimeOut_Phase1(roundID int32) {

	time.Sleep(TIMEOUT_PHASE_1)

	if !p.relayState.currentDCNetRound.isStillInRound(roundID) {
		return //everything went well, it's great !
	}

	if p.stateMachine.State() == "SHUTDOWN" {
		return //nothing to ensure in that case
	}

	log.Error("waitAndCheckIfClientsSentData : We seem to be stuck in round", roundID, ". Phase 1 timeout.")

	missingClientCiphers, missingTrusteesCiphers := p.relayState.bufferManager.MissingCiphersForCurrentRound()

	//If we're using UDP, client might have missed the broadcast, re-sending
	if p.relayState.UseUDP {
		for clientID := range missingClientCiphers {
			log.Error("Relay : Client " + strconv.Itoa(clientID) + " didn't sent us is cipher for round " + strconv.Itoa(int(roundID)) + ". Phase 1 timeout. Re-sending...")
			extraInfo := "(client " + strconv.Itoa(clientID) + ", round " + strconv.Itoa(int(p.relayState.currentDCNetRound.CurrentRound())) + ")"
			p.messageSender.SendToClientWithLog(clientID, p.relayState.currentDCNetRound.GetDataAlreadySent(), extraInfo)
		}
	}

	if len(missingClientCiphers) > 0 || len(missingTrusteesCiphers) > 0 {
		//if we're not done (we miss data), wait another timeout, after which clients/trustees will be considered offline
		go p.checkIfRoundHasEndedAfterTimeOut_Phase2(roundID)
	}

	//this shouldn't happen frequently (it means that the timeout 1 was fired, but the round finished almost at the same time)
}

/*
This second timeout happens after a longer delay. Clients and trustees will be considered offline if they haven't send data yet
*/
func (p *PriFiLibRelayInstance) checkIfRoundHasEndedAfterTimeOut_Phase2(roundID int32) {

	time.Sleep(TIMEOUT_PHASE_2)

	if !p.relayState.currentDCNetRound.isStillInRound(roundID) {
		//everything went well, it's great !
		return
	}

	if p.stateMachine.State() == "SHUTDOWN" {
		//nothing to ensure in that case
		return
	}

	log.Error("waitAndCheckIfClientsSentData : We seem to be stuck in round", roundID, ". Phase 2 timeout.")

	log.Lvl3("Stopping experiment, if any.")
	output := make([]string, 1)
	output[0] = "aborted-round-" + strconv.Itoa(int(roundID))
	p.relayState.ExperimentResultChannel <- output

	missingClientCiphers, missingTrusteesCiphers := p.relayState.bufferManager.MissingCiphersForCurrentRound()
	p.relayState.timeoutHandler(missingClientCiphers, missingTrusteesCiphers)
}
<commit_msg>When timeout2 happens, sends also the partial simulation results<commit_after>package relay

import (
	"time"

	"gopkg.in/dedis/onet.v1/log"
	"strconv"
)

/*
This first timeout happens after a short delay. Clients will not be considered disconnected yet,
but if we use UDP, it can mean that a client missed a broadcast, and we re-sent the message.
If the round was *not* done, we do another timeout (Phase 2), and then, clients/trustees will be considered
online if they didn't answer by that time.
*/
func (p *PriFiLibRelayInstance) checkIfRoundHasEndedAfterTimeOut_Phase1(roundID int32) {

	time.Sleep(TIMEOUT_PHASE_1)

	if !p.relayState.currentDCNetRound.isStillInRound(roundID) {
		return //everything went well, it's great !
	}

	if p.stateMachine.State() == "SHUTDOWN" {
		return //nothing to ensure in that case
	}

	log.Error("waitAndCheckIfClientsSentData : We seem to be stuck in round", roundID, ". Phase 1 timeout.")

	missingClientCiphers, missingTrusteesCiphers := p.relayState.bufferManager.MissingCiphersForCurrentRound()

	//If we're using UDP, client might have missed the broadcast, re-sending
	if p.relayState.UseUDP {
		for clientID := range missingClientCiphers {
			log.Error("Relay : Client " + strconv.Itoa(clientID) + " didn't sent us is cipher for round " + strconv.Itoa(int(roundID)) + ". Phase 1 timeout. Re-sending...")
			extraInfo := "(client " + strconv.Itoa(clientID) + ", round " + strconv.Itoa(int(p.relayState.currentDCNetRound.CurrentRound())) + ")"
			p.messageSender.SendToClientWithLog(clientID, p.relayState.currentDCNetRound.GetDataAlreadySent(), extraInfo)
		}
	}

	if len(missingClientCiphers) > 0 || len(missingTrusteesCiphers) > 0 {
		//if we're not done (we miss data), wait another timeout, after which clients/trustees will be considered offline
		go p.checkIfRoundHasEndedAfterTimeOut_Phase2(roundID)
	}

	//this shouldn't happen frequently (it means that the timeout 1 was fired, but the round finished almost at the same time)
}

/*
This second timeout happens after a longer delay. Clients and trustees will be considered offline if they haven't send data yet
*/
func (p *PriFiLibRelayInstance) checkIfRoundHasEndedAfterTimeOut_Phase2(roundID int32) {

	time.Sleep(TIMEOUT_PHASE_2)

	if !p.relayState.currentDCNetRound.isStillInRound(roundID) {
		//everything went well, it's great !
		return
	}

	if p.stateMachine.State() == "SHUTDOWN" {
		//nothing to ensure in that case
		return
	}

	log.Error("waitAndCheckIfClientsSentData : We seem to be stuck in round", roundID, ". Phase 2 timeout.")

	log.Lvl3("Stopping experiment, if any.")
	output := p.relayState.ExperimentResultData
	output = append(output, "!!aborted-round-"+strconv.Itoa(int(roundID)))
	p.relayState.ExperimentResultChannel <- output

	missingClientCiphers, missingTrusteesCiphers := p.relayState.bufferManager.MissingCiphersForCurrentRound()
	p.relayState.timeoutHandler(missingClientCiphers, missingTrusteesCiphers)
}
<|endoftext|>