<commit_before>/*
Copyright 2018 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 fake

import (
	"strings"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/testing"
)

func NewSimpleDynamicClient(scheme *runtime.Scheme, objects ...runtime.Object) *FakeDynamicClient {
	// In order to use List with this client, you have to have the v1.List registered in your scheme. Neat thing though
	// it does NOT have to be the *same* list
	scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "List"}, &unstructured.UnstructuredList{})

	codecs := serializer.NewCodecFactory(scheme)
	o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
	for _, obj := range objects {
		if err := o.Add(obj); err != nil {
			panic(err)
		}
	}

	cs := &FakeDynamicClient{}
	cs.AddReactor("*", "*", testing.ObjectReaction(o))
	cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
		gvr := action.GetResource()
		ns := action.GetNamespace()
		watch, err := o.Watch(gvr, ns)
		if err != nil {
			return false, nil, err
		}
		return true, watch, nil
	})

	return cs
}

// Clientset implements clientset.Interface. Meant to be embedded into a
// struct to get a default implementation. This makes faking out just the method
// you want to test easier.
type FakeDynamicClient struct {
	testing.Fake
	scheme *runtime.Scheme
}

type dynamicResourceClient struct {
	client    *FakeDynamicClient
	namespace string
	resource  schema.GroupVersionResource
}

var _ dynamic.Interface = &FakeDynamicClient{}

func (c *FakeDynamicClient) Resource(resource schema.GroupVersionResource) dynamic.NamespaceableResourceInterface {
	return &dynamicResourceClient{client: c, resource: resource}
}

func (c *dynamicResourceClient) Namespace(ns string) dynamic.ResourceInterface {
	ret := *c
	ret.namespace = ns
	return &ret
}

func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured, opts metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
	var uncastRet runtime.Object
	var err error
	switch {
	case len(c.namespace) == 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootCreateAction(c.resource, obj), obj)

	case len(c.namespace) == 0 && len(subresources) > 0:
		accessor, err := meta.Accessor(obj)
		if err != nil {
			return nil, err
		}
		name := accessor.GetName()
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), obj), obj)

	case len(c.namespace) > 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewCreateAction(c.resource, c.namespace, obj), obj)

	case len(c.namespace) > 0 && len(subresources) > 0:
		accessor, err := meta.Accessor(obj)
		if err != nil {
			return nil, err
		}
		name := accessor.GetName()
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), c.namespace, obj), obj)

	}

	if err != nil {
		return nil, err
	}
	if uncastRet == nil {
		return nil, err
	}

	ret := &unstructured.Unstructured{}
	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
		return nil, err
	}
	return ret, err
}

func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured, opts metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
	var uncastRet runtime.Object
	var err error
	switch {
	case len(c.namespace) == 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootUpdateAction(c.resource, obj), obj)

	case len(c.namespace) == 0 && len(subresources) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), obj), obj)

	case len(c.namespace) > 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewUpdateAction(c.resource, c.namespace, obj), obj)

	case len(c.namespace) > 0 && len(subresources) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, obj), obj)

	}

	if err != nil {
		return nil, err
	}
	if uncastRet == nil {
		return nil, err
	}

	ret := &unstructured.Unstructured{}
	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
		return nil, err
	}
	return ret, err
}

func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error) {
	var uncastRet runtime.Object
	var err error
	switch {
	case len(c.namespace) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootUpdateSubresourceAction(c.resource, "status", obj), obj)

	case len(c.namespace) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewUpdateSubresourceAction(c.resource, "status", c.namespace, obj), obj)

	}

	if err != nil {
		return nil, err
	}
	if uncastRet == nil {
		return nil, err
	}

	ret := &unstructured.Unstructured{}
	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
		return nil, err
	}
	return ret, err
}

func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions, subresources ...string) error {
	var err error
	switch {
	case len(c.namespace) == 0 && len(subresources) == 0:
		_, err = c.client.Fake.
			Invokes(testing.NewRootDeleteAction(c.resource, name), &metav1.Status{Status: "dynamic delete fail"})

	case len(c.namespace) == 0 && len(subresources) > 0:
		_, err = c.client.Fake.
			Invokes(testing.NewRootDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic delete fail"})

	case len(c.namespace) > 0 && len(subresources) == 0:
		_, err = c.client.Fake.
			Invokes(testing.NewDeleteAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic delete fail"})

	case len(c.namespace) > 0 && len(subresources) > 0:
		_, err = c.client.Fake.
			Invokes(testing.NewDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, name), &metav1.Status{Status: "dynamic delete fail"})
	}

	return err
}

func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
	var err error
	switch {
	case len(c.namespace) == 0:
		action := testing.NewRootDeleteCollectionAction(c.resource, listOptions)
		_, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "dynamic deletecollection fail"})

	case len(c.namespace) > 0:
		action := testing.NewDeleteCollectionAction(c.resource, c.namespace, listOptions)
		_, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "dynamic deletecollection fail"})

	}

	return err
}

func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
	var uncastRet runtime.Object
	var err error
	switch {
	case len(c.namespace) == 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootGetAction(c.resource, name), &metav1.Status{Status: "dynamic get fail"})

	case len(c.namespace) == 0 && len(subresources) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootGetSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic get fail"})

	case len(c.namespace) > 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewGetAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic get fail"})

	case len(c.namespace) > 0 && len(subresources) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewGetSubresourceAction(c.resource, c.namespace, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic get fail"})
	}

	if err != nil {
		return nil, err
	}
	if uncastRet == nil {
		return nil, err
	}

	ret := &unstructured.Unstructured{}
	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
		return nil, err
	}
	return ret, err
}

func (c *dynamicResourceClient) List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
	var obj runtime.Object
	var err error
	switch {
	case len(c.namespace) == 0:
		obj, err = c.client.Fake.
			Invokes(testing.NewRootListAction(c.resource, schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, opts), &metav1.Status{Status: "dynamic list fail"})

	case len(c.namespace) > 0:
		obj, err = c.client.Fake.
			Invokes(testing.NewListAction(c.resource, schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, c.namespace, opts), &metav1.Status{Status: "dynamic list fail"})

	}

	if obj == nil {
		return nil, err
	}

	label, _, _ := testing.ExtractFromListOptions(opts)
	if label == nil {
		label = labels.Everything()
	}

	retUnstructured := &unstructured.Unstructured{}
	if err := c.client.scheme.Convert(obj, retUnstructured, nil); err != nil {
		return nil, err
	}
	entireList, err := retUnstructured.ToList()
	if err != nil {
		return nil, err
	}

	list := &unstructured.UnstructuredList{}
	for i := range entireList.Items {
		item := &entireList.Items[i]
		metadata, err := meta.Accessor(item)
		if err != nil {
			return nil, err
		}
		if label.Matches(labels.Set(metadata.GetLabels())) {
			list.Items = append(list.Items, *item)
		}
	}
	return list, nil
}

func (c *dynamicResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
	switch {
	case len(c.namespace) == 0:
		return c.client.Fake.
			InvokesWatch(testing.NewRootWatchAction(c.resource, opts))

	case len(c.namespace) > 0:
		return c.client.Fake.
			InvokesWatch(testing.NewWatchAction(c.resource, c.namespace, opts))

	}

	panic("math broke")
}

func (c *dynamicResourceClient) Patch(name string, pt types.PatchType, data []byte, opts metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
	var uncastRet runtime.Object
	var err error
	switch {
	case len(c.namespace) == 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootPatchAction(c.resource, name, data), &metav1.Status{Status: "dynamic patch fail"})

	case len(c.namespace) == 0 && len(subresources) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootPatchSubresourceAction(c.resource, name, data, subresources...), &metav1.Status{Status: "dynamic patch fail"})

	case len(c.namespace) > 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewPatchAction(c.resource, c.namespace, name, data), &metav1.Status{Status: "dynamic patch fail"})

	case len(c.namespace) > 0 && len(subresources) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewPatchSubresourceAction(c.resource, c.namespace, name, data, subresources...), &metav1.Status{Status: "dynamic patch fail"})

	}

	if err != nil {
		return nil, err
	}
	if uncastRet == nil {
		return nil, err
	}

	ret := &unstructured.Unstructured{}
	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
		return nil, err
	}
	return ret, err
}
<commit_msg>Populates the fake dynamic client scheme<commit_after>/*
Copyright 2018 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 fake

import (
	"strings"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/testing"
)

func NewSimpleDynamicClient(scheme *runtime.Scheme, objects ...runtime.Object) *FakeDynamicClient {
	// In order to use List with this client, you have to have the v1.List registered in your scheme. Neat thing though
	// it does NOT have to be the *same* list
	scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "List"}, &unstructured.UnstructuredList{})

	codecs := serializer.NewCodecFactory(scheme)
	o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
	for _, obj := range objects {
		if err := o.Add(obj); err != nil {
			panic(err)
		}
	}

	cs := &FakeDynamicClient{scheme: scheme}
	cs.AddReactor("*", "*", testing.ObjectReaction(o))
	cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
		gvr := action.GetResource()
		ns := action.GetNamespace()
		watch, err := o.Watch(gvr, ns)
		if err != nil {
			return false, nil, err
		}
		return true, watch, nil
	})

	return cs
}

// Clientset implements clientset.Interface. Meant to be embedded into a
// struct to get a default implementation. This makes faking out just the method
// you want to test easier.
type FakeDynamicClient struct {
	testing.Fake
	scheme *runtime.Scheme
}

type dynamicResourceClient struct {
	client    *FakeDynamicClient
	namespace string
	resource  schema.GroupVersionResource
}

var _ dynamic.Interface = &FakeDynamicClient{}

func (c *FakeDynamicClient) Resource(resource schema.GroupVersionResource) dynamic.NamespaceableResourceInterface {
	return &dynamicResourceClient{client: c, resource: resource}
}

func (c *dynamicResourceClient) Namespace(ns string) dynamic.ResourceInterface {
	ret := *c
	ret.namespace = ns
	return &ret
}

func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured, opts metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
	var uncastRet runtime.Object
	var err error
	switch {
	case len(c.namespace) == 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootCreateAction(c.resource, obj), obj)

	case len(c.namespace) == 0 && len(subresources) > 0:
		accessor, err := meta.Accessor(obj)
		if err != nil {
			return nil, err
		}
		name := accessor.GetName()
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), obj), obj)

	case len(c.namespace) > 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewCreateAction(c.resource, c.namespace, obj), obj)

	case len(c.namespace) > 0 && len(subresources) > 0:
		accessor, err := meta.Accessor(obj)
		if err != nil {
			return nil, err
		}
		name := accessor.GetName()
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), c.namespace, obj), obj)

	}

	if err != nil {
		return nil, err
	}
	if uncastRet == nil {
		return nil, err
	}

	ret := &unstructured.Unstructured{}
	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
		return nil, err
	}
	return ret, err
}

func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured, opts metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
	var uncastRet runtime.Object
	var err error
	switch {
	case len(c.namespace) == 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootUpdateAction(c.resource, obj), obj)

	case len(c.namespace) == 0 && len(subresources) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), obj), obj)

	case len(c.namespace) > 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewUpdateAction(c.resource, c.namespace, obj), obj)

	case len(c.namespace) > 0 && len(subresources) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, obj), obj)

	}

	if err != nil {
		return nil, err
	}
	if uncastRet == nil {
		return nil, err
	}

	ret := &unstructured.Unstructured{}
	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
		return nil, err
	}
	return ret, err
}

func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error) {
	var uncastRet runtime.Object
	var err error
	switch {
	case len(c.namespace) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootUpdateSubresourceAction(c.resource, "status", obj), obj)

	case len(c.namespace) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewUpdateSubresourceAction(c.resource, "status", c.namespace, obj), obj)

	}

	if err != nil {
		return nil, err
	}
	if uncastRet == nil {
		return nil, err
	}

	ret := &unstructured.Unstructured{}
	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
		return nil, err
	}
	return ret, err
}

func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions, subresources ...string) error {
	var err error
	switch {
	case len(c.namespace) == 0 && len(subresources) == 0:
		_, err = c.client.Fake.
			Invokes(testing.NewRootDeleteAction(c.resource, name), &metav1.Status{Status: "dynamic delete fail"})

	case len(c.namespace) == 0 && len(subresources) > 0:
		_, err = c.client.Fake.
			Invokes(testing.NewRootDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic delete fail"})

	case len(c.namespace) > 0 && len(subresources) == 0:
		_, err = c.client.Fake.
			Invokes(testing.NewDeleteAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic delete fail"})

	case len(c.namespace) > 0 && len(subresources) > 0:
		_, err = c.client.Fake.
			Invokes(testing.NewDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, name), &metav1.Status{Status: "dynamic delete fail"})
	}

	return err
}

func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
	var err error
	switch {
	case len(c.namespace) == 0:
		action := testing.NewRootDeleteCollectionAction(c.resource, listOptions)
		_, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "dynamic deletecollection fail"})

	case len(c.namespace) > 0:
		action := testing.NewDeleteCollectionAction(c.resource, c.namespace, listOptions)
		_, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "dynamic deletecollection fail"})

	}

	return err
}

func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
	var uncastRet runtime.Object
	var err error
	switch {
	case len(c.namespace) == 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootGetAction(c.resource, name), &metav1.Status{Status: "dynamic get fail"})

	case len(c.namespace) == 0 && len(subresources) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootGetSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic get fail"})

	case len(c.namespace) > 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewGetAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic get fail"})

	case len(c.namespace) > 0 && len(subresources) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewGetSubresourceAction(c.resource, c.namespace, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic get fail"})
	}

	if err != nil {
		return nil, err
	}
	if uncastRet == nil {
		return nil, err
	}

	ret := &unstructured.Unstructured{}
	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
		return nil, err
	}
	return ret, err
}

func (c *dynamicResourceClient) List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
	var obj runtime.Object
	var err error
	switch {
	case len(c.namespace) == 0:
		obj, err = c.client.Fake.
			Invokes(testing.NewRootListAction(c.resource, schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, opts), &metav1.Status{Status: "dynamic list fail"})

	case len(c.namespace) > 0:
		obj, err = c.client.Fake.
			Invokes(testing.NewListAction(c.resource, schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, c.namespace, opts), &metav1.Status{Status: "dynamic list fail"})

	}

	if obj == nil {
		return nil, err
	}

	label, _, _ := testing.ExtractFromListOptions(opts)
	if label == nil {
		label = labels.Everything()
	}

	retUnstructured := &unstructured.Unstructured{}
	if err := c.client.scheme.Convert(obj, retUnstructured, nil); err != nil {
		return nil, err
	}
	entireList, err := retUnstructured.ToList()
	if err != nil {
		return nil, err
	}

	list := &unstructured.UnstructuredList{}
	for i := range entireList.Items {
		item := &entireList.Items[i]
		metadata, err := meta.Accessor(item)
		if err != nil {
			return nil, err
		}
		if label.Matches(labels.Set(metadata.GetLabels())) {
			list.Items = append(list.Items, *item)
		}
	}
	return list, nil
}

func (c *dynamicResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
	switch {
	case len(c.namespace) == 0:
		return c.client.Fake.
			InvokesWatch(testing.NewRootWatchAction(c.resource, opts))

	case len(c.namespace) > 0:
		return c.client.Fake.
			InvokesWatch(testing.NewWatchAction(c.resource, c.namespace, opts))

	}

	panic("math broke")
}

func (c *dynamicResourceClient) Patch(name string, pt types.PatchType, data []byte, opts metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
	var uncastRet runtime.Object
	var err error
	switch {
	case len(c.namespace) == 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootPatchAction(c.resource, name, data), &metav1.Status{Status: "dynamic patch fail"})

	case len(c.namespace) == 0 && len(subresources) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewRootPatchSubresourceAction(c.resource, name, data, subresources...), &metav1.Status{Status: "dynamic patch fail"})

	case len(c.namespace) > 0 && len(subresources) == 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewPatchAction(c.resource, c.namespace, name, data), &metav1.Status{Status: "dynamic patch fail"})

	case len(c.namespace) > 0 && len(subresources) > 0:
		uncastRet, err = c.client.Fake.
			Invokes(testing.NewPatchSubresourceAction(c.resource, c.namespace, name, data, subresources...), &metav1.Status{Status: "dynamic patch fail"})

	}

	if err != nil {
		return nil, err
	}
	if uncastRet == nil {
		return nil, err
	}

	ret := &unstructured.Unstructured{}
	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
		return nil, err
	}
	return ret, err
}
<|endoftext|>


<commit_before>// Copyright The OpenTelemetry 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 awsxrayreceiver

import (
	"context"
	"fmt"

	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/component/componenterror"
	"go.opentelemetry.io/collector/config"
	"go.opentelemetry.io/collector/consumer"
	"go.opentelemetry.io/collector/obsreport"
	"go.uber.org/zap"

	awsxray "github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/xray"
	"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxrayreceiver/internal/proxy"
	"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxrayreceiver/internal/translator"
	"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxrayreceiver/internal/udppoller"
)

const (
	// number of goroutines polling the UDP socket.
	// https://github.com/aws/aws-xray-daemon/blob/master/pkg/cfg/cfg.go#L184
	maxPollerCount = 2
)

// xrayReceiver implements the component.TracesReceiver interface for converting
// AWS X-Ray segment document into the OT internal trace format.
type xrayReceiver struct {
	instanceID   config.ComponentID
	poller       udppoller.Poller
	server       proxy.Server
	logger       *zap.Logger
	consumer     consumer.Traces
	longLivedCtx context.Context
	obsrecv      *obsreport.Receiver
}

func newReceiver(config *Config,
	consumer consumer.Traces,
	logger *zap.Logger) (component.TracesReceiver, error) {

	if consumer == nil {
		return nil, componenterror.ErrNilNextConsumer
	}

	logger.Info("Going to listen on endpoint for X-Ray segments",
		zap.String(udppoller.Transport, config.Endpoint))
	poller, err := udppoller.New(&udppoller.Config{
		ReceiverID:         config.ID(),
		Transport:          config.Transport,
		Endpoint:           config.Endpoint,
		NumOfPollerToStart: maxPollerCount,
	}, logger)
	if err != nil {
		return nil, err
	}

	logger.Info("Listening on endpoint for X-Ray segments",
		zap.String(udppoller.Transport, config.Endpoint))

	srv, err := proxy.NewServer(config.ProxyServer, logger)
	if err != nil {
		return nil, err
	}

	return &xrayReceiver{
		instanceID: config.ID(),
		poller:     poller,
		server:     srv,
		logger:     logger,
		consumer:   consumer,
		obsrecv:    obsreport.NewReceiver(obsreport.ReceiverSettings{ReceiverID: config.ID(), Transport: udppoller.Transport}),
	}, nil
}

func (x *xrayReceiver) Start(ctx context.Context, host component.Host) error {
	// TODO: Might want to pass `host` into read() below to report a fatal error
	x.longLivedCtx = obsreport.ReceiverContext(ctx, x.instanceID, udppoller.Transport)
	x.poller.Start(x.longLivedCtx)
	go x.start()
	go x.server.ListenAndServe()
	x.logger.Info("X-Ray TCP proxy server started")
	return nil
}

func (x *xrayReceiver) Shutdown(_ context.Context) error {
	var err error
	if pollerErr := x.poller.Close(); pollerErr != nil {
		err = pollerErr
	}

	if proxyErr := x.server.Close(); proxyErr != nil {
		if err == nil {
			err = proxyErr
		} else {
			err = fmt.Errorf("failed to close proxy: %s: failed to close poller: %s",
				proxyErr.Error(), err.Error())
		}
	}
	return err
}

func (x *xrayReceiver) start() {
	incomingSegments := x.poller.SegmentsChan()
	for seg := range incomingSegments {
		traces, totalSpansCount, err := translator.ToTraces(seg.Payload)
		if err != nil {
			x.logger.Warn("X-Ray segment to OT traces conversion failed", zap.Error(err))
			x.obsrecv.EndTracesOp(seg.Ctx, awsxray.TypeStr, totalSpansCount, err)
			continue
		}

		err = x.consumer.ConsumeTraces(seg.Ctx, *traces)
		if err != nil {
			x.logger.Warn("Trace consumer errored out", zap.Error(err))
			x.obsrecv.EndTracesOp(seg.Ctx, awsxray.TypeStr, totalSpansCount, err)
			continue
		}
		x.obsrecv.EndTracesOp(seg.Ctx, awsxray.TypeStr, totalSpansCount, nil)
	}
}
<commit_msg>Fix quick start bug within the awsxrayreceiver (#3653)<commit_after>// Copyright The OpenTelemetry 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 awsxrayreceiver

import (
	"context"
	"fmt"

	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/component/componenterror"
	"go.opentelemetry.io/collector/config"
	"go.opentelemetry.io/collector/consumer"
	"go.opentelemetry.io/collector/obsreport"
	"go.uber.org/zap"

	awsxray "github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/xray"
	"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxrayreceiver/internal/proxy"
	"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxrayreceiver/internal/translator"
	"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awsxrayreceiver/internal/udppoller"
)

const (
	// number of goroutines polling the UDP socket.
	// https://github.com/aws/aws-xray-daemon/blob/master/pkg/cfg/cfg.go#L184
	maxPollerCount = 2
)

// xrayReceiver implements the component.TracesReceiver interface for converting
// AWS X-Ray segment document into the OT internal trace format.
type xrayReceiver struct {
	instanceID   config.ComponentID
	poller       udppoller.Poller
	server       proxy.Server
	logger       *zap.Logger
	consumer     consumer.Traces
	longLivedCtx context.Context
	obsrecv      *obsreport.Receiver
}

func newReceiver(config *Config,
	consumer consumer.Traces,
	logger *zap.Logger) (component.TracesReceiver, error) {

	if consumer == nil {
		return nil, componenterror.ErrNilNextConsumer
	}

	logger.Info("Going to listen on endpoint for X-Ray segments",
		zap.String(udppoller.Transport, config.Endpoint))
	poller, err := udppoller.New(&udppoller.Config{
		ReceiverID:         config.ID(),
		Transport:          config.Transport,
		Endpoint:           config.Endpoint,
		NumOfPollerToStart: maxPollerCount,
	}, logger)
	if err != nil {
		return nil, err
	}

	logger.Info("Listening on endpoint for X-Ray segments",
		zap.String(udppoller.Transport, config.Endpoint))

	srv, err := proxy.NewServer(config.ProxyServer, logger)
	if err != nil {
		return nil, err
	}

	return &xrayReceiver{
		instanceID: config.ID(),
		poller:     poller,
		server:     srv,
		logger:     logger,
		consumer:   consumer,
		obsrecv:    obsreport.NewReceiver(obsreport.ReceiverSettings{ReceiverID: config.ID(), Transport: udppoller.Transport}),
	}, nil
}

func (x *xrayReceiver) Start(ctx context.Context, host component.Host) error {
	// TODO: Might want to pass `host` into read() below to report a fatal error
	x.longLivedCtx = obsreport.ReceiverContext(ctx, x.instanceID, udppoller.Transport)
	x.poller.Start(x.longLivedCtx)
	go x.start()
	go x.server.ListenAndServe()
	x.logger.Info("X-Ray TCP proxy server started")
	return nil
}

func (x *xrayReceiver) Shutdown(_ context.Context) error {
	var err error
	if pollerErr := x.poller.Close(); pollerErr != nil {
		err = pollerErr
	}

	if proxyErr := x.server.Close(); proxyErr != nil {
		if err == nil {
			err = proxyErr
		} else {
			err = fmt.Errorf("failed to close proxy: %s: failed to close poller: %s",
				proxyErr.Error(), err.Error())
		}
	}
	return err
}

func (x *xrayReceiver) start() {
	incomingSegments := x.poller.SegmentsChan()
	for seg := range incomingSegments {
		ctx := x.obsrecv.StartTracesOp(seg.Ctx)
		traces, totalSpansCount, err := translator.ToTraces(seg.Payload)
		if err != nil {
			x.logger.Warn("X-Ray segment to OT traces conversion failed", zap.Error(err))
			x.obsrecv.EndTracesOp(ctx, awsxray.TypeStr, totalSpansCount, err)
			continue
		}

		err = x.consumer.ConsumeTraces(ctx, *traces)
		if err != nil {
			x.logger.Warn("Trace consumer errored out", zap.Error(err))
			x.obsrecv.EndTracesOp(ctx, awsxray.TypeStr, totalSpansCount, err)
			continue
		}
		x.obsrecv.EndTracesOp(ctx, awsxray.TypeStr, totalSpansCount, nil)
	}
}
<|endoftext|>


<commit_before>package stringUtils

import "testing"

func TestIsEmpty(t *testing.T) {
	var notBlank string = "golang"
	actual := IsEmpty(notBlank)
	if actual == true {
		t.Errorf("fail test, not blank string should return false")
	}
	var blank string = ""
	actual = IsEmpty(blank)
	if actual == false {
		t.Errorf("fail test, black should empty")
	}
	var spaceIncludeText string = " golang "
	actual = IsEmpty(spaceIncludeText)
	if actual == true {
		t.Errorf("fail test, not blank string should return false")
	}
}

func TestIsNotEmpty(t *testing.T) {
	var notBlank string = "golang"
	actual := IsNotEmpty(notBlank)
	if actual == false {
		t.Errorf("fail test, not blank string should return true")
	}
	var blank string = ""
	actual = IsNotEmpty(blank)
	if actual == true {
		t.Errorf("fail test, black should return false")
	}
	var spaceIncludeText string = " golang "
	actual = IsNotEmpty(spaceIncludeText)
	if actual == false {
		t.Errorf("fail test, not blank string should return false")
	}
}

func TestIsAnyEmpty(t *testing.T) {
	actual := IsAnyEmpty("")
	if actual == false {
		t.Errorf("fail test, blank string should return true")
	}
	actual = IsAnyEmpty("", "golang")
	if actual == false {
		t.Errorf("fail test, blank string and not blank string should return true")
	}
	actual = IsAnyEmpty("golang", "")
	if actual == false {
		t.Errorf("fail test, not blank and blank string should return true")
	}
	actual = IsAnyEmpty(" golang ", "")
	if actual == false {
		t.Errorf("fail test, not blank and blank string should return true")
	}
	actual = IsAnyEmpty(" ", "golang")
	if actual {
		t.Errorf("fail test, not blank strings should return false")
	}
	actual = IsAnyEmpty("golang", "gophers")
	if actual {
		t.Errorf("fail test, not blank strings should return false")
	}
}

func TestIsNoneEmpty(t *testing.T) {
	actual := IsNoneEmpty("")
	if actual == true {
		t.Errorf("fail test, blank string should return false")
	}
	actual = IsNoneEmpty("", "golang")
	if actual == true {
		t.Errorf("fail test, blank string and string should return false")
	}
	actual = IsNoneEmpty("golang", "")
	if actual == true {
		t.Errorf("fail test, string and blank string should return false")
	}
	actual = IsNoneEmpty(" golang ", "")
	if actual == true {
		t.Errorf("fail test, string and blank string should return false")
	}
	actual = IsNoneEmpty(" ", "golang")
	if actual == false {
		t.Errorf("fail test, strings  should return true")
	}
	actual = IsNoneEmpty("golang", "gophers")
	if actual == false {
		t.Errorf("fail test, strings should return true")
	}
}

func TestIsBlank(t *testing.T) {
	actual := IsBlank("")
	if actual == false {
		t.Errorf("fail test, blank string should return true")
	}
	actual = IsBlank(" ")
	if actual == false {
		t.Errorf("fail test, space string should return true")
	}
	actual = IsBlank("golang")
	if actual == true {
		t.Errorf("fail test, string should return false")
	}
	actual = IsBlank(" golang ")
	if actual == true {
		t.Errorf("fail test, string should return false")
	}
}
<commit_msg>add test code<commit_after>package stringUtils

import "testing"

func TestIsEmpty(t *testing.T) {
	var notBlank string = "golang"
	actual := IsEmpty(notBlank)
	if actual == true {
		t.Errorf("fail test, not blank string should return false")
	}
	var blank string = ""
	actual = IsEmpty(blank)
	if actual == false {
		t.Errorf("fail test, black should empty")
	}
	var spaceIncludeText string = " golang "
	actual = IsEmpty(spaceIncludeText)
	if actual == true {
		t.Errorf("fail test, not blank string should return false")
	}
}

func TestIsNotEmpty(t *testing.T) {
	var notBlank string = "golang"
	actual := IsNotEmpty(notBlank)
	if actual == false {
		t.Errorf("fail test, not blank string should return true")
	}
	var blank string = ""
	actual = IsNotEmpty(blank)
	if actual == true {
		t.Errorf("fail test, black should return false")
	}
	var spaceIncludeText string = " golang "
	actual = IsNotEmpty(spaceIncludeText)
	if actual == false {
		t.Errorf("fail test, not blank string should return false")
	}
}

func TestIsAnyEmpty(t *testing.T) {
	actual := IsAnyEmpty("")
	if actual == false {
		t.Errorf("fail test, blank string should return true")
	}
	actual = IsAnyEmpty("", "golang")
	if actual == false {
		t.Errorf("fail test, blank string and not blank string should return true")
	}
	actual = IsAnyEmpty("golang", "")
	if actual == false {
		t.Errorf("fail test, not blank and blank string should return true")
	}
	actual = IsAnyEmpty(" golang ", "")
	if actual == false {
		t.Errorf("fail test, not blank and blank string should return true")
	}
	actual = IsAnyEmpty(" ", "golang")
	if actual {
		t.Errorf("fail test, not blank strings should return false")
	}
	actual = IsAnyEmpty("golang", "gophers")
	if actual {
		t.Errorf("fail test, not blank strings should return false")
	}
}

func TestIsNoneEmpty(t *testing.T) {
	actual := IsNoneEmpty("")
	if actual == true {
		t.Errorf("fail test, blank string should return false")
	}
	actual = IsNoneEmpty("", "golang")
	if actual == true {
		t.Errorf("fail test, blank string and string should return false")
	}
	actual = IsNoneEmpty("golang", "")
	if actual == true {
		t.Errorf("fail test, string and blank string should return false")
	}
	actual = IsNoneEmpty(" golang ", "")
	if actual == true {
		t.Errorf("fail test, string and blank string should return false")
	}
	actual = IsNoneEmpty(" ", "golang")
	if actual == false {
		t.Errorf("fail test, strings  should return true")
	}
	actual = IsNoneEmpty("golang", "gophers")
	if actual == false {
		t.Errorf("fail test, strings should return true")
	}
}

func TestIsBlank(t *testing.T) {
	actual := IsBlank("")
	if actual == false {
		t.Errorf("fail test, blank string should return true")
	}
	actual = IsBlank(" ")
	if actual == false {
		t.Errorf("fail test, space string should return true")
	}
	actual = IsBlank("  ")
	if actual == false {
		t.Errorf("fail test, space strings should return true")
	}
	actual = IsBlank("golang")
	if actual == true {
		t.Errorf("fail test, string should return false")
	}
	actual = IsBlank(" golang ")
	if actual == true {
		t.Errorf("fail test, string should return false")
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package models

import (
	"bytes"
	"fmt"
	"hash/fnv"
	"sort"
	"strings"
)

var (
	defaultOptions = NewTagOptions()
)

// NewTags builds a tags with the given size and tag options.
func NewTags(size int, opts TagOptions) Tags {
	if opts == nil {
		opts = defaultOptions
	}

	return Tags{
		// Todo: Pool these
		Tags: make([]Tag, 0, size),
		Opts: opts,
	}
}

// EmptyTags returns empty tags with a default tag options.
func EmptyTags() Tags {
	return NewTags(0, nil)
}

// ID returns a string representation of the tags.
func (t Tags) ID() string {
	var (
		idLen      = t.IDLen()
		strBuilder = strings.Builder{}
	)

	strBuilder.Grow(idLen)
	for _, tag := range t.Tags {
		strBuilder.Write(tag.Name)
		strBuilder.WriteByte(eq)
		strBuilder.Write(tag.Value)
		strBuilder.WriteByte(sep)
	}

	return strBuilder.String()
}

// IDMarshalTo writes out the ID representation
// of the tags into the provided buffer.
func (t Tags) IDMarshalTo(b []byte) []byte {
	for _, tag := range t.Tags {
		b = append(b, tag.Name...)
		b = append(b, eq)
		b = append(b, tag.Value...)
		b = append(b, sep)
	}

	return b
}

// IDLen returns the length of the ID that would be
// generated from the tags.
func (t Tags) IDLen() int {
	idLen := 2 * t.Len() // account for eq and sep
	for _, tag := range t.Tags {
		idLen += len(tag.Name)
		idLen += len(tag.Value)
	}

	return idLen
}

// IDWithExcludes returns a string representation of the tags excluding some tag keys.
func (t Tags) IDWithExcludes(excludeKeys ...[]byte) uint64 {
	b := make([]byte, 0, t.Len())
	for _, tag := range t.Tags {
		// Always exclude the metric name by default
		if bytes.Equal(tag.Name, t.Opts.MetricName()) {
			continue
		}

		found := false
		for _, n := range excludeKeys {
			if bytes.Equal(n, tag.Name) {
				found = true
				break
			}
		}

		// Skip the key
		if found {
			continue
		}

		b = append(b, tag.Name...)
		b = append(b, eq)
		b = append(b, tag.Value...)
		b = append(b, sep)
	}

	h := fnv.New64a()
	h.Write(b)
	return h.Sum64()
}

func (t Tags) tagSubset(keys [][]byte, include bool) Tags {
	tags := NewTags(t.Len(), t.Opts)
	for _, tag := range t.Tags {
		found := false
		for _, k := range keys {
			if bytes.Equal(tag.Name, k) {
				found = true
				break
			}
		}

		if found == include {
			tags = tags.AddTag(tag)
		}
	}

	return tags
}

// TagsWithoutKeys returns only the tags which do not have the given keys.
func (t Tags) TagsWithoutKeys(excludeKeys [][]byte) Tags {
	return t.tagSubset(excludeKeys, false)
}

// IDWithKeys returns a string representation of the tags only including the given keys.
func (t Tags) IDWithKeys(includeKeys ...[]byte) uint64 {
	b := make([]byte, 0, t.Len())
	for _, tag := range t.Tags {
		for _, k := range includeKeys {
			if bytes.Equal(tag.Name, k) {
				b = append(b, tag.Name...)
				b = append(b, eq)
				b = append(b, tag.Value...)
				b = append(b, sep)
				break
			}
		}
	}

	h := fnv.New64a()
	h.Write(b)
	return h.Sum64()
}

// TagsWithKeys returns only the tags which have the given keys.
func (t Tags) TagsWithKeys(includeKeys [][]byte) Tags {
	return t.tagSubset(includeKeys, true)
}

// WithoutName copies the tags excluding the name tag.
func (t Tags) WithoutName() Tags {
	return t.TagsWithoutKeys([][]byte{t.Opts.MetricName()})
}

// Get returns the value for the tag with the given name.
func (t Tags) Get(key []byte) ([]byte, bool) {
	for _, tag := range t.Tags {
		if bytes.Equal(tag.Name, key) {
			return tag.Value, true
		}
	}

	return nil, false
}

// Clone returns a copy of the tags.
func (t Tags) Clone() Tags {
	// TODO: Pool these
	clonedTags := make([]Tag, t.Len())
	for i, tag := range t.Tags {
		clonedTags[i] = tag.Clone()
	}

	return Tags{
		Tags: clonedTags,
		Opts: t.Opts,
	}
}

// AddTag is used to add a single tag and maintain sorted order.
func (t Tags) AddTag(tag Tag) Tags {
	t.Tags = append(t.Tags, tag)
	return t.Normalize()
}

// SetName sets the metric name.
func (t Tags) SetName(value []byte) Tags {
	return t.AddOrUpdateTag(Tag{Name: t.Opts.MetricName(), Value: value})
}

// Name gets the metric name.
func (t Tags) Name() ([]byte, bool) {
	return t.Get(t.Opts.MetricName())
}

// AddTags is used to add a list of tags and maintain sorted order.
func (t Tags) AddTags(tags []Tag) Tags {
	t.Tags = append(t.Tags, tags...)
	return t.Normalize()
}

// AddOrUpdateTag is used to add a single tag and maintain sorted order,
// or to replace the value of an existing tag.
func (t Tags) AddOrUpdateTag(tag Tag) Tags {
	tags := t.Tags
	for i, tt := range tags {
		if bytes.Equal(tag.Name, tt.Name) {
			tags[i].Value = tag.Value
			return t
		}
	}

	return t.AddTag(tag)
}

// Add is used to add two tag structures and maintain sorted order.
func (t Tags) Add(other Tags) Tags {
	t.Tags = append(t.Tags, other.Tags...)
	return t.Normalize()
}

func (t Tags) Len() int      { return len(t.Tags) }
func (t Tags) Swap(i, j int) { t.Tags[i], t.Tags[j] = t.Tags[j], t.Tags[i] }
func (t Tags) Less(i, j int) bool {
	return bytes.Compare(t.Tags[i].Name, t.Tags[j].Name) == -1
}

// Normalize normalizes the tags by sorting them in place.
// In future, it might also ensure other things slike uniqueness.
func (t Tags) Normalize() Tags {
	sort.Sort(t)
	return t
}

func (t Tag) String() string {
	return fmt.Sprintf("%s: %s", t.Name, t.Value)
}

// Clone returns a copy of the tag.
func (t Tag) Clone() Tag {
	// Todo: Pool these
	clonedName := make([]byte, len(t.Name))
	clonedVal := make([]byte, len(t.Value))
	copy(clonedName, t.Name)
	copy(clonedVal, t.Value)
	return Tag{
		Name:  clonedName,
		Value: clonedVal,
	}
}
<commit_msg>Fix spelling (#1242)<commit_after>// Copyright (c) 2018 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package models

import (
	"bytes"
	"fmt"
	"hash/fnv"
	"sort"
	"strings"
)

var (
	defaultOptions = NewTagOptions()
)

// NewTags builds a tags with the given size and tag options.
func NewTags(size int, opts TagOptions) Tags {
	if opts == nil {
		opts = defaultOptions
	}

	return Tags{
		// Todo: Pool these
		Tags: make([]Tag, 0, size),
		Opts: opts,
	}
}

// EmptyTags returns empty tags with a default tag options.
func EmptyTags() Tags {
	return NewTags(0, nil)
}

// ID returns a string representation of the tags.
func (t Tags) ID() string {
	var (
		idLen      = t.IDLen()
		strBuilder = strings.Builder{}
	)

	strBuilder.Grow(idLen)
	for _, tag := range t.Tags {
		strBuilder.Write(tag.Name)
		strBuilder.WriteByte(eq)
		strBuilder.Write(tag.Value)
		strBuilder.WriteByte(sep)
	}

	return strBuilder.String()
}

// IDMarshalTo writes out the ID representation
// of the tags into the provided buffer.
func (t Tags) IDMarshalTo(b []byte) []byte {
	for _, tag := range t.Tags {
		b = append(b, tag.Name...)
		b = append(b, eq)
		b = append(b, tag.Value...)
		b = append(b, sep)
	}

	return b
}

// IDLen returns the length of the ID that would be
// generated from the tags.
func (t Tags) IDLen() int {
	idLen := 2 * t.Len() // account for eq and sep
	for _, tag := range t.Tags {
		idLen += len(tag.Name)
		idLen += len(tag.Value)
	}

	return idLen
}

// IDWithExcludes returns a string representation of the tags excluding some tag keys.
func (t Tags) IDWithExcludes(excludeKeys ...[]byte) uint64 {
	b := make([]byte, 0, t.Len())
	for _, tag := range t.Tags {
		// Always exclude the metric name by default
		if bytes.Equal(tag.Name, t.Opts.MetricName()) {
			continue
		}

		found := false
		for _, n := range excludeKeys {
			if bytes.Equal(n, tag.Name) {
				found = true
				break
			}
		}

		// Skip the key
		if found {
			continue
		}

		b = append(b, tag.Name...)
		b = append(b, eq)
		b = append(b, tag.Value...)
		b = append(b, sep)
	}

	h := fnv.New64a()
	h.Write(b)
	return h.Sum64()
}

func (t Tags) tagSubset(keys [][]byte, include bool) Tags {
	tags := NewTags(t.Len(), t.Opts)
	for _, tag := range t.Tags {
		found := false
		for _, k := range keys {
			if bytes.Equal(tag.Name, k) {
				found = true
				break
			}
		}

		if found == include {
			tags = tags.AddTag(tag)
		}
	}

	return tags
}

// TagsWithoutKeys returns only the tags which do not have the given keys.
func (t Tags) TagsWithoutKeys(excludeKeys [][]byte) Tags {
	return t.tagSubset(excludeKeys, false)
}

// IDWithKeys returns a string representation of the tags only including the given keys.
func (t Tags) IDWithKeys(includeKeys ...[]byte) uint64 {
	b := make([]byte, 0, t.Len())
	for _, tag := range t.Tags {
		for _, k := range includeKeys {
			if bytes.Equal(tag.Name, k) {
				b = append(b, tag.Name...)
				b = append(b, eq)
				b = append(b, tag.Value...)
				b = append(b, sep)
				break
			}
		}
	}

	h := fnv.New64a()
	h.Write(b)
	return h.Sum64()
}

// TagsWithKeys returns only the tags which have the given keys.
func (t Tags) TagsWithKeys(includeKeys [][]byte) Tags {
	return t.tagSubset(includeKeys, true)
}

// WithoutName copies the tags excluding the name tag.
func (t Tags) WithoutName() Tags {
	return t.TagsWithoutKeys([][]byte{t.Opts.MetricName()})
}

// Get returns the value for the tag with the given name.
func (t Tags) Get(key []byte) ([]byte, bool) {
	for _, tag := range t.Tags {
		if bytes.Equal(tag.Name, key) {
			return tag.Value, true
		}
	}

	return nil, false
}

// Clone returns a copy of the tags.
func (t Tags) Clone() Tags {
	// TODO: Pool these
	clonedTags := make([]Tag, t.Len())
	for i, tag := range t.Tags {
		clonedTags[i] = tag.Clone()
	}

	return Tags{
		Tags: clonedTags,
		Opts: t.Opts,
	}
}

// AddTag is used to add a single tag and maintain sorted order.
func (t Tags) AddTag(tag Tag) Tags {
	t.Tags = append(t.Tags, tag)
	return t.Normalize()
}

// SetName sets the metric name.
func (t Tags) SetName(value []byte) Tags {
	return t.AddOrUpdateTag(Tag{Name: t.Opts.MetricName(), Value: value})
}

// Name gets the metric name.
func (t Tags) Name() ([]byte, bool) {
	return t.Get(t.Opts.MetricName())
}

// AddTags is used to add a list of tags and maintain sorted order.
func (t Tags) AddTags(tags []Tag) Tags {
	t.Tags = append(t.Tags, tags...)
	return t.Normalize()
}

// AddOrUpdateTag is used to add a single tag and maintain sorted order,
// or to replace the value of an existing tag.
func (t Tags) AddOrUpdateTag(tag Tag) Tags {
	tags := t.Tags
	for i, tt := range tags {
		if bytes.Equal(tag.Name, tt.Name) {
			tags[i].Value = tag.Value
			return t
		}
	}

	return t.AddTag(tag)
}

// Add is used to add two tag structures and maintain sorted order.
func (t Tags) Add(other Tags) Tags {
	t.Tags = append(t.Tags, other.Tags...)
	return t.Normalize()
}

func (t Tags) Len() int      { return len(t.Tags) }
func (t Tags) Swap(i, j int) { t.Tags[i], t.Tags[j] = t.Tags[j], t.Tags[i] }
func (t Tags) Less(i, j int) bool {
	return bytes.Compare(t.Tags[i].Name, t.Tags[j].Name) == -1
}

// Normalize normalizes the tags by sorting them in place.
// In the future, it might also ensure other things like uniqueness.
func (t Tags) Normalize() Tags {
	sort.Sort(t)
	return t
}

func (t Tag) String() string {
	return fmt.Sprintf("%s: %s", t.Name, t.Value)
}

// Clone returns a copy of the tag.
func (t Tag) Clone() Tag {
	// Todo: Pool these
	clonedName := make([]byte, len(t.Name))
	clonedVal := make([]byte, len(t.Value))
	copy(clonedName, t.Name)
	copy(clonedVal, t.Value)
	return Tag{
		Name:  clonedName,
		Value: clonedVal,
	}
}
<|endoftext|>


<commit_before>package testutil

import (
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/smartystreets/goconvey/convey"
	"polydawn.net/repeatr/def"
)

/*
	Decorates a goconvey test with a tmpdir.

	See also https://github.com/smartystreets/goconvey/wiki/Decorating-tests-to-provide-common-logic
*/
func WithTmpdir(fn interface{}) func(c convey.C) {
	return func(c convey.C) {
		retreat, err := os.Getwd()
		if err != nil {
			panic(err)
		}
		if originalDir == "" {
			originalDir = retreat
		}

		convey.Reset(func() {
			os.Chdir(retreat)
		})

		tmpBase := def.Base()
		err = os.MkdirAll(tmpBase, 0755)
		if err != nil {
			panic(err)
		}
		err = os.Chdir(tmpBase)
		if err != nil {
			panic(err)
		}

		err = os.MkdirAll("repeatr-test", 0755)
		if err != nil {
			panic(err)
		}
		tmpdir, err := ioutil.TempDir("repeatr-test", "")
		if err != nil {
			panic(err)
		}
		tmpdir, err = filepath.Abs(tmpdir)
		if err != nil {
			panic(err)
		}
		convey.Reset(func() {
			os.RemoveAll(tmpdir)
		})
		err = os.Chdir(tmpdir)
		if err != nil {
			panic(err)
		}

		switch fn := fn.(type) {
		case func():
			fn()
		case func(c convey.C):
			fn(c)
		}
	}
}

/*
	Calls `fn` after chdir'ing to `dir`, and resets chdir on return.

	`dir` is expected to already exist, and will not be removed on return.
*/
func UsingDir(dir string, fn func()) {
	retreat, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	if originalDir == "" {
		originalDir = retreat
	}

	defer os.Chdir(retreat)

	err = os.Chdir(dir)
	if err != nil {
		panic(err)
	}

	fn()
}

/*
	Returns the first cwd, before anyone ever `WithTmpdir`'d.

	For most tests, this will be the test package directory in the source tree.
*/
func OriginalDir() string {
	// not goroutine safe, but what kind of maniac are you anyway?
	if originalDir == "" {
		var err error
		originalDir, err = os.Getwd()
		if err != nil {
			panic(err)
		}
	}
	return originalDir
}

var originalDir string
<commit_msg>Make testutil.WithTempDir choosier.<commit_after>package testutil

import (
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/smartystreets/goconvey/convey"
)

/*
	Decorates a goconvey test with a tmpdir.

	See also https://github.com/smartystreets/goconvey/wiki/Decorating-tests-to-provide-common-logic
*/
func WithTmpdir(fn interface{}) func(c convey.C) {
	return func(c convey.C) {
		retreat, err := os.Getwd()
		if err != nil {
			panic(err)
		}
		if originalDir == "" {
			originalDir = retreat
		}

		convey.Reset(func() {
			os.Chdir(retreat)
		})

		tmpBase := "/tmp/repeatr-test/"
		err = os.MkdirAll(tmpBase, os.FileMode(0755)|os.ModeSticky)
		if err != nil {
			panic(err)
		}
		err = os.Chdir(tmpBase)
		if err != nil {
			panic(err)
		}

		tmpdir, err := ioutil.TempDir("", "")
		if err != nil {
			panic(err)
		}
		tmpdir, err = filepath.Abs(tmpdir)
		if err != nil {
			panic(err)
		}
		convey.Reset(func() {
			os.RemoveAll(tmpdir)
		})
		err = os.Chdir(tmpdir)
		if err != nil {
			panic(err)
		}

		switch fn := fn.(type) {
		case func():
			fn()
		case func(c convey.C):
			fn(c)
		}
	}
}

/*
	Calls `fn` after chdir'ing to `dir`, and resets chdir on return.

	`dir` is expected to already exist, and will not be removed on return.
*/
func UsingDir(dir string, fn func()) {
	retreat, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	if originalDir == "" {
		originalDir = retreat
	}

	defer os.Chdir(retreat)

	err = os.Chdir(dir)
	if err != nil {
		panic(err)
	}

	fn()
}

/*
	Returns the first cwd, before anyone ever `WithTmpdir`'d.

	For most tests, this will be the test package directory in the source tree.
*/
func OriginalDir() string {
	// not goroutine safe, but what kind of maniac are you anyway?
	if originalDir == "" {
		var err error
		originalDir, err = os.Getwd()
		if err != nil {
			panic(err)
		}
	}
	return originalDir
}

var originalDir string
<|endoftext|>


<commit_before>/**
 * (C) Copyright 2014, Deft Labs
 *
 * 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 dlshared

import (
	"fmt"
	"sync"
	"time"
	"testing"
)

// Test the distributed lock. This requires mongo running on localhost, port 28000
func TestDistributedLock(t *testing.T) {

	lock := NewMongoDistributedLock("testLockId", "MongoTestDb", "test", "locks", 1, 1, 2, 86400)

	kernel, err := StartKernel("disritbutedLockTest", "test/configuration.json", func(kernel *Kernel) {
	 	kernel.AddComponentWithStartStopMethods("MongoTestDb", NewMongoFromConfigPath("MongoConfigDb", "mongoDb.testDb"), "Start", "Stop")
	 	kernel.AddComponentWithStartStopMethods("DistributedLock", lock, "Start", "Stop")
	})

	if err != nil { t.Errorf("TestDistributedLock start kernel is broken:", err); return }

	testLockUnlock(lock)

	waitGroup := new(sync.WaitGroup)

	// Make sure the lock is held
	lock.Lock()

	waitGroup.Add(10000)
	for i := 0; i < 10000; i++ {
		go func() {
			if lock.TryLock() { t.Errorf("TestDistributedLock is broken - try lock was able to get the lock") }
			waitGroup.Done()
		}()
	}

	go func() {
		lock.Lock()
		t.Errorf("TestDistributedLock is broken - we should not be able to obtain the lock.")
	}()

	if !lock.HasLock() { t.Errorf("TestDistributedLock is broken - we should have the lock.") }

	fmt.Println("--------- after has lock")

	time.Sleep(1*time.Second)

	fmt.Println("--------- before wait")
	waitGroup.Wait()
	fmt.Println("--------- after wait")

	fmt.Println("--------- before unlock")
	lock.Unlock()
	fmt.Println("--------- after unlock")

	if err := kernel.Stop(); err != nil { t.Errorf("TestDistributedLock stop kernel is broken:", err) }
}

func testLockUnlock(lock DistributedLock) {
	lock.Lock()
	lock.Unlock()
}

<commit_msg>debug CI<commit_after>/**
 * (C) Copyright 2014, Deft Labs
 *
 * 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 dlshared

import (
	"fmt"
	"sync"
	"time"
	"testing"
)

// Test the distributed lock. This requires mongo running on localhost, port 28000
func TestDistributedLock(t *testing.T) {

	lock := NewMongoDistributedLock("testLockId", "MongoTestDb", "test", "locks", 1, 1, 2, 86400)

	kernel, err := StartKernel("disritbutedLockTest", "test/configuration.json", func(kernel *Kernel) {
	 	kernel.AddComponentWithStartStopMethods("MongoTestDb", NewMongoFromConfigPath("MongoConfigDb", "mongoDb.testDb"), "Start", "Stop")
	 	kernel.AddComponentWithStartStopMethods("DistributedLock", lock, "Start", "Stop")
	})

	if err != nil { t.Errorf("TestDistributedLock start kernel is broken:", err); return }

	testLockUnlock(lock)

	waitGroup := new(sync.WaitGroup)

	// Make sure the lock is held
	lock.Lock()

	waitGroup.Add(10000)
	for i := 0; i < 10000; i++ {
		go func() {
			if lock.TryLock() { t.Errorf("TestDistributedLock is broken - try lock was able to get the lock") }
			waitGroup.Done()
		}()
	}

	go func() {
		lock.Lock()
		t.Errorf("TestDistributedLock is broken - we should not be able to obtain the lock.")
	}()

	if !lock.HasLock() { t.Errorf("TestDistributedLock is broken - we should have the lock.") }

	time.Sleep(1*time.Second)

	waitGroup.Wait()
	fmt.Println("--------- after wait")

	fmt.Println("--------- before unlock")
	lock.Unlock()
	fmt.Println("--------- after unlock")

	if err := kernel.Stop(); err != nil { t.Errorf("TestDistributedLock stop kernel is broken:", err) }
}

func testLockUnlock(lock DistributedLock) {
	lock.Lock()
	lock.Unlock()
}

<|endoftext|>


<commit_before>// hblsync -- High Bandwidth & Latency directory sync tool
//
// Copyright 2010 Brad Fitzpatrick
// brad@danga.com
//
// Usage: hblsync <srcdir> <dstdir>
//

package main

import "container/vector"
import "exp/iterable"
import "fmt"
import "flag"
import "os"

var delete *bool = flag.Bool("delete", false, "delete mode (takes 1 argument)")

func usage() {
	os.Stderr.WriteString("Usage: hblsync <src_dir> <dst_dir>\n")
	os.Stderr.WriteString("       hblsync --delete <dir>\n")
	os.Exit(1)
}

func main() {
	flag.Parse()
	resultChan := make(chan SyncStats)

	// Delete mode
	if (*delete) {
		if (flag.NArg() != 1) { usage() }
		go RemoveAll(flag.Arg(0), resultChan)
		fmt.Println("Results:", <-resultChan)
		return
	}

	// Sync mode
	if (flag.NArg() != 2) {
		usage();
	}

	srcDir, dstDir := flag.Arg(0), flag.Arg(1)
	ensureDirectory("source", srcDir)
	ensureDirectory("destination", dstDir)

	go SyncDirectories(srcDir, dstDir, resultChan)
	fmt.Println("Results:", <-resultChan)
}

func ensureDirectory(which string, dirName string) {
	stat, e := os.Stat(dirName)
	if e != nil {
		fmt.Fprintf(os.Stderr, "Error stat'ing %s directory: %s\n",
			which, e.String())
		os.Exit(1)
	}
	if !stat.IsDirectory() {
		fmt.Fprintf(os.Stderr, "%s directory isn't a directory: %s\n",
			which, dirName)
		os.Exit(1)
	}
}

type SyncStats struct {
	ErrorCount int

	FilesGood int   // no change needed
	DirsGood int    // already existed (contents might be wrong)

	DirsCreated int
	FilesCreated int

	FilesReplaced int  // existed, but wrong. doesn't count in FilesDeleted
	DirsDeleted int
	FilesDeleted int
}

func (stats *SyncStats) incrementBy(delta *SyncStats) {
	stats.ErrorCount += delta.ErrorCount
	stats.DirsCreated += delta.DirsCreated
	stats.DirsDeleted += delta.DirsDeleted
	stats.DirsGood += delta.DirsGood
	stats.FilesCreated += delta.FilesCreated
	stats.FilesReplaced += delta.FilesReplaced
	stats.FilesDeleted += delta.FilesDeleted
	stats.FilesGood += delta.FilesGood
}

type outstandingOps struct {
	v vector.Vector;  // of chan SyncStats
}

func (ops *outstandingOps) new() chan SyncStats {
	ch := make(chan SyncStats)
	ops.v.Push(ch)
	return ch
}

// Wait for all outstandin operations, summing the total into outStats
func (ops *outstandingOps) wait(outStats *SyncStats) {
	for _, value := range ops.v {
		ch := value.(chan SyncStats)
		subStats := <-ch
		outStats.incrementBy(&subStats)
	}
}

func readDirnames(dir string, outNames *[]string, ok chan bool) {
	fd, err := os.Open(dir, os.O_RDONLY, 0)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading dirnames: %v\n", err)
		ok <- false
	}
	defer fd.Close()
	names, err := fd.Readdirnames(-1)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading dirnames: %v\n", err)
		ok <- false
	} else {
		*outNames = names
		ok <- true
	}
}

func stringSet(items []string) map[string]bool {
	var set = map[string]bool {}
	for _, name := range items {
		set[name] = true
	}
	return set
}

func makeMapLookupTest(someMap map[string]bool) func(entry interface{}) bool {
	return func(entry interface{}) bool {
                name := entry.(string)
		if someMap[name] {
			return true
		}
		return false
	}
}

func copyRegularFile(srcName string, stat *os.FileInfo, dstName string,
	             out chan SyncStats) {
	stats := new(SyncStats)
	defer func() { out <- *stats }()

	outfd, err := os.Open(dstName, os.O_CREATE|os.O_EXCL|os.O_WRONLY,
		stat.Permission())
	if err != nil {
		fmt.Fprintf(os.Stderr,
			"Error opening copy output file %s: %s\n",
			dstName, err)
		stats.ErrorCount++
		return
	}
	defer outfd.Close()

	infd, err := os.Open(srcName, os.O_RDONLY, 0)
	if err != nil {
                fmt.Fprintf(os.Stderr,
                        "Error opening copy source file %s: %s\n",
                        srcName, err)
                stats.ErrorCount++
                return
	}
	defer infd.Close()

	const BUF_SIZE = 1024 * 256
	buf := make([]byte, BUF_SIZE)
	bytesRemain := stat.Size
	for (bytesRemain > 0) {
		n, err := infd.Read(buf)
		switch {
		case n == 0:
			stats.FilesCreated++
			return
		case n < 0:
			stats.ErrorCount++
			fmt.Fprintf(os.Stderr, "Error copying file %s in read: %s",
				srcName, err)
			return
		default:
			outN, err := outfd.Write(buf[0 : n])
			if err != nil || outN != n {
				fmt.Fprintf(os.Stderr, "Error copying file %s in write: %s",
					srcName, err)
				return
			}
			bytesRemain -= int64(outN)
		}
	}
}

func copyDirectory(srcName string, stat *os.FileInfo, dstName string,
	           out chan SyncStats) {
	stats := new(SyncStats)
	defer func() { out <- *stats }()

	err := os.Mkdir(dstName, stat.Permission())
	if err != nil {
		stats.ErrorCount++
		return
	}
	stats.DirsCreated++

	ops := new(outstandingOps)
	go SyncDirectories(srcName, dstName, ops.new())
	ops.wait(stats)
}

func sendError(out chan SyncStats) {
	stats := new(SyncStats)
	stats.ErrorCount++
	out <- *stats
}

func Copy(srcName string, dstName string, out chan SyncStats) {
	srcStat, serr := os.Lstat(srcName)
	if serr != nil {
                fmt.Fprintf(os.Stderr, "Can't stat source %s: %v", srcName, serr)
		sendError(out)
		return
	}

	switch {
	case srcStat.IsRegular():
		copyRegularFile(srcName, srcStat, dstName, out)
	case srcStat.IsDirectory():
		copyDirectory(srcName, srcStat, dstName, out)
	default:
		// TODO: symlinks, etc
		fmt.Fprintf(os.Stderr, "Can't handle special file %s",
			srcName)
		sendError(out)
	}
}

func RemoveAll(filename string, out chan SyncStats) {
	stats := new(SyncStats)
	defer func() {
		out <- *stats
	}()
	defer func() {
		//fmt.Println("RemoveAll:", filename, stats)
	}()

	dirstat, err := os.Lstat(filename)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Can't stat %s: %v", filename, err)
		stats.ErrorCount++
		return
	}

	// Leaf case: if Remove works, we're done.
	err = os.Remove(filename)
	if err == nil {
		if dirstat.IsDirectory() {
			stats.DirsDeleted++
		} else {
			stats.FilesDeleted++
		}
		return
	}

	// Otherwise, is this a directory we need to recurse into?
	if !dirstat.IsDirectory() {
		fmt.Fprintf(os.Stderr, "Not a directory as expected: %s",
			filename)
		stats.ErrorCount++
                return
	}

	fd, err := os.Open(filename, os.O_RDONLY, 0)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error opening dir %s: %v",
			filename, err)
		stats.ErrorCount++
                return
	}

	names, err := fd.Readdirnames(-1)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error readdir dir %s: %v",
			filename, err)
		fd.Close()
		stats.ErrorCount++
                return
	}
	fd.Close()

	ops := new(outstandingOps)
	for _, name := range names {
		go RemoveAll(fmt.Sprintf("%s/%s", filename, name), ops.new())
	}
	ops.wait(stats)

	// Delete the final directory
	err = os.Remove(filename)
	if err != nil {
		stats.ErrorCount++
	} else {
		stats.DirsDeleted++
	}
}

func SyncDirectories(srcDir string, dstDir string, out chan SyncStats) {
	stats := new(SyncStats)

	var srcDirnames []string
	var dstDirnames []string
	srcReadOp := make(chan bool)
	dstReadOp := make(chan bool)
	go readDirnames(srcDir, &srcDirnames, srcReadOp)
	go readDirnames(dstDir, &dstDirnames, dstReadOp)
	if !<-srcReadOp {
		stats.ErrorCount++
		fmt.Fprintf(os.Stderr, "Error reading %v\n", srcDir)
	}
	if !<-dstReadOp {
		stats.ErrorCount++
		fmt.Fprintf(os.Stderr, "Error reading %v\n", dstDir)
	}
	if stats.ErrorCount != 0 {
		out <- *stats
		return
	}

	srcSet := stringSet(srcDirnames)
	dstSet := stringSet(dstDirnames)
	inSourceDir := makeMapLookupTest(srcSet)
	inDestDir := makeMapLookupTest(dstSet)
	notInSourceDir := func(entry interface{}) bool {
		return !inSourceDir(entry)
	}

	srcNamesIter := iterable.StringArray(srcDirnames)
	dstNamesIter := iterable.StringArray(dstDirnames)
	inDst, notInDst := iterable.Partition(srcNamesIter, inDestDir)
	toBeDeletedNames := iterable.Filter(dstNamesIter, notInSourceDir)

	ops := new(outstandingOps)

	// TODO: sync contents
	fmt.Printf("Syncing dir %s -> %s\n", srcDir, dstDir)
	fmt.Println("src contents:", srcDirnames)
	fmt.Println("dst contents:", dstDirnames)

	fmt.Println("inDst contents:")
	for e := range inDst.Iter() {
		fmt.Println("  *", e)
	}

	for e := range notInDst.Iter() {
		go Copy(fmt.Sprintf("%s/%s", srcDir, e),
			fmt.Sprintf("%s/%s", dstDir, e),
			ops.new())
	}

	for e := range toBeDeletedNames.Iter() {
		go RemoveAll(fmt.Sprintf("%s/%s", dstDir, e), ops.new())
	}

	ops.wait(stats)

	fmt.Printf("sending stats for sync of dir %s\n", srcDir)
	out <- *stats
}
<commit_msg>fix file copy stats<commit_after>// hblsync -- High Bandwidth & Latency directory sync tool
//
// Copyright 2010 Brad Fitzpatrick
// brad@danga.com
//
// Usage: hblsync <srcdir> <dstdir>
//

package main

import "container/vector"
import "exp/iterable"
import "fmt"
import "flag"
import "os"

var delete *bool = flag.Bool("delete", false, "delete mode (takes 1 argument)")

func usage() {
	os.Stderr.WriteString("Usage: hblsync <src_dir> <dst_dir>\n")
	os.Stderr.WriteString("       hblsync --delete <dir>\n")
	os.Exit(1)
}

func main() {
	flag.Parse()
	resultChan := make(chan SyncStats)

	// Delete mode
	if (*delete) {
		if (flag.NArg() != 1) { usage() }
		go RemoveAll(flag.Arg(0), resultChan)
		fmt.Println("Results:", <-resultChan)
		return
	}

	// Sync mode
	if (flag.NArg() != 2) {
		usage();
	}

	srcDir, dstDir := flag.Arg(0), flag.Arg(1)
	ensureDirectory("source", srcDir)
	ensureDirectory("destination", dstDir)

	go SyncDirectories(srcDir, dstDir, resultChan)
	fmt.Println("Results:", <-resultChan)
}

func ensureDirectory(which string, dirName string) {
	stat, e := os.Stat(dirName)
	if e != nil {
		fmt.Fprintf(os.Stderr, "Error stat'ing %s directory: %s\n",
			which, e.String())
		os.Exit(1)
	}
	if !stat.IsDirectory() {
		fmt.Fprintf(os.Stderr, "%s directory isn't a directory: %s\n",
			which, dirName)
		os.Exit(1)
	}
}

type SyncStats struct {
	ErrorCount int

	FilesGood int   // no change needed
	DirsGood int    // already existed (contents might be wrong)

	DirsCreated int
	FilesCreated int

	FilesReplaced int  // existed, but wrong. doesn't count in FilesDeleted
	DirsDeleted int
	FilesDeleted int
}

func (stats *SyncStats) incrementBy(delta *SyncStats) {
	stats.ErrorCount += delta.ErrorCount
	stats.DirsCreated += delta.DirsCreated
	stats.DirsDeleted += delta.DirsDeleted
	stats.DirsGood += delta.DirsGood
	stats.FilesCreated += delta.FilesCreated
	stats.FilesReplaced += delta.FilesReplaced
	stats.FilesDeleted += delta.FilesDeleted
	stats.FilesGood += delta.FilesGood
}

type outstandingOps struct {
	v vector.Vector;  // of chan SyncStats
}

func (ops *outstandingOps) new() chan SyncStats {
	ch := make(chan SyncStats)
	ops.v.Push(ch)
	return ch
}

// Wait for all outstandin operations, summing the total into outStats
func (ops *outstandingOps) wait(outStats *SyncStats) {
	for _, value := range ops.v {
		ch := value.(chan SyncStats)
		subStats := <-ch
		outStats.incrementBy(&subStats)
	}
}

func readDirnames(dir string, outNames *[]string, ok chan bool) {
	fd, err := os.Open(dir, os.O_RDONLY, 0)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading dirnames: %v\n", err)
		ok <- false
	}
	defer fd.Close()
	names, err := fd.Readdirnames(-1)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading dirnames: %v\n", err)
		ok <- false
	} else {
		*outNames = names
		ok <- true
	}
}

func stringSet(items []string) map[string]bool {
	var set = map[string]bool {}
	for _, name := range items {
		set[name] = true
	}
	return set
}

func makeMapLookupTest(someMap map[string]bool) func(entry interface{}) bool {
	return func(entry interface{}) bool {
                name := entry.(string)
		if someMap[name] {
			return true
		}
		return false
	}
}

func copyRegularFile(srcName string, stat *os.FileInfo, dstName string,
	             out chan SyncStats) {
	stats := new(SyncStats)
	defer func() { out <- *stats }()

	outfd, err := os.Open(dstName, os.O_CREATE|os.O_EXCL|os.O_WRONLY,
		stat.Permission())
	if err != nil {
		fmt.Fprintf(os.Stderr,
			"Error opening copy output file %s: %s\n",
			dstName, err)
		stats.ErrorCount++
		return
	}
	defer outfd.Close()

	infd, err := os.Open(srcName, os.O_RDONLY, 0)
	if err != nil {
                fmt.Fprintf(os.Stderr,
                        "Error opening copy source file %s: %s\n",
                        srcName, err)
                stats.ErrorCount++
                return
	}
	defer infd.Close()

	const BUF_SIZE = 1024 * 256
	buf := make([]byte, BUF_SIZE)
	bytesRemain := stat.Size
	for (bytesRemain > 0) {
		n, err := infd.Read(buf)
		switch {
		case n == 0:
			break
		case n < 0:
			stats.ErrorCount++
			fmt.Fprintf(os.Stderr, "Error copying file %s in read: %s",
				srcName, err)
			return
		default:
			outN, err := outfd.Write(buf[0 : n])
			if err != nil || outN != n {
				fmt.Fprintf(os.Stderr, "Error copying file %s in write: %s",
					srcName, err)
				return
			}
			bytesRemain -= int64(outN)
		}
	}
	stats.FilesCreated++
}

func copyDirectory(srcName string, stat *os.FileInfo, dstName string,
	           out chan SyncStats) {
	stats := new(SyncStats)
	defer func() { out <- *stats }()

	err := os.Mkdir(dstName, stat.Permission())
	if err != nil {
		stats.ErrorCount++
		return
	}
	stats.DirsCreated++

	ops := new(outstandingOps)
	go SyncDirectories(srcName, dstName, ops.new())
	ops.wait(stats)
}

func sendError(out chan SyncStats) {
	stats := new(SyncStats)
	stats.ErrorCount++
	out <- *stats
}

func Copy(srcName string, dstName string, out chan SyncStats) {
	srcStat, serr := os.Lstat(srcName)
	if serr != nil {
                fmt.Fprintf(os.Stderr, "Can't stat source %s: %v", srcName, serr)
		sendError(out)
		return
	}

	switch {
	case srcStat.IsRegular():
		copyRegularFile(srcName, srcStat, dstName, out)
	case srcStat.IsDirectory():
		copyDirectory(srcName, srcStat, dstName, out)
	default:
		// TODO: symlinks, etc
		fmt.Fprintf(os.Stderr, "Can't handle special file %s",
			srcName)
		sendError(out)
	}
}

func RemoveAll(filename string, out chan SyncStats) {
	stats := new(SyncStats)
	defer func() {
		out <- *stats
	}()
	defer func() {
		//fmt.Println("RemoveAll:", filename, stats)
	}()

	dirstat, err := os.Lstat(filename)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Can't stat %s: %v", filename, err)
		stats.ErrorCount++
		return
	}

	// Leaf case: if Remove works, we're done.
	err = os.Remove(filename)
	if err == nil {
		if dirstat.IsDirectory() {
			stats.DirsDeleted++
		} else {
			stats.FilesDeleted++
		}
		return
	}

	// Otherwise, is this a directory we need to recurse into?
	if !dirstat.IsDirectory() {
		fmt.Fprintf(os.Stderr, "Not a directory as expected: %s",
			filename)
		stats.ErrorCount++
                return
	}

	fd, err := os.Open(filename, os.O_RDONLY, 0)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error opening dir %s: %v",
			filename, err)
		stats.ErrorCount++
                return
	}

	names, err := fd.Readdirnames(-1)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error readdir dir %s: %v",
			filename, err)
		fd.Close()
		stats.ErrorCount++
                return
	}
	fd.Close()

	ops := new(outstandingOps)
	for _, name := range names {
		go RemoveAll(fmt.Sprintf("%s/%s", filename, name), ops.new())
	}
	ops.wait(stats)

	// Delete the final directory
	err = os.Remove(filename)
	if err != nil {
		stats.ErrorCount++
	} else {
		stats.DirsDeleted++
	}
}

func SyncDirectories(srcDir string, dstDir string, out chan SyncStats) {
	stats := new(SyncStats)

	var srcDirnames []string
	var dstDirnames []string
	srcReadOp := make(chan bool)
	dstReadOp := make(chan bool)
	go readDirnames(srcDir, &srcDirnames, srcReadOp)
	go readDirnames(dstDir, &dstDirnames, dstReadOp)
	if !<-srcReadOp {
		stats.ErrorCount++
		fmt.Fprintf(os.Stderr, "Error reading %v\n", srcDir)
	}
	if !<-dstReadOp {
		stats.ErrorCount++
		fmt.Fprintf(os.Stderr, "Error reading %v\n", dstDir)
	}
	if stats.ErrorCount != 0 {
		out <- *stats
		return
	}

	srcSet := stringSet(srcDirnames)
	dstSet := stringSet(dstDirnames)
	inSourceDir := makeMapLookupTest(srcSet)
	inDestDir := makeMapLookupTest(dstSet)
	notInSourceDir := func(entry interface{}) bool {
		return !inSourceDir(entry)
	}

	srcNamesIter := iterable.StringArray(srcDirnames)
	dstNamesIter := iterable.StringArray(dstDirnames)
	inDst, notInDst := iterable.Partition(srcNamesIter, inDestDir)
	toBeDeletedNames := iterable.Filter(dstNamesIter, notInSourceDir)

	ops := new(outstandingOps)

	// TODO: sync contents
	fmt.Printf("Syncing dir %s -> %s\n", srcDir, dstDir)
	fmt.Println("src contents:", srcDirnames)
	fmt.Println("dst contents:", dstDirnames)

	fmt.Println("inDst contents:")
	for e := range inDst.Iter() {
		fmt.Println("  *", e)
	}

	for e := range notInDst.Iter() {
		go Copy(fmt.Sprintf("%s/%s", srcDir, e),
			fmt.Sprintf("%s/%s", dstDir, e),
			ops.new())
	}

	for e := range toBeDeletedNames.Iter() {
		go RemoveAll(fmt.Sprintf("%s/%s", dstDir, e), ops.new())
	}

	ops.wait(stats)

	fmt.Printf("sending stats for sync of dir %s\n", srcDir)
	out <- *stats
}
<|endoftext|>


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

// +build windows

// TODO: Add support for hidden KBs

// Enforce implements filesystem watching for configured required updates.
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/google/cabbie/cablib"
	"gopkg.in/fsnotify/fsnotify.v1"
	"github.com/google/glazier/go/helpers"
)

const enforceDir = `C:\ProgramData\Cabbie`

type enforcement struct {
	Required []string `json:"required"`
}

var (
	errFileType    = errors.New("file is not json")
	errInvalidFile = errors.New("file path is invalid")
	errParsing     = errors.New("could not parse file content")
)

func enforcements(path string) (enforcement, error) {
	path = filepath.Clean(path)
	// Enforcement files must live in Cabbie's directory. If given a relative path,
	// attempt a lookup by joining Cabbie's path and the given string.
	if !filepath.IsAbs(path) {
		path = filepath.Join(enforceDir, path)
	}
	if filepath.Ext(path) != ".json" {
		return enforcement{}, fmt.Errorf("%w: %q", errFileType, path)
	}
	b, err := helpers.PathExists(path)
	if err != nil {
		return enforcement{}, fmt.Errorf("error determining %q existence: %v", path, err)
	}
	if !b {
		return enforcement{}, fmt.Errorf("%w: %q", errInvalidFile, path)
	}
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return enforcement{}, fmt.Errorf("error reading file %q: %v", path, err)
	}
	var e enforcement
	if err := json.Unmarshal(data, &e); err != nil {
		return enforcement{}, fmt.Errorf("%w for %q: %v", errParsing, path, err)
	}
	return e, nil
}

func allEnforcements() (enforcement, error) {
	files, err := ioutil.ReadDir(enforceDir)
	if err != nil {
		return enforcement{}, err
	}
	var e enforcement
	for _, f := range files {
		p := filepath.Join(enforceDir, f.Name())
		kbs, err := enforcements(p)
		if err != nil {
			elog.Error(6, fmt.Sprintf("getAllEnforcements: error getting updates from %q:\n%v", p, err))
			continue
		}
		e.Required = append(e.Required, kbs.Required...)
	}
	if err := enforcedUpdateCount.Set(int64(len(e.Required))); err != nil {
		elog.Error(6, fmt.Sprintf("Error posting metric:\n%v", err))
	}
	e.dedupe()
	return e, nil
}

func (e *enforcement) dedupe() {
	u := make([]string, 0)
	m := make(map[string]bool)
	for _, v := range e.Required {
		if !m[v] {
			m[v] = true
			u = append(u, v)
		}
	}

	e.Required = u
}

func (e *enforcement) install() error {
	if len(e.Required) == 0 {
		elog.Info(0002, "No enforced updates defined.")
		return nil
	}
	i := installCmd{kbs: strings.Join(e.Required, ",")}
	return i.installUpdates()
}

// Filesystem watcher for required updates. This is meant to install required updates as soon as they are configured.
// All configured required updates are read on a schedule (see cabbie.go t.Enforcement ticker usage) to ensure required
// updates are installed even if a filesystem event is missed.
func runEnforcementWatcher(file chan<- string) error {
	fsw, err := fsnotify.NewWatcher()
	if err != nil {
		return fmt.Errorf("enforce: error creating filesystem watcher:\n%v", err)
	}
	defer fsw.Close()

	exist, err := helpers.PathExists(enforceDir)
	if err != nil {
		return fmt.Errorf("enforce: error checking existence of %q:\n%v", enforceDir, err)
	}
	if !exist {
		if err := os.MkdirAll(enforceDir, 0664); err != nil {
			return fmt.Errorf("enforce: error creating %q:\n%v", enforceDir, err)
		}
	}

	if err := fsw.Add(enforceDir); err != nil {
		return fmt.Errorf("enforce: error adding %q to filesystem watcher:\n%v", enforceDir, err)
	}

	for {
		evt := <-fsw.Events
		if cablib.SliceContains([]fsnotify.Op{fsnotify.Write, fsnotify.Create}, evt.Op) {
			file <- evt.Name
		}
	}
}
<commit_msg>Stop arbitrarily constructing the enforcement path; relative paths are only used in tests.<commit_after>// Copyright 2019 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.

// +build windows

// TODO: Add support for hidden KBs

// Enforce implements filesystem watching for configured required updates.
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/google/cabbie/cablib"
	"gopkg.in/fsnotify/fsnotify.v1"
	"github.com/google/glazier/go/helpers"
)

const enforceDir = `C:\ProgramData\Cabbie`

type enforcement struct {
	Required []string `json:"required"`
}

var (
	errFileType    = errors.New("file is not json")
	errInvalidFile = errors.New("file path is invalid")
	errParsing     = errors.New("could not parse file content")
)

func enforcements(path string) (enforcement, error) {
	path = filepath.Clean(path)
	if filepath.Ext(path) != ".json" {
		return enforcement{}, fmt.Errorf("%w: %q", errFileType, path)
	}
	b, err := helpers.PathExists(path)
	if err != nil {
		return enforcement{}, fmt.Errorf("error determining %q existence: %v", path, err)
	}
	if !b {
		return enforcement{}, fmt.Errorf("%w: %q", errInvalidFile, path)
	}
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return enforcement{}, fmt.Errorf("error reading file %q: %v", path, err)
	}
	var e enforcement
	if err := json.Unmarshal(data, &e); err != nil {
		return enforcement{}, fmt.Errorf("%w for %q: %v", errParsing, path, err)
	}
	return e, nil
}

func allEnforcements() (enforcement, error) {
	files, err := ioutil.ReadDir(enforceDir)
	if err != nil {
		return enforcement{}, err
	}
	var e enforcement
	for _, f := range files {
		p := filepath.Join(enforceDir, f.Name())
		kbs, err := enforcements(p)
		if err != nil {
			elog.Error(6, fmt.Sprintf("getAllEnforcements: error getting updates from %q:\n%v", p, err))
			continue
		}
		e.Required = append(e.Required, kbs.Required...)
	}
	if err := enforcedUpdateCount.Set(int64(len(e.Required))); err != nil {
		elog.Error(6, fmt.Sprintf("Error posting metric:\n%v", err))
	}
	e.dedupe()
	return e, nil
}

func (e *enforcement) dedupe() {
	u := make([]string, 0)
	m := make(map[string]bool)
	for _, v := range e.Required {
		if !m[v] {
			m[v] = true
			u = append(u, v)
		}
	}

	e.Required = u
}

func (e *enforcement) install() error {
	if len(e.Required) == 0 {
		elog.Info(0002, "No enforced updates defined.")
		return nil
	}
	i := installCmd{kbs: strings.Join(e.Required, ",")}
	return i.installUpdates()
}

// Filesystem watcher for required updates. This is meant to install required updates as soon as they are configured.
// All configured required updates are read on a schedule (see cabbie.go t.Enforcement ticker usage) to ensure required
// updates are installed even if a filesystem event is missed.
func runEnforcementWatcher(file chan<- string) error {
	fsw, err := fsnotify.NewWatcher()
	if err != nil {
		return fmt.Errorf("enforce: error creating filesystem watcher:\n%v", err)
	}
	defer fsw.Close()

	exist, err := helpers.PathExists(enforceDir)
	if err != nil {
		return fmt.Errorf("enforce: error checking existence of %q:\n%v", enforceDir, err)
	}
	if !exist {
		if err := os.MkdirAll(enforceDir, 0664); err != nil {
			return fmt.Errorf("enforce: error creating %q:\n%v", enforceDir, err)
		}
	}

	if err := fsw.Add(enforceDir); err != nil {
		return fmt.Errorf("enforce: error adding %q to filesystem watcher:\n%v", enforceDir, err)
	}

	for {
		evt := <-fsw.Events
		if cablib.SliceContains([]fsnotify.Op{fsnotify.Write, fsnotify.Create}, evt.Op) {
			file <- evt.Name
		}
	}
}
<|endoftext|>


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

package resourcetesting

import (
	"io"
	"strings"
	"time"

	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	"github.com/juju/testing/filetesting"
	gc "gopkg.in/check.v1"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"

	"github.com/juju/juju/resource"
)

// NewResource produces full resource info for the given name and
// content. The origin is set set to "upload". A reader is also returned
// which contains the content.
func NewResource(c *gc.C, stub *testing.Stub, name, content string) resource.Opened {
	res := resource.Resource{
		Resource:  NewCharmResource(c, name, content),
		Username:  "a.user",
		Timestamp: time.Now().UTC(),
	}
	err := res.Validate()
	c.Assert(err, jc.ErrorIsNil)

	return resource.Opened{
		Resource:   res,
		ReadCloser: newStubReadCloser(stub, content),
	}
}

// NewCharmResource produces basic resource info for the given name
// and content. The origin is set set to "upload".
func NewCharmResource(c *gc.C, name, content string) charmresource.Resource {
	fp, err := charmresource.GenerateFingerprint(strings.NewReader(content))
	c.Assert(err, jc.ErrorIsNil)
	res := charmresource.Resource{
		Meta: charmresource.Meta{
			Name: name,
			Type: charmresource.TypeFile,
			Path: name + ".tgz",
		},
		Origin:      charmresource.OriginUpload,
		Revision:    0,
		Fingerprint: fp,
		Size:        int64(len(content)),
	}
	err = res.Validate()
	c.Assert(err, jc.ErrorIsNil)

	return res
}

// NewPlaceholderResource returns resource info for a resource that
// has not been uploaded or pulled from the charm store yet. The origin
// is set to "upload".
func NewPlaceholderResource(c *gc.C, name string) resource.Resource {
	res := resource.Resource{
		Resource: NewCharmResource(c, name, ""),
	}
	res.Fingerprint = charmresource.Fingerprint{}
	res.Username = ""
	res.Timestamp = time.Time{}
	return res
}

type stubReadCloser struct {
	io.Reader
	io.Closer
}

func newStubReadCloser(stub *testing.Stub, content string) io.ReadCloser {
	return &stubReadCloser{
		Reader: filetesting.NewStubReader(stub, content),
		Closer: &filetesting.StubCloser{
			Stub: stub,
		},
	}
}
<commit_msg>Add a package doc comment.<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// This package provides helpers for testing with resources.
package resourcetesting

import (
	"io"
	"strings"
	"time"

	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	"github.com/juju/testing/filetesting"
	gc "gopkg.in/check.v1"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"

	"github.com/juju/juju/resource"
)

// NewResource produces full resource info for the given name and
// content. The origin is set set to "upload". A reader is also returned
// which contains the content.
func NewResource(c *gc.C, stub *testing.Stub, name, content string) resource.Opened {
	res := resource.Resource{
		Resource:  NewCharmResource(c, name, content),
		Username:  "a.user",
		Timestamp: time.Now().UTC(),
	}
	err := res.Validate()
	c.Assert(err, jc.ErrorIsNil)

	return resource.Opened{
		Resource:   res,
		ReadCloser: newStubReadCloser(stub, content),
	}
}

// NewCharmResource produces basic resource info for the given name
// and content. The origin is set set to "upload".
func NewCharmResource(c *gc.C, name, content string) charmresource.Resource {
	fp, err := charmresource.GenerateFingerprint(strings.NewReader(content))
	c.Assert(err, jc.ErrorIsNil)
	res := charmresource.Resource{
		Meta: charmresource.Meta{
			Name: name,
			Type: charmresource.TypeFile,
			Path: name + ".tgz",
		},
		Origin:      charmresource.OriginUpload,
		Revision:    0,
		Fingerprint: fp,
		Size:        int64(len(content)),
	}
	err = res.Validate()
	c.Assert(err, jc.ErrorIsNil)

	return res
}

// NewPlaceholderResource returns resource info for a resource that
// has not been uploaded or pulled from the charm store yet. The origin
// is set to "upload".
func NewPlaceholderResource(c *gc.C, name string) resource.Resource {
	res := resource.Resource{
		Resource: NewCharmResource(c, name, ""),
	}
	res.Fingerprint = charmresource.Fingerprint{}
	res.Username = ""
	res.Timestamp = time.Time{}
	return res
}

type stubReadCloser struct {
	io.Reader
	io.Closer
}

func newStubReadCloser(stub *testing.Stub, content string) io.ReadCloser {
	return &stubReadCloser{
		Reader: filetesting.NewStubReader(stub, content),
		Closer: &filetesting.StubCloser{
			Stub: stub,
		},
	}
}
<|endoftext|>


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

package workers

import (
	"io"
	"time"

	"github.com/juju/errors"
	"github.com/juju/names"
	"gopkg.in/juju/charm.v6-unstable"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"

	"github.com/juju/juju/worker"
)

// Service exposes the functionality of the Juju entity needed here.
type Service interface {
	// ID identifies the service in the model.
	ID() names.ServiceTag

	// CharmURL identifies the service's charm.
	CharmURL() *charm.URL
}

// DataStore exposes the functionality of Juju state needed here.
type DataStore interface {
	// ListAllServices returns all the services in the model.
	ListAllServices() ([]Service, error)

	// SetCharmStoreResources sets the "polled from the charm store"
	// resources for the service to the provided values.
	SetCharmStoreResources(serviceID string, info []charmresource.Resource, lastPolled time.Time) error
}

// CharmStoreClient exposes the functionality of the charm store
// needed here.
type CharmStoreClient interface {
	io.Closer

	// ListResources returns the resources info for each identified charm.
	ListResources([]*charm.URL) ([][]charmresource.Resource, error)
}

// CharmStorePoller provides the functionality to poll the charm store
// for changes in resources in the Juju model.
type CharmStorePoller struct {
	CharmStorePollerDeps

	// Period is the time between poll attempts.
	Period time.Duration
}

// NewCharmStorePoller returns a charm store poller that uses the
// provided data store.
func NewCharmStorePoller(st DataStore, newClient func() (CharmStoreClient, error)) *CharmStorePoller {
	deps := &csPollerDeps{
		DataStore: st,
		newClient: newClient,
	}
	return &CharmStorePoller{
		CharmStorePollerDeps: deps,
		Period:               5 * time.Minute, // TODO(ericsnow) This is probably too frequent.
	}
}

// NewWorker returns a new periodic worker for the poller.
func (csp CharmStorePoller) NewWorker() worker.Worker {
	// TODO(ericsnow) Wrap Do() in a retry? Log the error instead of
	// returning it?
	return csp.NewPeriodicWorker(csp.Do, csp.Period)
}

// Do performs a single polling iteration.
func (csp CharmStorePoller) Do(stop <-chan struct{}) error {
	services, err := csp.ListAllServices()
	if err != nil {
		return errors.Trace(err)
	}
	select {
	case <-stop:
		return nil
	default:
	}

	var cURLs []*charm.URL
	for _, service := range services {
		cURL := service.CharmURL()
		if cURL == nil {
			continue
		}
		cURLs = append(cURLs, cURL)
	}
	select {
	case <-stop:
		return nil
	default:
	}

	chResources, err := csp.ListCharmStoreResources(cURLs)
	if err != nil {
		return errors.Trace(err)
	}

	lastPolled := time.Now().UTC()
	for i, service := range services {
		select {
		case <-stop:
			return nil
		default:
		}

		serviceID := service.ID().Id()
		if err := csp.SetCharmStoreResources(serviceID, chResources[i], lastPolled); err != nil {
			return errors.Trace(err)
		}
	}

	return nil
}

// CharmStorePollerDeps exposes the external dependencies of a charm
// store poller.
type CharmStorePollerDeps interface {
	DataStore

	// NewPeriodicWorker returns a new periodic worker.
	NewPeriodicWorker(func(stop <-chan struct{}) error, time.Duration) worker.Worker

	// ListCharmStoreResources returns the resources from the charm
	// store for each of the identified charms.
	ListCharmStoreResources([]*charm.URL) ([][]charmresource.Resource, error)
}

type csPollerDeps struct {
	DataStore
	newClient func() (CharmStoreClient, error)
}

// NewPeriodicWorker implements CharmStorePollerDeps.
func (csPollerDeps) NewPeriodicWorker(call func(stop <-chan struct{}) error, period time.Duration) worker.Worker {
	return worker.NewPeriodicWorker(call, period, worker.NewTimer)
}

// ListCharmStoreResources implements CharmStorePollerDeps.
func (deps csPollerDeps) ListCharmStoreResources(cURLs []*charm.URL) ([][]charmresource.Resource, error) {
	client, err := deps.newClient()
	if err != nil {
		return nil, errors.Trace(err)
	}
	defer client.Close()

	chResources, err := client.ListResources(cURLs)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return chResources, nil
}
<commit_msg>Move the poll period to a constant.<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package workers

import (
	"io"
	"time"

	"github.com/juju/errors"
	"github.com/juju/names"
	"gopkg.in/juju/charm.v6-unstable"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"

	"github.com/juju/juju/worker"
)

const charmStorePollPeriod = 1 * time.day

// Service exposes the functionality of the Juju entity needed here.
type Service interface {
	// ID identifies the service in the model.
	ID() names.ServiceTag

	// CharmURL identifies the service's charm.
	CharmURL() *charm.URL
}

// DataStore exposes the functionality of Juju state needed here.
type DataStore interface {
	// ListAllServices returns all the services in the model.
	ListAllServices() ([]Service, error)

	// SetCharmStoreResources sets the "polled from the charm store"
	// resources for the service to the provided values.
	SetCharmStoreResources(serviceID string, info []charmresource.Resource, lastPolled time.Time) error
}

// CharmStoreClient exposes the functionality of the charm store
// needed here.
type CharmStoreClient interface {
	io.Closer

	// ListResources returns the resources info for each identified charm.
	ListResources([]*charm.URL) ([][]charmresource.Resource, error)
}

// CharmStorePoller provides the functionality to poll the charm store
// for changes in resources in the Juju model.
type CharmStorePoller struct {
	CharmStorePollerDeps

	// Period is the time between poll attempts.
	Period time.Duration
}

// NewCharmStorePoller returns a charm store poller that uses the
// provided data store.
func NewCharmStorePoller(st DataStore, newClient func() (CharmStoreClient, error)) *CharmStorePoller {
	deps := &csPollerDeps{
		DataStore: st,
		newClient: newClient,
	}
	return &CharmStorePoller{
		CharmStorePollerDeps: deps,
		Period:               charmStorePollPeriod,
	}
}

// NewWorker returns a new periodic worker for the poller.
func (csp CharmStorePoller) NewWorker() worker.Worker {
	// TODO(ericsnow) Wrap Do() in a retry? Log the error instead of
	// returning it?
	return csp.NewPeriodicWorker(csp.Do, csp.Period)
}

// Do performs a single polling iteration.
func (csp CharmStorePoller) Do(stop <-chan struct{}) error {
	services, err := csp.ListAllServices()
	if err != nil {
		return errors.Trace(err)
	}
	select {
	case <-stop:
		return nil
	default:
	}

	var cURLs []*charm.URL
	for _, service := range services {
		cURL := service.CharmURL()
		if cURL == nil {
			continue
		}
		cURLs = append(cURLs, cURL)
	}
	select {
	case <-stop:
		return nil
	default:
	}

	chResources, err := csp.ListCharmStoreResources(cURLs)
	if err != nil {
		return errors.Trace(err)
	}

	lastPolled := time.Now().UTC()
	for i, service := range services {
		select {
		case <-stop:
			return nil
		default:
		}

		serviceID := service.ID().Id()
		if err := csp.SetCharmStoreResources(serviceID, chResources[i], lastPolled); err != nil {
			return errors.Trace(err)
		}
	}

	return nil
}

// CharmStorePollerDeps exposes the external dependencies of a charm
// store poller.
type CharmStorePollerDeps interface {
	DataStore

	// NewPeriodicWorker returns a new periodic worker.
	NewPeriodicWorker(func(stop <-chan struct{}) error, time.Duration) worker.Worker

	// ListCharmStoreResources returns the resources from the charm
	// store for each of the identified charms.
	ListCharmStoreResources([]*charm.URL) ([][]charmresource.Resource, error)
}

type csPollerDeps struct {
	DataStore
	newClient func() (CharmStoreClient, error)
}

// NewPeriodicWorker implements CharmStorePollerDeps.
func (csPollerDeps) NewPeriodicWorker(call func(stop <-chan struct{}) error, period time.Duration) worker.Worker {
	return worker.NewPeriodicWorker(call, period, worker.NewTimer)
}

// ListCharmStoreResources implements CharmStorePollerDeps.
func (deps csPollerDeps) ListCharmStoreResources(cURLs []*charm.URL) ([][]charmresource.Resource, error) {
	client, err := deps.newClient()
	if err != nil {
		return nil, errors.Trace(err)
	}
	defer client.Close()

	chResources, err := client.ListResources(cURLs)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return chResources, nil
}
<|endoftext|>