<commit_before>/*
Copyright 2017 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 volume

import (
	"fmt"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/golang/glog"
	"github.com/kubernetes-incubator/external-storage/lib/controller"
	"github.com/kubernetes-incubator/external-storage/lib/gidallocator"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

const (
	// are we allowed to set this? else make up our own
	annCreatedBy       = "kubernetes.io/createdby"
	createdBy          = "glusterfs-simple-provisioner"
	dynamicEpSvcPrefix = "glusterfs-simple-"
)

// NewGlusterfsProvisioner creates a new glusterfs simple provisioner
func NewGlusterfsProvisioner(config *rest.Config, client kubernetes.Interface) controller.Provisioner {
	glog.Infof("Creating NewGlusterfsProvisioner.")
	return newGlusterfsProvisionerInternal(config, client)
}

func newGlusterfsProvisionerInternal(config *rest.Config, client kubernetes.Interface) *glusterfsProvisioner {
	var identity types.UID

	restClient := client.Core().RESTClient()
	provisioner := &glusterfsProvisioner{
		config:     config,
		client:     client,
		restClient: restClient,
		identity:   identity,
		allocator:  gidallocator.New(client),
	}

	return provisioner
}

type glusterfsProvisioner struct {
	client     kubernetes.Interface
	restClient rest.Interface
	config     *rest.Config
	identity   types.UID
	allocator  gidallocator.Allocator
}

type glusterBrick struct {
	Host string
	Path string
}

var _ controller.Provisioner = &glusterfsProvisioner{}

func (p *glusterfsProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) {
	if options.PVC.Spec.Selector != nil {
		return nil, fmt.Errorf("claim Selector is not supported")
	}
	glog.V(4).Infof("Start Provisioning volume: VolumeOptions %v", options)

	gid, err := p.allocator.AllocateNext(options)
	if err != nil {
		return nil, err
	}

	pvcNamespace := options.PVC.Namespace
	pvcName := options.PVC.Name
	cfg, err := NewProvisionerConfig(options.PVName, options.Parameters)
	if err != nil {
		return nil, fmt.Errorf("Parameter is invalid: %s", err)
	}

	r, err := p.createVolume(pvcNamespace, pvcName, cfg, gid)
	if err != nil {
		return nil, err
	}

	annotations := make(map[string]string)
	annotations[annCreatedBy] = createdBy
	annotations[gidallocator.VolumeGidAnnotationKey] = strconv.FormatInt(int64(gid), 10)
	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name:        options.PVName,
			Annotations: annotations,
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: options.PersistentVolumeReclaimPolicy,
			AccessModes:                   options.PVC.Spec.AccessModes,
			Capacity: v1.ResourceList{
				v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
			},
			PersistentVolumeSource: v1.PersistentVolumeSource{
				Glusterfs: r,
			},
		},
	}
	return pv, nil
}

func (p *glusterfsProvisioner) getClusterNodes(cfg *ProvisionerConfig) []string {
	// XXX: Improve to get all cluster nodes
	nodes := make([]string, len(cfg.BrickRootPaths))
	for i, root := range cfg.BrickRootPaths {
		nodes[i] = root.Host
	}
	return nodes
}

func (p *glusterfsProvisioner) createVolume(
	namespace string, name string, cfg *ProvisionerConfig, gid int) (*v1.GlusterfsVolumeSource, error) {
	var err error
	var bricks []glusterBrick
	var endpoint *v1.Endpoints
	var service *v1.Service

	bricks, err = p.createBricks(namespace, name, cfg, gid)
	if err != nil {
		glog.Errorf("Creating bricks is failed: %s,%s", namespace, name)
	}

	if err == nil {
		err = p.createGlusterVolume(bricks, cfg)
	}

	if err == nil {
		epServiceName := dynamicEpSvcPrefix + name
		epNamespace := namespace
		dynamicHostIps := p.getClusterNodes(cfg)
		endpoint, service, err = p.createEndpointService(epNamespace, epServiceName, dynamicHostIps, name)

		if err != nil {
			glog.Errorf("glusterfs: failed to create endpoint/service: %v", err)
		} else {
			glog.V(3).Infof("glusterfs: dynamic ep %v and svc : %v ", endpoint, service)
			return &v1.GlusterfsVolumeSource{
				EndpointsName: endpoint.Name,
				Path:          cfg.VolumeName,
				ReadOnly:      false,
			}, nil
		}
	}

	p.deleteVolume(namespace, name, cfg)
	return nil, err
}

func (p *glusterfsProvisioner) createBricks(
	namespace string, pvcName string, cfg *ProvisionerConfig, gid int) ([]glusterBrick, error) {
	var cmds []string
	bricks := make([]glusterBrick, len(cfg.BrickRootPaths))
	brickName := strings.Join([]string{pvcName, cfg.VolumeName}, "-")

	for i, root := range cfg.BrickRootPaths {
		host := root.Host
		path := filepath.Join(root.Path, namespace, brickName)
		bricks[i].Host = host
		bricks[i].Path = path

		glog.Infof("mkdir -p %s:%s", host, path)
		cmds = []string{
			fmt.Sprintf("mkdir -p %s", path),
			fmt.Sprintf("chown :%v %s", gid, path),
		}
		err := p.ExecuteCommands(host, cmds, cfg)
		if err != nil {
			return nil, err
		}
	}

	return bricks, nil
}

func (p *glusterfsProvisioner) createGlusterVolume(bricks []glusterBrick, cfg *ProvisionerConfig) error {
	cmd := fmt.Sprintf(
		"gluster --mode=script volume create %s %s", cfg.VolumeName, cfg.VolumeType,
	)
	for _, b := range bricks {
		cmd += fmt.Sprintf(" %s:%s", b.Host, b.Path)
	}
	if cfg.ForceCreate {
		cmd += " force"
	}

	cmds := []string{
		cmd,
		fmt.Sprintf("gluster --mode=script volume start %s", cfg.VolumeName),
	}
	// XXX: Fix this simple host determination
	host := bricks[0].Host

	// Create and Start gluster volume
	err := p.ExecuteCommands(host, cmds, cfg)
	if err != nil {
		glog.Errorf("Failed to create gluster volume: %v", cmds)
		return err
	}
	return nil
}

func (p *glusterfsProvisioner) createEndpointService(namespace string, epServiceName string, hostips []string, pvcname string) (endpoint *v1.Endpoints, service *v1.Service, err error) {

	addrlist := make([]v1.EndpointAddress, len(hostips))
	for i, v := range hostips {
		addrlist[i].IP = v
	}
	endpoint = &v1.Endpoints{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: namespace,
			Name:      epServiceName,
			Labels: map[string]string{
				"gluster.kubernetes.io/provisioned-for-pvc": pvcname,
			},
		},
		Subsets: []v1.EndpointSubset{{
			Addresses: addrlist,
			Ports:     []v1.EndpointPort{{Port: 1, Protocol: "TCP"}},
		}},
	}
	kubeClient := p.client
	if kubeClient == nil {
		return nil, nil, fmt.Errorf("glusterfs: failed to get kube client when creating endpoint service")
	}
	_, err = kubeClient.Core().Endpoints(namespace).Create(endpoint)
	if err != nil && errors.IsAlreadyExists(err) {
		glog.V(1).Infof("glusterfs: endpoint [%s] already exist in namespace [%s]", endpoint, namespace)
		err = nil
	}
	if err != nil {
		glog.Errorf("glusterfs: failed to create endpoint: %v", err)
		return nil, nil, fmt.Errorf("error creating endpoint: %v", err)
	}
	service = &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      epServiceName,
			Namespace: namespace,
			Labels: map[string]string{
				"gluster.kubernetes.io/provisioned-for-pvc": pvcname,
			},
		},
		Spec: v1.ServiceSpec{
			Ports: []v1.ServicePort{
				{Protocol: "TCP", Port: 1}}}}
	_, err = kubeClient.Core().Services(namespace).Create(service)
	if err != nil && errors.IsAlreadyExists(err) {
		glog.V(1).Infof("glusterfs: service [%s] already exist in namespace [%s]", service, namespace)
		err = nil
	}
	if err != nil {
		glog.Errorf("glusterfs: failed to create service: %v", err)
		return nil, nil, fmt.Errorf("error creating service: %v", err)
	}
	return endpoint, service, nil
}
<commit_msg>[glusterfs] Set permission correctly<commit_after>/*
Copyright 2017 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 volume

import (
	"fmt"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/golang/glog"
	"github.com/kubernetes-incubator/external-storage/lib/controller"
	"github.com/kubernetes-incubator/external-storage/lib/gidallocator"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

const (
	// are we allowed to set this? else make up our own
	annCreatedBy       = "kubernetes.io/createdby"
	createdBy          = "glusterfs-simple-provisioner"
	dynamicEpSvcPrefix = "glusterfs-simple-"
)

// NewGlusterfsProvisioner creates a new glusterfs simple provisioner
func NewGlusterfsProvisioner(config *rest.Config, client kubernetes.Interface) controller.Provisioner {
	glog.Infof("Creating NewGlusterfsProvisioner.")
	return newGlusterfsProvisionerInternal(config, client)
}

func newGlusterfsProvisionerInternal(config *rest.Config, client kubernetes.Interface) *glusterfsProvisioner {
	var identity types.UID

	restClient := client.Core().RESTClient()
	provisioner := &glusterfsProvisioner{
		config:     config,
		client:     client,
		restClient: restClient,
		identity:   identity,
		allocator:  gidallocator.New(client),
	}

	return provisioner
}

type glusterfsProvisioner struct {
	client     kubernetes.Interface
	restClient rest.Interface
	config     *rest.Config
	identity   types.UID
	allocator  gidallocator.Allocator
}

type glusterBrick struct {
	Host string
	Path string
}

var _ controller.Provisioner = &glusterfsProvisioner{}

func (p *glusterfsProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) {
	if options.PVC.Spec.Selector != nil {
		return nil, fmt.Errorf("claim Selector is not supported")
	}
	glog.V(4).Infof("Start Provisioning volume: VolumeOptions %v", options)

	gid, err := p.allocator.AllocateNext(options)
	if err != nil {
		return nil, err
	}

	pvcNamespace := options.PVC.Namespace
	pvcName := options.PVC.Name
	cfg, err := NewProvisionerConfig(options.PVName, options.Parameters)
	if err != nil {
		return nil, fmt.Errorf("Parameter is invalid: %s", err)
	}

	r, err := p.createVolume(pvcNamespace, pvcName, cfg, gid)
	if err != nil {
		return nil, err
	}

	annotations := make(map[string]string)
	annotations[annCreatedBy] = createdBy
	annotations[gidallocator.VolumeGidAnnotationKey] = strconv.FormatInt(int64(gid), 10)
	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name:        options.PVName,
			Annotations: annotations,
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: options.PersistentVolumeReclaimPolicy,
			AccessModes:                   options.PVC.Spec.AccessModes,
			Capacity: v1.ResourceList{
				v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
			},
			PersistentVolumeSource: v1.PersistentVolumeSource{
				Glusterfs: r,
			},
		},
	}
	return pv, nil
}

func (p *glusterfsProvisioner) getClusterNodes(cfg *ProvisionerConfig) []string {
	// XXX: Improve to get all cluster nodes
	nodes := make([]string, len(cfg.BrickRootPaths))
	for i, root := range cfg.BrickRootPaths {
		nodes[i] = root.Host
	}
	return nodes
}

func (p *glusterfsProvisioner) createVolume(
	namespace string, name string, cfg *ProvisionerConfig, gid int) (*v1.GlusterfsVolumeSource, error) {
	var err error
	var bricks []glusterBrick
	var endpoint *v1.Endpoints
	var service *v1.Service

	bricks, err = p.createBricks(namespace, name, cfg, gid)
	if err != nil {
		glog.Errorf("Creating bricks is failed: %s,%s", namespace, name)
	}

	if err == nil {
		err = p.createGlusterVolume(bricks, cfg)
	}

	if err == nil {
		epServiceName := dynamicEpSvcPrefix + name
		epNamespace := namespace
		dynamicHostIps := p.getClusterNodes(cfg)
		endpoint, service, err = p.createEndpointService(epNamespace, epServiceName, dynamicHostIps, name)

		if err != nil {
			glog.Errorf("glusterfs: failed to create endpoint/service: %v", err)
		} else {
			glog.V(3).Infof("glusterfs: dynamic ep %v and svc : %v ", endpoint, service)
			return &v1.GlusterfsVolumeSource{
				EndpointsName: endpoint.Name,
				Path:          cfg.VolumeName,
				ReadOnly:      false,
			}, nil
		}
	}

	p.deleteVolume(namespace, name, cfg)
	return nil, err
}

func (p *glusterfsProvisioner) createBricks(
	namespace string, pvcName string, cfg *ProvisionerConfig, gid int) ([]glusterBrick, error) {
	var cmds []string
	bricks := make([]glusterBrick, len(cfg.BrickRootPaths))
	brickName := strings.Join([]string{pvcName, cfg.VolumeName}, "-")

	for i, root := range cfg.BrickRootPaths {
		host := root.Host
		path := filepath.Join(root.Path, namespace, brickName)
		bricks[i].Host = host
		bricks[i].Path = path

		glog.Infof("mkdir -p %s:%s", host, path)
		cmds = []string{
			fmt.Sprintf("mkdir -p %s", path),
			fmt.Sprintf("chown :%v %s", gid, path),
			fmt.Sprintf("chmod 0771 %s", path),
		}
		err := p.ExecuteCommands(host, cmds, cfg)
		if err != nil {
			return nil, err
		}
	}

	return bricks, nil
}

func (p *glusterfsProvisioner) createGlusterVolume(bricks []glusterBrick, cfg *ProvisionerConfig) error {
	cmd := fmt.Sprintf(
		"gluster --mode=script volume create %s %s", cfg.VolumeName, cfg.VolumeType,
	)
	for _, b := range bricks {
		cmd += fmt.Sprintf(" %s:%s", b.Host, b.Path)
	}
	if cfg.ForceCreate {
		cmd += " force"
	}

	cmds := []string{
		cmd,
		fmt.Sprintf("gluster --mode=script volume start %s", cfg.VolumeName),
	}
	// XXX: Fix this simple host determination
	host := bricks[0].Host

	// Create and Start gluster volume
	err := p.ExecuteCommands(host, cmds, cfg)
	if err != nil {
		glog.Errorf("Failed to create gluster volume: %v", cmds)
		return err
	}
	return nil
}

func (p *glusterfsProvisioner) createEndpointService(namespace string, epServiceName string, hostips []string, pvcname string) (endpoint *v1.Endpoints, service *v1.Service, err error) {

	addrlist := make([]v1.EndpointAddress, len(hostips))
	for i, v := range hostips {
		addrlist[i].IP = v
	}
	endpoint = &v1.Endpoints{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: namespace,
			Name:      epServiceName,
			Labels: map[string]string{
				"gluster.kubernetes.io/provisioned-for-pvc": pvcname,
			},
		},
		Subsets: []v1.EndpointSubset{{
			Addresses: addrlist,
			Ports:     []v1.EndpointPort{{Port: 1, Protocol: "TCP"}},
		}},
	}
	kubeClient := p.client
	if kubeClient == nil {
		return nil, nil, fmt.Errorf("glusterfs: failed to get kube client when creating endpoint service")
	}
	_, err = kubeClient.Core().Endpoints(namespace).Create(endpoint)
	if err != nil && errors.IsAlreadyExists(err) {
		glog.V(1).Infof("glusterfs: endpoint [%s] already exist in namespace [%s]", endpoint, namespace)
		err = nil
	}
	if err != nil {
		glog.Errorf("glusterfs: failed to create endpoint: %v", err)
		return nil, nil, fmt.Errorf("error creating endpoint: %v", err)
	}
	service = &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      epServiceName,
			Namespace: namespace,
			Labels: map[string]string{
				"gluster.kubernetes.io/provisioned-for-pvc": pvcname,
			},
		},
		Spec: v1.ServiceSpec{
			Ports: []v1.ServicePort{
				{Protocol: "TCP", Port: 1}}}}
	_, err = kubeClient.Core().Services(namespace).Create(service)
	if err != nil && errors.IsAlreadyExists(err) {
		glog.V(1).Infof("glusterfs: service [%s] already exist in namespace [%s]", service, namespace)
		err = nil
	}
	if err != nil {
		glog.Errorf("glusterfs: failed to create service: %v", err)
		return nil, nil, fmt.Errorf("error creating service: %v", err)
	}
	return endpoint, service, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The GoPlus Authors (goplus.org)

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 parser

import (
	"io/ioutil"
	"os"
	"path"
	"strings"
	"testing"

	"github.com/goplus/gop/parser/parsertest"
	"github.com/goplus/gop/token"
	"github.com/qiniu/x/log"
)

// -----------------------------------------------------------------------------

func init() {
	log.SetOutputLevel(log.Ldebug)
	log.SetFlags(log.Llevel)
}

func testFrom(t *testing.T, pkgDir, sel string) {
	if sel != "" && !strings.Contains(pkgDir, sel) {
		return
	}
	log.Debug("Parsing", pkgDir)
	fset := token.NewFileSet()
	pkgs, err := ParseDir(fset, pkgDir, nil, 0)
	if err != nil || len(pkgs) != 1 {
		t.Fatal("ParseDir failed:", err, len(pkgs))
	}
	for _, pkg := range pkgs {
		b, err := ioutil.ReadFile(pkgDir + "/parser.expect")
		if err != nil {
			t.Fatal("Parsing", pkgDir, "-", err)
		}
		parsertest.Expect(t, pkg, string(b))
		return
	}
}

func TestFromTestdata(t *testing.T) {
	sel := "exists"
	dir, err := os.Getwd()
	if err != nil {
		t.Fatal("Getwd failed:", err)
	}
	dir = path.Join(dir, "./_testdata")
	fis, err := ioutil.ReadDir(dir)
	if err != nil {
		t.Fatal("ReadDir failed:", err)
	}
	for _, fi := range fis {
		testFrom(t, dir+"/"+fi.Name(), sel)
	}
}

// -----------------------------------------------------------------------------
<commit_msg>TestFromTestdata remove select case<commit_after>/*
Copyright 2020 The GoPlus Authors (goplus.org)

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 parser

import (
	"io/ioutil"
	"os"
	"path"
	"strings"
	"testing"

	"github.com/goplus/gop/parser/parsertest"
	"github.com/goplus/gop/token"
	"github.com/qiniu/x/log"
)

// -----------------------------------------------------------------------------

func init() {
	log.SetOutputLevel(log.Ldebug)
	log.SetFlags(log.Llevel)
}

func testFrom(t *testing.T, pkgDir, sel string) {
	if sel != "" && !strings.Contains(pkgDir, sel) {
		return
	}
	log.Debug("Parsing", pkgDir)
	fset := token.NewFileSet()
	pkgs, err := ParseDir(fset, pkgDir, nil, 0)
	if err != nil || len(pkgs) != 1 {
		t.Fatal("ParseDir failed:", err, len(pkgs))
	}
	for _, pkg := range pkgs {
		b, err := ioutil.ReadFile(pkgDir + "/parser.expect")
		if err != nil {
			t.Fatal("Parsing", pkgDir, "-", err)
		}
		parsertest.Expect(t, pkg, string(b))
		return
	}
}

func TestFromTestdata(t *testing.T) {
	sel := ""
	dir, err := os.Getwd()
	if err != nil {
		t.Fatal("Getwd failed:", err)
	}
	dir = path.Join(dir, "./_testdata")
	fis, err := ioutil.ReadDir(dir)
	if err != nil {
		t.Fatal("ReadDir failed:", err)
	}
	for _, fi := range fis {
		testFrom(t, dir+"/"+fi.Name(), sel)
	}
}

// -----------------------------------------------------------------------------
<|endoftext|>


<commit_before>package gaurun

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/alexjlockwood/gcm"
	"github.com/cubicdaiya/apns"
	"net/http"
	"sync/atomic"
	"time"
)

type RequestGaurun struct {
	Notifications []RequestGaurunNotification `json:"notifications"`
}

type RequestGaurunNotification struct {
	// Common
	Tokens   []string `json:"token"`
	Platform int      `json:"platform"`
	Message  string   `json:"message"`
	// Android
	CollapseKey    string `json:"collapse_key,omitempty"`
	DelayWhileIdle bool   `json:"delay_while_idle,omitempty"`
	TimeToLive     int    `json:"time_to_live,omitempty"`
	// iOS
	Badge  int          `json:"badge,omitempty"`
	Sound  string       `json:"sound,omitempty"`
	Expiry int          `json:"expiry,omitempty"`
	Retry  int          `json:"retry,omitempty"`
	Extend []ExtendJSON `json:"extend,omitempty"`
	// meta
	IDs []uint64 `json:"seq_id,omitempty"`
}

type ExtendJSON struct {
	Key   string `json:"key"`
	Value string `json:"val"`
}

type ResponseGaurun struct {
	Message string `json:"message"`
}

type CertificatePem struct {
	Cert []byte
	Key  []byte
}

func InitGCMClient() {
	TransportGaurun = &http.Transport{MaxIdleConnsPerHost: ConfGaurun.Core.WorkerNum}
	GCMClient = &gcm.Sender{ApiKey: ConfGaurun.Android.ApiKey}
	GCMClient.Http = &http.Client{Transport: TransportGaurun}
	GCMClient.Http.Timeout = time.Duration(ConfGaurun.Android.Timeout) * time.Second
}

func StartPushWorkers(workerNum, queueNum int) {
	QueueNotification = make(chan RequestGaurunNotification, queueNum)
	for i := 0; i < workerNum; i++ {
		go pushNotificationWorker()
	}
}

func enqueueNotifications(notifications []RequestGaurunNotification) {
	for _, notification := range notifications {
		err := validateNotification(&notification)
		if err != nil {
			LogError.Error(err.Error())
			continue
		}
		var enabledPush bool
		switch notification.Platform {
		case PlatFormIos:
			enabledPush = ConfGaurun.Ios.Enabled
		case PlatFormAndroid:
			enabledPush = ConfGaurun.Android.Enabled
		}
		if enabledPush {
			notification.IDs = make([]uint64, len(notification.Tokens))
			for i := 0; i < len(notification.IDs); i++ {
				notification.IDs[i] = numberingPush()
				LogPush(notification.IDs[i], StatusAcceptedPush, notification.Tokens[i], 0, notification, nil)
			}
			QueueNotification <- notification
		}
	}
}

func classifyByDevice(reqGaurun *RequestGaurun) ([]RequestGaurunNotification, []RequestGaurunNotification) {
	var (
		reqGaurunNotificationIos     []RequestGaurunNotification
		reqGaurunNotificationAndroid []RequestGaurunNotification
	)
	for _, notification := range reqGaurun.Notifications {
		switch notification.Platform {
		case PlatFormIos:
			reqGaurunNotificationIos = append(reqGaurunNotificationIos, notification)
		case PlatFormAndroid:
			reqGaurunNotificationAndroid = append(reqGaurunNotificationAndroid, notification)
		}
	}
	return reqGaurunNotificationIos, reqGaurunNotificationAndroid
}

func pushNotificationIos(req RequestGaurunNotification, client *apns.Client) bool {
	LogError.Debug("START push notification for iOS")

	for i, token := range req.Tokens {
		id := req.IDs[i]
		payload := apns.NewPayload()
		payload.Alert = req.Message
		payload.Badge = req.Badge
		payload.Sound = req.Sound

		pn := apns.NewPushNotification()
		pn.DeviceToken = token
		pn.Expiry = uint32(req.Expiry)
		pn.AddPayload(payload)

		if len(req.Extend) > 0 {
			for _, extend := range req.Extend {
				pn.Set(extend.Key, extend.Value)
			}
		}

		stime := time.Now()
		resp := client.Send(pn)
		etime := time.Now()
		ptime := etime.Sub(stime).Seconds()

		if resp.Error != nil {
			atomic.AddInt64(&StatGaurun.Ios.PushError, 1)
			LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, resp.Error)
			client.Conn.Close()
			client.ConnTls.Close()
			return false
		} else {
			LogPush(id, StatusSucceededPush, token, ptime, req, nil)
			atomic.AddInt64(&StatGaurun.Ios.PushSuccess, 1)
		}
	}

	client = nil
	LogError.Debug("END push notification for iOS")
	return true
}

func pushNotificationAndroid(req RequestGaurunNotification) bool {
	LogError.Debug("START push notification for Android")

	data := map[string]interface{}{"message": req.Message}
	if len(req.Extend) > 0 {
		for _, extend := range req.Extend {
			data[extend.Key] = extend.Value
		}
	}

	msg := gcm.NewMessage(data, req.Tokens...)
	msg.CollapseKey = req.CollapseKey
	msg.DelayWhileIdle = req.DelayWhileIdle
	msg.TimeToLive = req.TimeToLive

	stime := time.Now()
	resp, err := GCMClient.SendNoRetry(msg)
	etime := time.Now()
	ptime := etime.Sub(stime).Seconds()
	if err != nil {
		atomic.AddInt64(&StatGaurun.Android.PushError, 1)
		for i, token := range req.Tokens {
			LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, err)
		}
		return false
	}

	if resp.Failure > 0 {
		atomic.AddInt64(&StatGaurun.Android.PushSuccess, int64(resp.Success))
		atomic.AddInt64(&StatGaurun.Android.PushError, int64(resp.Failure))
		if len(resp.Results) == len(req.Tokens) {
			for i, token := range req.Tokens {
				if resp.Results[i].Error != "" {
					LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, errors.New(resp.Results[i].Error))
				}
			}
		}
		return true
	}

	for i, token := range req.Tokens {
		LogPush(req.IDs[i], StatusSucceededPush, token, ptime, req, nil)
	}
	StatGaurun.Android.PushSuccess += int64(len(req.Tokens))
	LogError.Debug("END push notification for Android")
	return true
}

func pushNotificationWorker() {
	var (
		success    bool
		retryMax   int
		ep         string
		apnsClient *apns.Client
		loop       int
		err        error
	)
	if ConfGaurun.Ios.Sandbox {
		ep = EpApnsSandbox
	} else {
		ep = EpApnsProd
	}

	apnsClient = nil
	loop = 0
	for {
		if apnsClient == nil {
			apnsClient, err = apns.NewClient(
				ep,
				ConfGaurun.Ios.PemCertPath,
				ConfGaurun.Ios.PemKeyPath,
				time.Duration(ConfGaurun.Ios.Timeout)*time.Second,
			)
			if err != nil {
				msg := fmt.Sprintf("failed to connect to APNS: %s", err.Error())
				LogError.Error(msg)
				apnsClient = nil
				continue
			}
			apnsClient.TimeoutWaitError = time.Duration(ConfGaurun.Ios.TimeoutError) * time.Millisecond
		}

		if ConfGaurun.Ios.KeepAliveMax > 0 && loop > ConfGaurun.Ios.KeepAliveMax {
			apnsClient.Conn.Close()
			apnsClient.ConnTls.Close()
			apnsClient = nil
			loop = 0
			continue
		}
		loop++

		notification := <-QueueNotification
		switch notification.Platform {
		case PlatFormIos:
			success = pushNotificationIos(notification, apnsClient)
			if !success {
				apnsClient = nil
			}
			retryMax = ConfGaurun.Ios.RetryMax
		case PlatFormAndroid:
			success = pushNotificationAndroid(notification)
			retryMax = ConfGaurun.Android.RetryMax
		}
		if !success && notification.Retry < retryMax {
			if len(QueueNotification) < cap(QueueNotification) {
				notification.Retry++
				QueueNotification <- notification
			}
		}
	}
}

func validateNotification(notification *RequestGaurunNotification) error {

	for _, token := range notification.Tokens {
		if len(token) == 0 {
			return errors.New("empty token")
		}
	}

	if notification.Platform < 1 || notification.Platform > 2 {
		return errors.New("invalid platform")
	}

	if len(notification.Message) == 0 {
		return errors.New("empty message")
	}

	return nil
}

func sendResponse(w http.ResponseWriter, msg string, code int) {
	var (
		respGaurun ResponseGaurun
	)

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Server", fmt.Sprintf("Gaurun %s", Version))

	w.WriteHeader(code)
	respGaurun.Message = msg
	err := json.NewEncoder(w).Encode(&respGaurun)
	if err != nil {
		msg := "Response-body could not be created"
		http.Error(w, msg, http.StatusInternalServerError)
		LogError.Error(msg)
		return
	}
}

func PushNotificationHandler(w http.ResponseWriter, r *http.Request) {
	LogAcceptedRequest(ConfGaurun.Api.PushUri, r.Method, r.Proto, r.ContentLength)
	LogError.Debug("push-request is Accepted")

	LogError.Debug("method check")
	if r.Method != "POST" {
		sendResponse(w, "method must be POST", http.StatusBadRequest)
		return
	}

	LogError.Debug("parse request body")
	var reqGaurun RequestGaurun

	err := json.NewDecoder(r.Body).Decode(&reqGaurun)
	if err != nil {
		sendResponse(w, "Request-body is malformed", http.StatusBadRequest)
		return
	}

	if len(reqGaurun.Notifications) == 0 {
		sendResponse(w, "empty notification", http.StatusBadRequest)
		return
	} else if len(reqGaurun.Notifications) > ConfGaurun.Core.NotificationMax {
		msg := fmt.Sprintf("number of notifications(%d) over limit(%d)", len(reqGaurun.Notifications), ConfGaurun.Core.NotificationMax)
		sendResponse(w, msg, http.StatusBadRequest)
		return
	}

	LogError.Debug("enqueue notification")
	go enqueueNotifications(reqGaurun.Notifications)

	LogError.Debug("response to client")
	sendResponse(w, "ok", http.StatusOK)
}
<commit_msg>fixed meaningless logic.<commit_after>package gaurun

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/alexjlockwood/gcm"
	"github.com/cubicdaiya/apns"
	"net/http"
	"sync/atomic"
	"time"
)

type RequestGaurun struct {
	Notifications []RequestGaurunNotification `json:"notifications"`
}

type RequestGaurunNotification struct {
	// Common
	Tokens   []string `json:"token"`
	Platform int      `json:"platform"`
	Message  string   `json:"message"`
	// Android
	CollapseKey    string `json:"collapse_key,omitempty"`
	DelayWhileIdle bool   `json:"delay_while_idle,omitempty"`
	TimeToLive     int    `json:"time_to_live,omitempty"`
	// iOS
	Badge  int          `json:"badge,omitempty"`
	Sound  string       `json:"sound,omitempty"`
	Expiry int          `json:"expiry,omitempty"`
	Retry  int          `json:"retry,omitempty"`
	Extend []ExtendJSON `json:"extend,omitempty"`
	// meta
	IDs []uint64 `json:"seq_id,omitempty"`
}

type ExtendJSON struct {
	Key   string `json:"key"`
	Value string `json:"val"`
}

type ResponseGaurun struct {
	Message string `json:"message"`
}

type CertificatePem struct {
	Cert []byte
	Key  []byte
}

func InitGCMClient() {
	TransportGaurun = &http.Transport{MaxIdleConnsPerHost: ConfGaurun.Core.WorkerNum}
	GCMClient = &gcm.Sender{ApiKey: ConfGaurun.Android.ApiKey}
	GCMClient.Http = &http.Client{Transport: TransportGaurun}
	GCMClient.Http.Timeout = time.Duration(ConfGaurun.Android.Timeout) * time.Second
}

func StartPushWorkers(workerNum, queueNum int) {
	QueueNotification = make(chan RequestGaurunNotification, queueNum)
	for i := 0; i < workerNum; i++ {
		go pushNotificationWorker()
	}
}

func enqueueNotifications(notifications []RequestGaurunNotification) {
	for _, notification := range notifications {
		err := validateNotification(&notification)
		if err != nil {
			LogError.Error(err.Error())
			continue
		}
		var enabledPush bool
		switch notification.Platform {
		case PlatFormIos:
			enabledPush = ConfGaurun.Ios.Enabled
		case PlatFormAndroid:
			enabledPush = ConfGaurun.Android.Enabled
		}
		if enabledPush {
			notification.IDs = make([]uint64, len(notification.Tokens))
			for i := 0; i < len(notification.IDs); i++ {
				notification.IDs[i] = numberingPush()
				LogPush(notification.IDs[i], StatusAcceptedPush, notification.Tokens[i], 0, notification, nil)
			}
			QueueNotification <- notification
		}
	}
}

func classifyByDevice(reqGaurun *RequestGaurun) ([]RequestGaurunNotification, []RequestGaurunNotification) {
	var (
		reqGaurunNotificationIos     []RequestGaurunNotification
		reqGaurunNotificationAndroid []RequestGaurunNotification
	)
	for _, notification := range reqGaurun.Notifications {
		switch notification.Platform {
		case PlatFormIos:
			reqGaurunNotificationIos = append(reqGaurunNotificationIos, notification)
		case PlatFormAndroid:
			reqGaurunNotificationAndroid = append(reqGaurunNotificationAndroid, notification)
		}
	}
	return reqGaurunNotificationIos, reqGaurunNotificationAndroid
}

func pushNotificationIos(req RequestGaurunNotification, client *apns.Client) bool {
	LogError.Debug("START push notification for iOS")

	for i, token := range req.Tokens {
		id := req.IDs[i]
		payload := apns.NewPayload()
		payload.Alert = req.Message
		payload.Badge = req.Badge
		payload.Sound = req.Sound

		pn := apns.NewPushNotification()
		pn.DeviceToken = token
		pn.Expiry = uint32(req.Expiry)
		pn.AddPayload(payload)

		if len(req.Extend) > 0 {
			for _, extend := range req.Extend {
				pn.Set(extend.Key, extend.Value)
			}
		}

		stime := time.Now()
		resp := client.Send(pn)
		etime := time.Now()
		ptime := etime.Sub(stime).Seconds()

		if resp.Error != nil {
			atomic.AddInt64(&StatGaurun.Ios.PushError, 1)
			LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, resp.Error)
			client.Conn.Close()
			client.ConnTls.Close()
			return false
		} else {
			LogPush(id, StatusSucceededPush, token, ptime, req, nil)
			atomic.AddInt64(&StatGaurun.Ios.PushSuccess, 1)
		}
	}

	client = nil
	LogError.Debug("END push notification for iOS")
	return true
}

func pushNotificationAndroid(req RequestGaurunNotification) bool {
	LogError.Debug("START push notification for Android")

	data := map[string]interface{}{"message": req.Message}
	if len(req.Extend) > 0 {
		for _, extend := range req.Extend {
			data[extend.Key] = extend.Value
		}
	}

	msg := gcm.NewMessage(data, req.Tokens...)
	msg.CollapseKey = req.CollapseKey
	msg.DelayWhileIdle = req.DelayWhileIdle
	msg.TimeToLive = req.TimeToLive

	stime := time.Now()
	resp, err := GCMClient.SendNoRetry(msg)
	etime := time.Now()
	ptime := etime.Sub(stime).Seconds()
	if err != nil {
		atomic.AddInt64(&StatGaurun.Android.PushError, 1)
		for i, token := range req.Tokens {
			LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, err)
		}
		return false
	}

	if resp.Failure > 0 {
		atomic.AddInt64(&StatGaurun.Android.PushSuccess, int64(resp.Success))
		atomic.AddInt64(&StatGaurun.Android.PushError, int64(resp.Failure))
		if len(resp.Results) == len(req.Tokens) {
			for i, token := range req.Tokens {
				if resp.Results[i].Error != "" {
					LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, errors.New(resp.Results[i].Error))
				}
			}
		}
		return true
	}

	for i, token := range req.Tokens {
		LogPush(req.IDs[i], StatusSucceededPush, token, ptime, req, nil)
	}
	StatGaurun.Android.PushSuccess += int64(len(req.Tokens))
	LogError.Debug("END push notification for Android")
	return true
}

func pushNotificationWorker() {
	var (
		success    bool
		retryMax   int
		ep         string
		apnsClient *apns.Client
		loop       int
		err        error
	)
	if ConfGaurun.Ios.Sandbox {
		ep = EpApnsSandbox
	} else {
		ep = EpApnsProd
	}

	apnsClient = nil
	loop = 0
	for {
		if apnsClient == nil {
			apnsClient, err = apns.NewClient(
				ep,
				ConfGaurun.Ios.PemCertPath,
				ConfGaurun.Ios.PemKeyPath,
				time.Duration(ConfGaurun.Ios.Timeout)*time.Second,
			)
			if err != nil {
				msg := fmt.Sprintf("failed to connect to APNS: %s", err.Error())
				LogError.Error(msg)
				apnsClient = nil
				continue
			}
			apnsClient.TimeoutWaitError = time.Duration(ConfGaurun.Ios.TimeoutError) * time.Millisecond
		}

		if ConfGaurun.Ios.KeepAliveMax > 0 && loop > ConfGaurun.Ios.KeepAliveMax {
			apnsClient.Conn.Close()
			apnsClient.ConnTls.Close()
			apnsClient = nil
			loop = 0
			continue
		}
		loop++

		notification := <-QueueNotification
		switch notification.Platform {
		case PlatFormIos:
			success = pushNotificationIos(notification, apnsClient)
			if !success {
				apnsClient = nil
			}
			retryMax = ConfGaurun.Ios.RetryMax
		case PlatFormAndroid:
			success = pushNotificationAndroid(notification)
			retryMax = ConfGaurun.Android.RetryMax
		}
		if !success && notification.Retry < retryMax {
			if len(QueueNotification) < cap(QueueNotification) {
				notification.Retry++
				QueueNotification <- notification
			}
		}
	}
}

func validateNotification(notification *RequestGaurunNotification) error {

	for _, token := range notification.Tokens {
		if len(token) == 0 {
			return errors.New("empty token")
		}
	}

	if notification.Platform < 1 || notification.Platform > 2 {
		return errors.New("invalid platform")
	}

	if len(notification.Message) == 0 {
		return errors.New("empty message")
	}

	return nil
}

func sendResponse(w http.ResponseWriter, msg string, code int) {
	var (
		respGaurun ResponseGaurun
	)

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Server", fmt.Sprintf("Gaurun %s", Version))

	w.WriteHeader(code)
	respGaurun.Message = msg
	err := json.NewEncoder(w).Encode(&respGaurun)
	if err != nil {
		// Internal Server Error(500) should be returned by right.
		// But 'code' is returned because of the limitation of json.NewEncoder and WriteHeader.
		msg := "Response-body could not be created"
		fmt.Fprintf(w, msg)
		LogError.Error(msg)
		return
	}
}

func PushNotificationHandler(w http.ResponseWriter, r *http.Request) {
	LogAcceptedRequest(ConfGaurun.Api.PushUri, r.Method, r.Proto, r.ContentLength)
	LogError.Debug("push-request is Accepted")

	LogError.Debug("method check")
	if r.Method != "POST" {
		sendResponse(w, "method must be POST", http.StatusBadRequest)
		return
	}

	LogError.Debug("parse request body")
	var reqGaurun RequestGaurun

	err := json.NewDecoder(r.Body).Decode(&reqGaurun)
	if err != nil {
		sendResponse(w, "Request-body is malformed", http.StatusBadRequest)
		return
	}

	if len(reqGaurun.Notifications) == 0 {
		sendResponse(w, "empty notification", http.StatusBadRequest)
		return
	} else if len(reqGaurun.Notifications) > ConfGaurun.Core.NotificationMax {
		msg := fmt.Sprintf("number of notifications(%d) over limit(%d)", len(reqGaurun.Notifications), ConfGaurun.Core.NotificationMax)
		sendResponse(w, msg, http.StatusBadRequest)
		return
	}

	LogError.Debug("enqueue notification")
	go enqueueNotifications(reqGaurun.Notifications)

	LogError.Debug("response to client")
	sendResponse(w, "ok", http.StatusOK)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 gcsproxy

import (
	"fmt"
	"io"

	"github.com/googlecloudplatform/gcsfuse/lease"
	"github.com/jacobsa/gcloud/gcs"
	"golang.org/x/net/context"
)

// A read-only view on a particular generation of an object in GCS. Reads may
// involve reading from a local cache.
//
// This type is not safe for concurrent access. The user must provide external
// synchronization around the methods where it is not otherwise noted.
type ReadProxy struct {
	// INVARIANT: wrapped.CheckInvariants does not panic.
	wrapped lease.ReadProxy
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func makeRefreshers(
	chunkSize uint64,
	o *gcs.Object,
	bucket gcs.Bucket) (refreshers []lease.Refresher)

// Create a view on the given GCS object generation. If rl is non-nil, it must
// contain a lease for the contents of the object and will be used when
// possible instead of re-reading the object.
//
// If the object is larger than the given chunk size, we will only read
// and cache portions of it at a time.
func NewReadProxy(
	chunkSize uint64,
	leaser lease.FileLeaser,
	bucket gcs.Bucket,
	o *gcs.Object,
	rl lease.ReadLease) (rp *ReadProxy) {
	// Set up a lease.ReadProxy.
	//
	// Special case: don't bring in the complication of a multi-read proxy if we
	// have only one refresher.
	var wrapped lease.ReadProxy
	refreshers := makeRefreshers(chunkSize, o, bucket)
	if len(refreshers) == 1 {
		wrapped = lease.NewReadProxy(leaser, refreshers[0], rl)
	} else {
		wrapped = lease.NewMultiReadProxy(leaser, refreshers, rl)
	}

	// Serve from that.
	rp = &ReadProxy{
		wrapped: wrapped,
	}

	return
}

// Panic if any internal invariants are violated.
func (rp *ReadProxy) CheckInvariants() {
	// INVARIANT: wrapped.CheckInvariants does not panic.
	rp.wrapped.CheckInvariants()
}

// Destroy any local file caches, putting the proxy into an indeterminate
// state. Should be used before dropping the final reference to the proxy.
func (rp *ReadProxy) Destroy() (err error) {
	rp.wrapped.Destroy()
	return
}

// Return a read/write lease for the contents of the object. This implicitly
// destroys the proxy, which must not be used further.
func (rp *ReadProxy) Upgrade(
	ctx context.Context) (rwl lease.ReadWriteLease, err error) {
	rwl, err = rp.wrapped.Upgrade(ctx)
	return
}

// Return the size of the object generation in bytes.
func (rp *ReadProxy) Size() (size int64) {
	size = rp.wrapped.Size()
	return
}

// Make a random access read into our view of the content. May block for
// network access.
//
// Guarantees that err != nil if n < len(buf)
func (rp *ReadProxy) ReadAt(
	ctx context.Context,
	buf []byte,
	offset int64) (n int, err error) {
	n, err = rp.wrapped.ReadAt(ctx, buf, offset)
	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// A refresher that returns the contents of a particular generation of a GCS
// object. Optionally, only a particular range is returned.
type objectRefresher struct {
	Bucket gcs.Bucket
	O      *gcs.Object
	Range  *gcs.ByteRange
}

func (r *objectRefresher) Size() (size int64) {
	if r.Range != nil {
		size = int64(r.Range.Limit - r.Range.Start)
		return
	}

	size = int64(r.O.Size)
	return
}

func (r *objectRefresher) Refresh(
	ctx context.Context) (rc io.ReadCloser, err error) {
	req := &gcs.ReadObjectRequest{
		Name:       r.O.Name,
		Generation: r.O.Generation,
		Range:      r.Range,
	}

	rc, err = r.Bucket.NewReader(ctx, req)
	if err != nil {
		err = fmt.Errorf("NewReader: %v", err)
		return
	}

	return
}
<commit_msg>makeRefreshers<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 gcsproxy

import (
	"fmt"
	"io"

	"github.com/googlecloudplatform/gcsfuse/lease"
	"github.com/jacobsa/gcloud/gcs"
	"golang.org/x/net/context"
)

// A read-only view on a particular generation of an object in GCS. Reads may
// involve reading from a local cache.
//
// This type is not safe for concurrent access. The user must provide external
// synchronization around the methods where it is not otherwise noted.
type ReadProxy struct {
	// INVARIANT: wrapped.CheckInvariants does not panic.
	wrapped lease.ReadProxy
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func makeRefreshers(
	chunkSize uint64,
	o *gcs.Object,
	bucket gcs.Bucket) (refreshers []lease.Refresher) {
	// Iterate over each chunk of the object.
	for startOff := uint64(0); startOff < o.Size; startOff += chunkSize {
		r := gcs.ByteRange{startOff, startOff + chunkSize}

		// Clip the range so that objectRefresher can report the correct size.
		if r.Limit > o.Size {
			r.Limit = o.Size
		}

		refresher := &objectRefresher{
			O:      o,
			Bucket: bucket,
			Range:  &r,
		}

		refreshers = append(refreshers, refresher)
	}

	return
}

// Create a view on the given GCS object generation. If rl is non-nil, it must
// contain a lease for the contents of the object and will be used when
// possible instead of re-reading the object.
//
// If the object is larger than the given chunk size, we will only read
// and cache portions of it at a time.
func NewReadProxy(
	chunkSize uint64,
	leaser lease.FileLeaser,
	bucket gcs.Bucket,
	o *gcs.Object,
	rl lease.ReadLease) (rp *ReadProxy) {
	// Set up a lease.ReadProxy.
	//
	// Special case: don't bring in the complication of a multi-read proxy if we
	// have only one refresher.
	var wrapped lease.ReadProxy
	refreshers := makeRefreshers(chunkSize, o, bucket)
	if len(refreshers) == 1 {
		wrapped = lease.NewReadProxy(leaser, refreshers[0], rl)
	} else {
		wrapped = lease.NewMultiReadProxy(leaser, refreshers, rl)
	}

	// Serve from that.
	rp = &ReadProxy{
		wrapped: wrapped,
	}

	return
}

// Panic if any internal invariants are violated.
func (rp *ReadProxy) CheckInvariants() {
	// INVARIANT: wrapped.CheckInvariants does not panic.
	rp.wrapped.CheckInvariants()
}

// Destroy any local file caches, putting the proxy into an indeterminate
// state. Should be used before dropping the final reference to the proxy.
func (rp *ReadProxy) Destroy() (err error) {
	rp.wrapped.Destroy()
	return
}

// Return a read/write lease for the contents of the object. This implicitly
// destroys the proxy, which must not be used further.
func (rp *ReadProxy) Upgrade(
	ctx context.Context) (rwl lease.ReadWriteLease, err error) {
	rwl, err = rp.wrapped.Upgrade(ctx)
	return
}

// Return the size of the object generation in bytes.
func (rp *ReadProxy) Size() (size int64) {
	size = rp.wrapped.Size()
	return
}

// Make a random access read into our view of the content. May block for
// network access.
//
// Guarantees that err != nil if n < len(buf)
func (rp *ReadProxy) ReadAt(
	ctx context.Context,
	buf []byte,
	offset int64) (n int, err error) {
	n, err = rp.wrapped.ReadAt(ctx, buf, offset)
	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// A refresher that returns the contents of a particular generation of a GCS
// object. Optionally, only a particular range is returned.
type objectRefresher struct {
	Bucket gcs.Bucket
	O      *gcs.Object
	Range  *gcs.ByteRange
}

func (r *objectRefresher) Size() (size int64) {
	if r.Range != nil {
		size = int64(r.Range.Limit - r.Range.Start)
		return
	}

	size = int64(r.O.Size)
	return
}

func (r *objectRefresher) Refresh(
	ctx context.Context) (rc io.ReadCloser, err error) {
	req := &gcs.ReadObjectRequest{
		Name:       r.O.Name,
		Generation: r.O.Generation,
		Range:      r.Range,
	}

	rc, err = r.Bucket.NewReader(ctx, req)
	if err != nil {
		err = fmt.Errorf("NewReader: %v", err)
		return
	}

	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2009 The Go Authors. All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:

//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Deep equality test via reflection

package testutils

import (
	"fmt"
	"reflect"
	"testing"
	"unsafe"

	"github.com/davecgh/go-spew/spew"
	assert "github.com/stretchr/testify/require"
)

// During deepValueEqual, must keep track of checks that are
// in progress. The comparison algorithm assumes that all
// checks in progress are true when it reencounters them.
// Visited comparisons are stored in a map indexed by visit.
type visit struct {
	a1  unsafe.Pointer
	a2  unsafe.Pointer
	typ reflect.Type
}

// Tests for deep equality using reflected types. The map argument tracks
// comparisons that have already been seen, which allows short circuiting on
// recursive types.
func deepValueEqual(v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
	if !v1.IsValid() || !v2.IsValid() {
		return v1.IsValid() == v2.IsValid()
	}
	if v1.Type() != v2.Type() {
		return false
	}

	// We want to avoid putting more in the visited map than we need to.
	// For any possible reference cycle that might be encountered,
	// hard(t) needs to return true for at least one of the types in the cycle.
	hard := func(k reflect.Kind) bool {
		switch k {
		case reflect.Map, reflect.Slice, reflect.Ptr, reflect.Interface:
			return true
		}
		return false
	}

	if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) {
		addr1 := unsafe.Pointer(v1.UnsafeAddr())
		addr2 := unsafe.Pointer(v2.UnsafeAddr())
		if uintptr(addr1) > uintptr(addr2) {
			// Canonicalize order to reduce number of entries in visited.
			// Assumes non-moving garbage collector.
			addr1, addr2 = addr2, addr1
		}

		// Short circuit if references are already seen.
		typ := v1.Type()
		v := visit{addr1, addr2, typ}
		if visited[v] {
			return true
		}

		// Remember for later.
		visited[v] = true
	}

	switch v1.Kind() {
	case reflect.Array:
		for i := 0; i < v1.Len(); i++ {
			if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
				return false
			}
		}
		return true
	case reflect.Slice:
		if v1.IsNil() != v2.IsNil() {
			return false
		}
		if v1.Len() != v2.Len() {
			return false
		}
		if v1.Pointer() == v2.Pointer() {
			return true
		}
		for i := 0; i < v1.Len(); i++ {
			if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
				return false
			}
		}
		return true
	case reflect.Interface:
		if v1.IsNil() || v2.IsNil() {
			return v1.IsNil() == v2.IsNil()
		}
		return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
	case reflect.Ptr:
		if v1.Pointer() == v2.Pointer() {
			return true
		}
		return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
	case reflect.Struct:

		// https://stackoverflow.com/a/43918797 tells us that we can make an addressable
		// copy of the struct (if it isn't addressable already)
		// This will allow us to compare unexported fields later using unsafe.Pointer
		// Note, we shouldn't just blindly make a new, addressable copy of the element,
		// because that defeats the infinite recursion protection from above.
		v1copy := v1
		if !v1.CanAddr() {
			v1copy = reflect.New(v1.Type()).Elem()
			v1copy.Set(v1)
		}
		v2copy := v2
		if !v2.CanAddr() {
			v2copy = reflect.New(v2.Type()).Elem()
			v2copy.Set(v2)
		}

		// If Equal is defined on the struct, call it and use the result if valid.
		// This is especially helpful in Go 1.9, with the introduction of monotonic
		// times. reflect.DeepEqual() would compare the (unexported) monotonic field
		// and incorrectly deduce times as being not equal. By calling the Equal
		// method when available, we allow struct writers to control the DeepEqual
		// behavior.

		// Do PtrTo to expose all methods on the type and pointers of the type (the
		// former comes with the latter)
		tp := reflect.PtrTo(v1.Type())
		if equal, found := tp.MethodByName("Equal"); found {
			if equal.Type.NumIn() == 2 && // Two inputs (caller + object checking for equality)
				equal.Type.In(1).AssignableTo(v2copy.Type()) && // make sure v2copy can be passed in to the function in the second slot.
				equal.Type.NumOut() == 1 && // One output, which is exactly a bool
				equal.Type.Out(0).Kind() == reflect.Bool {
				// Actually call the function. Since we requested methods with a pointer reciever
				// we need to use Addr() on the caller object.
				retvals := equal.Func.Call([]reflect.Value{v1copy.Addr(), v2copy})
				if len(retvals) == 1 {
					if isEqual, ok := retvals[0].Interface().(bool); ok {
						return isEqual
					}
				}
				// If any of that failed, the Equal method we found isn't something we can use, so
				// fallthrough to the regular field by field comparison.
			}
		}
		for i, n := 0, v1.NumField(); i < n; i++ {
			// Then, we can use unsafe.Pointer to get access to the field, even if it is
			// unexported.
			f1 := v1copy.Field(i)
			if !f1.CanInterface() {
				f1 = reflect.NewAt(f1.Type(), unsafe.Pointer(f1.UnsafeAddr())).Elem()
			}
			f2 := v2copy.Field(i)
			if !f2.CanInterface() {
				f2 = reflect.NewAt(f2.Type(), unsafe.Pointer(f2.UnsafeAddr())).Elem()
			}
			if !deepValueEqual(f1, f2, visited, depth+1) {
				return false
			}
		}
		return true
	case reflect.Map:
		if v1.IsNil() != v2.IsNil() {
			return false
		}
		if v1.Len() != v2.Len() {
			return false
		}
		if v1.Pointer() == v2.Pointer() {
			return true
		}
		for _, k := range v1.MapKeys() {
			val1 := v1.MapIndex(k)
			val2 := v2.MapIndex(k)
			if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
				return false
			}
		}
		return true
	case reflect.Func:
		if v1.IsNil() && v2.IsNil() {
			return true
		}
		// Can't do better than this:
		return false
	default:
		// Normal equality suffices

		// This is different from the reflect.DeepEqual because we don't have access to
		// some unexported functions in the reflect package that let us get access
		// to unexported variables. Because of the unsafe.Pointer() logic done above
		// we can safely make a call to Interface(), which would typically fail for
		// unexported fields.
		return v1.Interface() == v2.Interface()
	}
}

// DeepEqual reports whether x and y are ``deeply equal,'' defined as follows.
// Two values of identical type are deeply equal if one of the following cases applies.
// Values of distinct types are never deeply equal.
//
// Array values are deeply equal when their corresponding elements are deeply equal.
//
// Struct values are deeply equal if their corresponding fields,
// both exported and unexported, are deeply equal.
//
// Func values are deeply equal if both are nil; otherwise they are not deeply equal.
//
// Interface values are deeply equal if they hold deeply equal concrete values.
//
// Map values are deeply equal when all of the following are true:
// they are both nil or both non-nil, they have the same length,
// and either they are the same map object or their corresponding keys
// (matched using Go equality) map to deeply equal values.
//
// Pointer values are deeply equal if they are equal using Go's == operator
// or if they point to deeply equal values.
//
// Slice values are deeply equal when all of the following are true:
// they are both nil or both non-nil, they have the same length,
// and either they point to the same initial entry of the same underlying array
// (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal.
// Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil))
// are not deeply equal.
//
// Other values - numbers, bools, strings, and channels - are deeply equal
// if they are equal using Go's == operator.
//
// In general DeepEqual is a recursive relaxation of Go's == operator.
// However, this idea is impossible to implement without some inconsistency.
// Specifically, it is possible for a value to be unequal to itself,
// either because it is of func type (uncomparable in general)
// or because it is a floating-point NaN value (not equal to itself in floating-point comparison),
// or because it is an array, struct, or interface containing
// such a value.
// On the other hand, pointer values are always equal to themselves,
// even if they point at or contain such problematic values,
// because they compare equal using Go's == operator, and that
// is a sufficient condition to be deeply equal, regardless of content.
// DeepEqual has been defined so that the same short-cut applies
// to slices and maps: if x and y are the same slice or the same map,
// they are deeply equal regardless of content.
//
// As DeepEqual traverses the data values it may find a cycle. The
// second and subsequent times that DeepEqual compares two pointer
// values that have been compared before, it treats the values as
// equal rather than examining the values to which they point.
// This ensures that DeepEqual terminates.

// DeepEqual has been modified to ignore the monotonic portion of time.Time objects.
func DeepEqual(x, y interface{}) bool {
	if x == nil || y == nil {
		return x == y
	}
	v1 := reflect.ValueOf(x)
	v2 := reflect.ValueOf(y)
	if v1.Type() != v2.Type() {
		return false
	}
	return deepValueEqual(v1, v2, make(map[visit]bool), 0)
}

// AssertDeepEqual fails the test if the two objects do not pass reflect.DeepEqual.
func AssertDeepEqual(t *testing.T, a, b interface{}) {
	if !DeepEqual(a, b) {
		assert.FailNow(t, fmt.Sprintf("Objects do not match: \na:\n%s\n\nb:\n%s\n", spew.Sprint(a), spew.Sprint(b)))
	}
}
<commit_msg>Make AssertDeepEqual's diff nicer<commit_after>// Copyright (c) 2009 The Go Authors. All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:

//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Deep equality test via reflection

package testutils

import (
	"fmt"
	"reflect"
	"testing"
	"unsafe"

	"github.com/davecgh/go-spew/spew"
	"github.com/pmezard/go-difflib/difflib"
	assert "github.com/stretchr/testify/require"
)

// During deepValueEqual, must keep track of checks that are
// in progress. The comparison algorithm assumes that all
// checks in progress are true when it reencounters them.
// Visited comparisons are stored in a map indexed by visit.
type visit struct {
	a1  unsafe.Pointer
	a2  unsafe.Pointer
	typ reflect.Type
}

// Tests for deep equality using reflected types. The map argument tracks
// comparisons that have already been seen, which allows short circuiting on
// recursive types.
func deepValueEqual(v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
	if !v1.IsValid() || !v2.IsValid() {
		return v1.IsValid() == v2.IsValid()
	}
	if v1.Type() != v2.Type() {
		return false
	}

	// We want to avoid putting more in the visited map than we need to.
	// For any possible reference cycle that might be encountered,
	// hard(t) needs to return true for at least one of the types in the cycle.
	hard := func(k reflect.Kind) bool {
		switch k {
		case reflect.Map, reflect.Slice, reflect.Ptr, reflect.Interface:
			return true
		}
		return false
	}

	if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) {
		addr1 := unsafe.Pointer(v1.UnsafeAddr())
		addr2 := unsafe.Pointer(v2.UnsafeAddr())
		if uintptr(addr1) > uintptr(addr2) {
			// Canonicalize order to reduce number of entries in visited.
			// Assumes non-moving garbage collector.
			addr1, addr2 = addr2, addr1
		}

		// Short circuit if references are already seen.
		typ := v1.Type()
		v := visit{addr1, addr2, typ}
		if visited[v] {
			return true
		}

		// Remember for later.
		visited[v] = true
	}

	switch v1.Kind() {
	case reflect.Array:
		for i := 0; i < v1.Len(); i++ {
			if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
				return false
			}
		}
		return true
	case reflect.Slice:
		if v1.IsNil() != v2.IsNil() {
			return false
		}
		if v1.Len() != v2.Len() {
			return false
		}
		if v1.Pointer() == v2.Pointer() {
			return true
		}
		for i := 0; i < v1.Len(); i++ {
			if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
				return false
			}
		}
		return true
	case reflect.Interface:
		if v1.IsNil() || v2.IsNil() {
			return v1.IsNil() == v2.IsNil()
		}
		return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
	case reflect.Ptr:
		if v1.Pointer() == v2.Pointer() {
			return true
		}
		return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
	case reflect.Struct:

		// https://stackoverflow.com/a/43918797 tells us that we can make an addressable
		// copy of the struct (if it isn't addressable already)
		// This will allow us to compare unexported fields later using unsafe.Pointer
		// Note, we shouldn't just blindly make a new, addressable copy of the element,
		// because that defeats the infinite recursion protection from above.
		v1copy := v1
		if !v1.CanAddr() {
			v1copy = reflect.New(v1.Type()).Elem()
			v1copy.Set(v1)
		}
		v2copy := v2
		if !v2.CanAddr() {
			v2copy = reflect.New(v2.Type()).Elem()
			v2copy.Set(v2)
		}

		// If Equal is defined on the struct, call it and use the result if valid.
		// This is especially helpful in Go 1.9, with the introduction of monotonic
		// times. reflect.DeepEqual() would compare the (unexported) monotonic field
		// and incorrectly deduce times as being not equal. By calling the Equal
		// method when available, we allow struct writers to control the DeepEqual
		// behavior.

		// Do PtrTo to expose all methods on the type and pointers of the type (the
		// former comes with the latter)
		tp := reflect.PtrTo(v1.Type())
		if equal, found := tp.MethodByName("Equal"); found {
			if equal.Type.NumIn() == 2 && // Two inputs (caller + object checking for equality)
				equal.Type.In(1).AssignableTo(v2copy.Type()) && // make sure v2copy can be passed in to the function in the second slot.
				equal.Type.NumOut() == 1 && // One output, which is exactly a bool
				equal.Type.Out(0).Kind() == reflect.Bool {
				// Actually call the function. Since we requested methods with a pointer reciever
				// we need to use Addr() on the caller object.
				retvals := equal.Func.Call([]reflect.Value{v1copy.Addr(), v2copy})
				if len(retvals) == 1 {
					if isEqual, ok := retvals[0].Interface().(bool); ok {
						return isEqual
					}
				}
				// If any of that failed, the Equal method we found isn't something we can use, so
				// fallthrough to the regular field by field comparison.
			}
		}
		for i, n := 0, v1.NumField(); i < n; i++ {
			// Then, we can use unsafe.Pointer to get access to the field, even if it is
			// unexported.
			f1 := v1copy.Field(i)
			if !f1.CanInterface() {
				f1 = reflect.NewAt(f1.Type(), unsafe.Pointer(f1.UnsafeAddr())).Elem()
			}
			f2 := v2copy.Field(i)
			if !f2.CanInterface() {
				f2 = reflect.NewAt(f2.Type(), unsafe.Pointer(f2.UnsafeAddr())).Elem()
			}
			if !deepValueEqual(f1, f2, visited, depth+1) {
				return false
			}
		}
		return true
	case reflect.Map:
		if v1.IsNil() != v2.IsNil() {
			return false
		}
		if v1.Len() != v2.Len() {
			return false
		}
		if v1.Pointer() == v2.Pointer() {
			return true
		}
		for _, k := range v1.MapKeys() {
			val1 := v1.MapIndex(k)
			val2 := v2.MapIndex(k)
			if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
				return false
			}
		}
		return true
	case reflect.Func:
		if v1.IsNil() && v2.IsNil() {
			return true
		}
		// Can't do better than this:
		return false
	default:
		// Normal equality suffices

		// This is different from the reflect.DeepEqual because we don't have access to
		// some unexported functions in the reflect package that let us get access
		// to unexported variables. Because of the unsafe.Pointer() logic done above
		// we can safely make a call to Interface(), which would typically fail for
		// unexported fields.
		return v1.Interface() == v2.Interface()
	}
}

// DeepEqual reports whether x and y are ``deeply equal,'' defined as follows.
// Two values of identical type are deeply equal if one of the following cases applies.
// Values of distinct types are never deeply equal.
//
// Array values are deeply equal when their corresponding elements are deeply equal.
//
// Struct values are deeply equal if their corresponding fields,
// both exported and unexported, are deeply equal.
//
// Func values are deeply equal if both are nil; otherwise they are not deeply equal.
//
// Interface values are deeply equal if they hold deeply equal concrete values.
//
// Map values are deeply equal when all of the following are true:
// they are both nil or both non-nil, they have the same length,
// and either they are the same map object or their corresponding keys
// (matched using Go equality) map to deeply equal values.
//
// Pointer values are deeply equal if they are equal using Go's == operator
// or if they point to deeply equal values.
//
// Slice values are deeply equal when all of the following are true:
// they are both nil or both non-nil, they have the same length,
// and either they point to the same initial entry of the same underlying array
// (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal.
// Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil))
// are not deeply equal.
//
// Other values - numbers, bools, strings, and channels - are deeply equal
// if they are equal using Go's == operator.
//
// In general DeepEqual is a recursive relaxation of Go's == operator.
// However, this idea is impossible to implement without some inconsistency.
// Specifically, it is possible for a value to be unequal to itself,
// either because it is of func type (uncomparable in general)
// or because it is a floating-point NaN value (not equal to itself in floating-point comparison),
// or because it is an array, struct, or interface containing
// such a value.
// On the other hand, pointer values are always equal to themselves,
// even if they point at or contain such problematic values,
// because they compare equal using Go's == operator, and that
// is a sufficient condition to be deeply equal, regardless of content.
// DeepEqual has been defined so that the same short-cut applies
// to slices and maps: if x and y are the same slice or the same map,
// they are deeply equal regardless of content.
//
// As DeepEqual traverses the data values it may find a cycle. The
// second and subsequent times that DeepEqual compares two pointer
// values that have been compared before, it treats the values as
// equal rather than examining the values to which they point.
// This ensures that DeepEqual terminates.

// DeepEqual has been modified to ignore the monotonic portion of time.Time objects.
func DeepEqual(x, y interface{}) bool {
	if x == nil || y == nil {
		return x == y
	}
	v1 := reflect.ValueOf(x)
	v2 := reflect.ValueOf(y)
	if v1.Type() != v2.Type() {
		return false
	}
	return deepValueEqual(v1, v2, make(map[visit]bool), 0)
}

// Twiddle this flag for the complete spew.SDump of an object
// This is useful when pumped into a diff tool, but likely extreme
// overkill for most uses. The diff that is created by default should
// be enough for the average test.
var superVerbose = false

// AssertDeepEqual fails the test if the two objects do not pass reflect.DeepEqual.
func AssertDeepEqual(t *testing.T, expected, actual interface{}) {
	if !DeepEqual(expected, actual) {
		// The formatting is inspired by stretchr/testify's assert.Equal() output.
		extra := ""
		if doDetailedDiff(expected, actual) {
			e := spewConfig.Sdump(expected)
			a := spewConfig.Sdump(actual)

			diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
				A:        difflib.SplitLines(e),
				B:        difflib.SplitLines(a),
				FromFile: "Expected",
				FromDate: "",
				ToFile:   "Actual",
				ToDate:   "",
				Context:  2,
			})

			extra = "\n\nDiff:\n" + diff
		}

		if superVerbose {
			assert.FailNow(t, fmt.Sprintf("Objects do not match: \na:\n%s\n\nb:\n%s\n%s", spew.Sdump(expected), spew.Sdump(actual), extra))
		} else {
			assert.FailNow(t, fmt.Sprintf("Objects do not match: \na:\n%#v\n\nb:\n%#v\n%s", expected, actual, extra))
		}
	}
}

// doDetailedDiff returns true if doing a detailed diff would help. This means if
// the two objects are the same type and are one of the complicated types:
// e.g. Map, Slice, Struct, etc.
func doDetailedDiff(e, a interface{}) bool {
	if e == nil || a == nil {
		return false
	}

	et := reflect.TypeOf(e)
	ek := et.Kind()
	if ek == reflect.Ptr {
		et = et.Elem()
		ek = et.Kind()
	}
	at := reflect.TypeOf(a)
	ak := at.Kind()
	if ak == reflect.Ptr {
		at = at.Elem()
		ak = at.Kind()
	}

	if et != at {
		return false
	}

	if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array {
		return false
	}
	return true
}

var spewConfig = spew.ConfigState{
	Indent:                  "  ",
	DisablePointerAddresses: true,
	DisableCapacities:       true,
	SortKeys:                true,
}
<|endoftext|>


<commit_before>package genapp

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"sort"

	"github.com/goadesign/goa/design"
	"github.com/goadesign/goa/goagen/codegen"
	"github.com/goadesign/goa/goagen/utils"
)

// Generator is the application code generator.
type Generator struct {
	API      *design.APIDefinition // The API definition
	OutDir   string                // Path to output directory
	Target   string                // Name of generated package
	NoTest   bool                  // Whether to skip test generation
	genfiles []string              // Generated files
}

// Generate is the generator entry point called by the meta generator.
func Generate() (files []string, err error) {
	var (
		outDir, target, ver string
		notest              bool
	)

	set := flag.NewFlagSet("app", flag.PanicOnError)
	set.String("design", "", "")
	set.StringVar(&outDir, "out", "", "")
	set.StringVar(&target, "pkg", "app", "")
	set.StringVar(&ver, "version", "", "")
	set.BoolVar(&notest, "notest", false, "")
	set.Parse(os.Args[1:])
	outDir = filepath.Join(outDir, target)

	if err := codegen.CheckVersion(ver); err != nil {
		return nil, err
	}

	target = codegen.Goify(target, false)
	g := &Generator{OutDir: outDir, Target: target, NoTest: notest, API: design.Design}

	return g.Generate()
}

// Generate the application code, implement codegen.Generator.
func (g *Generator) Generate() (_ []string, err error) {
	if g.API == nil {
		return nil, fmt.Errorf("missing API definition, make sure design is properly initialized")
	}

	go utils.Catch(nil, func() { g.Cleanup() })

	defer func() {
		if err != nil {
			g.Cleanup()
		}
	}()

	codegen.Reserved[g.Target] = true

	os.RemoveAll(g.OutDir)

	if err := os.MkdirAll(g.OutDir, 0755); err != nil {
		return nil, err
	}
	g.genfiles = []string{g.OutDir}
	if err := g.generateContexts(); err != nil {
		return nil, err
	}
	if err := g.generateControllers(); err != nil {
		return nil, err
	}
	if err := g.generateSecurity(); err != nil {
		return nil, err
	}
	if err := g.generateHrefs(); err != nil {
		return nil, err
	}
	if err := g.generateMediaTypes(); err != nil {
		return nil, err
	}
	if err := g.generateUserTypes(); err != nil {
		return nil, err
	}
	if !g.NoTest {
		if err := g.generateResourceTest(); err != nil {
			return nil, err
		}
	}

	return g.genfiles, nil
}

// Cleanup removes the entire "app" directory if it was created by this generator.
func (g *Generator) Cleanup() {
	if len(g.genfiles) == 0 {
		return
	}
	os.RemoveAll(g.OutDir)
	g.genfiles = nil
}

// generateContexts iterates through the API resources and actions and generates the action
// contexts.
func (g *Generator) generateContexts() error {
	ctxFile := filepath.Join(g.OutDir, "contexts.go")
	ctxWr, err := NewContextsWriter(ctxFile)
	if err != nil {
		panic(err) // bug
	}
	title := fmt.Sprintf("%s: Application Contexts", g.API.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("fmt"),
		codegen.SimpleImport("golang.org/x/net/context"),
		codegen.SimpleImport("strconv"),
		codegen.SimpleImport("strings"),
		codegen.SimpleImport("time"),
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.NewImport("uuid", "github.com/satori/go.uuid"),
	}
	g.genfiles = append(g.genfiles, ctxFile)
	ctxWr.WriteHeader(title, g.Target, imports)
	err = g.API.IterateResources(func(r *design.ResourceDefinition) error {
		return r.IterateActions(func(a *design.ActionDefinition) error {
			ctxName := codegen.Goify(a.Name, true) + codegen.Goify(a.Parent.Name, true) + "Context"
			headers := r.Headers.Merge(a.Headers)
			if headers != nil && len(headers.Type.ToObject()) == 0 {
				headers = nil // So that {{if .Headers}} returns false in templates
			}
			params := a.AllParams()
			if params != nil && len(params.Type.ToObject()) == 0 {
				params = nil // So that {{if .Params}} returns false in templates
			}

			non101 := make(map[string]*design.ResponseDefinition)
			for k, v := range a.Responses {
				if v.Status != 101 {
					non101[k] = v
				}
			}
			ctxData := ContextTemplateData{
				Name:         ctxName,
				ResourceName: r.Name,
				ActionName:   a.Name,
				Payload:      a.Payload,
				Params:       params,
				Headers:      headers,
				Routes:       a.Routes,
				Responses:    non101,
				API:          g.API,
				DefaultPkg:   g.Target,
				Security:     a.Security,
			}
			return ctxWr.Execute(&ctxData)
		})
	})
	if err != nil {
		return err
	}
	return ctxWr.FormatCode()
}

// generateControllers iterates through the API resources and generates the low level
// controllers.
func (g *Generator) generateControllers() error {
	ctlFile := filepath.Join(g.OutDir, "controllers.go")
	ctlWr, err := NewControllersWriter(ctlFile)
	if err != nil {
		panic(err) // bug
	}
	title := fmt.Sprintf("%s: Application Controllers", g.API.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("net/http"),
		codegen.SimpleImport("fmt"),
		codegen.SimpleImport("golang.org/x/net/context"),
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.SimpleImport("github.com/goadesign/goa/cors"),
		codegen.SimpleImport("regexp"),
	}
	encoders, err := BuildEncoders(g.API.Produces, true)
	if err != nil {
		return err
	}
	decoders, err := BuildEncoders(g.API.Consumes, false)
	if err != nil {
		return err
	}
	encoderImports := make(map[string]bool)
	for _, data := range encoders {
		encoderImports[data.PackagePath] = true
	}
	for _, data := range decoders {
		encoderImports[data.PackagePath] = true
	}
	var packagePaths []string
	for packagePath := range encoderImports {
		if packagePath != "github.com/goadesign/goa" {
			packagePaths = append(packagePaths, packagePath)
		}
	}
	sort.Strings(packagePaths)
	for _, packagePath := range packagePaths {
		imports = append(imports, codegen.SimpleImport(packagePath))
	}
	ctlWr.WriteHeader(title, g.Target, imports)
	ctlWr.WriteInitService(encoders, decoders)

	var controllersData []*ControllerTemplateData
	err = g.API.IterateResources(func(r *design.ResourceDefinition) error {
		// Create file servers for all directory file servers that serve index.html.
		fileServers := r.FileServers
		for _, fs := range r.FileServers {
			if fs.IsDir() {
				rpath := design.WildcardRegex.ReplaceAllLiteralString(fs.RequestPath, "")
				rpath += "/"
				fileServers = append(fileServers, &design.FileServerDefinition{
					Parent:      fs.Parent,
					Description: fs.Description,
					Docs:        fs.Docs,
					FilePath:    filepath.Join(fs.FilePath, "index.html"),
					RequestPath: rpath,
					Metadata:    fs.Metadata,
					Security:    fs.Security,
				})
			}
		}
		data := &ControllerTemplateData{
			API:            g.API,
			Resource:       codegen.Goify(r.Name, true),
			PreflightPaths: r.PreflightPaths(),
			FileServers:    fileServers,
		}
		ierr := r.IterateActions(func(a *design.ActionDefinition) error {
			context := fmt.Sprintf("%s%sContext", codegen.Goify(a.Name, true), codegen.Goify(r.Name, true))
			unmarshal := fmt.Sprintf("unmarshal%s%sPayload", codegen.Goify(a.Name, true), codegen.Goify(r.Name, true))
			action := map[string]interface{}{
				"Name":            codegen.Goify(a.Name, true),
				"Routes":          a.Routes,
				"Context":         context,
				"Unmarshal":       unmarshal,
				"Payload":         a.Payload,
				"PayloadOptional": a.PayloadOptional,
				"Security":        a.Security,
			}
			data.Actions = append(data.Actions, action)
			return nil
		})
		if ierr != nil {
			return ierr
		}
		if len(data.Actions) > 0 || len(data.FileServers) > 0 {
			data.Encoders = encoders
			data.Decoders = decoders
			data.Origins = r.AllOrigins()
			controllersData = append(controllersData, data)
		}
		return nil
	})
	if err != nil {
		return err
	}
	g.genfiles = append(g.genfiles, ctlFile)
	if err = ctlWr.Execute(controllersData); err != nil {
		return err
	}
	return ctlWr.FormatCode()
}

// generateControllers iterates through the API resources and generates the low level
// controllers.
func (g *Generator) generateSecurity() error {
	if len(g.API.SecuritySchemes) == 0 {
		return nil
	}

	secFile := filepath.Join(g.OutDir, "security.go")
	secWr, err := NewSecurityWriter(secFile)
	if err != nil {
		panic(err) // bug
	}

	title := fmt.Sprintf("%s: Application Security", g.API.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("net/http"),
		codegen.SimpleImport("errors"),
		codegen.SimpleImport("golang.org/x/net/context"),
		codegen.SimpleImport("github.com/goadesign/goa"),
	}
	secWr.WriteHeader(title, g.Target, imports)

	g.genfiles = append(g.genfiles, secFile)

	if err = secWr.Execute(design.Design.SecuritySchemes); err != nil {
		return err
	}

	return secWr.FormatCode()
}

// generateHrefs iterates through the API resources and generates the href factory methods.
func (g *Generator) generateHrefs() error {
	hrefFile := filepath.Join(g.OutDir, "hrefs.go")
	resWr, err := NewResourcesWriter(hrefFile)
	if err != nil {
		panic(err) // bug
	}
	title := fmt.Sprintf("%s: Application Resource Href Factories", g.API.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("fmt"),
	}
	resWr.WriteHeader(title, g.Target, imports)
	err = g.API.IterateResources(func(r *design.ResourceDefinition) error {
		m := g.API.MediaTypeWithIdentifier(r.MediaType)
		var identifier string
		if m != nil {
			identifier = m.Identifier
		} else {
			identifier = "text/plain"
		}
		data := ResourceData{
			Name:              codegen.Goify(r.Name, true),
			Identifier:        identifier,
			Description:       r.Description,
			Type:              m,
			CanonicalTemplate: codegen.CanonicalTemplate(r),
			CanonicalParams:   codegen.CanonicalParams(r),
		}
		return resWr.Execute(&data)
	})
	g.genfiles = append(g.genfiles, hrefFile)
	if err != nil {
		return err
	}
	return resWr.FormatCode()
}

// generateMediaTypes iterates through the media types and generate the data structures and
// marshaling code.
func (g *Generator) generateMediaTypes() error {
	mtFile := filepath.Join(g.OutDir, "media_types.go")
	mtWr, err := NewMediaTypesWriter(mtFile)
	if err != nil {
		panic(err) // bug
	}
	title := fmt.Sprintf("%s: Application Media Types", g.API.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.SimpleImport("fmt"),
		codegen.SimpleImport("time"),
		codegen.NewImport("uuid", "github.com/satori/go.uuid"),
	}
	mtWr.WriteHeader(title, g.Target, imports)
	err = g.API.IterateMediaTypes(func(mt *design.MediaTypeDefinition) error {
		if mt.IsError() {
			return nil
		}
		if mt.Type.IsObject() || mt.Type.IsArray() {
			return mtWr.Execute(mt)
		}
		return nil
	})
	g.genfiles = append(g.genfiles, mtFile)
	if err != nil {
		return err
	}
	return mtWr.FormatCode()
}

// generateUserTypes iterates through the user types and generates the data structures and
// marshaling code.
func (g *Generator) generateUserTypes() error {
	utFile := filepath.Join(g.OutDir, "user_types.go")
	utWr, err := NewUserTypesWriter(utFile)
	if err != nil {
		panic(err) // bug
	}
	title := fmt.Sprintf("%s: Application User Types", g.API.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.SimpleImport("fmt"),
		codegen.SimpleImport("time"),
	}
	utWr.WriteHeader(title, g.Target, imports)
	err = g.API.IterateUserTypes(func(t *design.UserTypeDefinition) error {
		return utWr.Execute(t)
	})
	g.genfiles = append(g.genfiles, utFile)
	if err != nil {
		return err
	}
	return utWr.FormatCode()
}
<commit_msg>Add missing uuid import to user_types.go (#728)<commit_after>package genapp

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"sort"

	"github.com/goadesign/goa/design"
	"github.com/goadesign/goa/goagen/codegen"
	"github.com/goadesign/goa/goagen/utils"
)

// Generator is the application code generator.
type Generator struct {
	API      *design.APIDefinition // The API definition
	OutDir   string                // Path to output directory
	Target   string                // Name of generated package
	NoTest   bool                  // Whether to skip test generation
	genfiles []string              // Generated files
}

// Generate is the generator entry point called by the meta generator.
func Generate() (files []string, err error) {
	var (
		outDir, target, ver string
		notest              bool
	)

	set := flag.NewFlagSet("app", flag.PanicOnError)
	set.String("design", "", "")
	set.StringVar(&outDir, "out", "", "")
	set.StringVar(&target, "pkg", "app", "")
	set.StringVar(&ver, "version", "", "")
	set.BoolVar(&notest, "notest", false, "")
	set.Parse(os.Args[1:])
	outDir = filepath.Join(outDir, target)

	if err := codegen.CheckVersion(ver); err != nil {
		return nil, err
	}

	target = codegen.Goify(target, false)
	g := &Generator{OutDir: outDir, Target: target, NoTest: notest, API: design.Design}

	return g.Generate()
}

// Generate the application code, implement codegen.Generator.
func (g *Generator) Generate() (_ []string, err error) {
	if g.API == nil {
		return nil, fmt.Errorf("missing API definition, make sure design is properly initialized")
	}

	go utils.Catch(nil, func() { g.Cleanup() })

	defer func() {
		if err != nil {
			g.Cleanup()
		}
	}()

	codegen.Reserved[g.Target] = true

	os.RemoveAll(g.OutDir)

	if err := os.MkdirAll(g.OutDir, 0755); err != nil {
		return nil, err
	}
	g.genfiles = []string{g.OutDir}
	if err := g.generateContexts(); err != nil {
		return nil, err
	}
	if err := g.generateControllers(); err != nil {
		return nil, err
	}
	if err := g.generateSecurity(); err != nil {
		return nil, err
	}
	if err := g.generateHrefs(); err != nil {
		return nil, err
	}
	if err := g.generateMediaTypes(); err != nil {
		return nil, err
	}
	if err := g.generateUserTypes(); err != nil {
		return nil, err
	}
	if !g.NoTest {
		if err := g.generateResourceTest(); err != nil {
			return nil, err
		}
	}

	return g.genfiles, nil
}

// Cleanup removes the entire "app" directory if it was created by this generator.
func (g *Generator) Cleanup() {
	if len(g.genfiles) == 0 {
		return
	}
	os.RemoveAll(g.OutDir)
	g.genfiles = nil
}

// generateContexts iterates through the API resources and actions and generates the action
// contexts.
func (g *Generator) generateContexts() error {
	ctxFile := filepath.Join(g.OutDir, "contexts.go")
	ctxWr, err := NewContextsWriter(ctxFile)
	if err != nil {
		panic(err) // bug
	}
	title := fmt.Sprintf("%s: Application Contexts", g.API.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("fmt"),
		codegen.SimpleImport("golang.org/x/net/context"),
		codegen.SimpleImport("strconv"),
		codegen.SimpleImport("strings"),
		codegen.SimpleImport("time"),
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.NewImport("uuid", "github.com/satori/go.uuid"),
	}
	g.genfiles = append(g.genfiles, ctxFile)
	ctxWr.WriteHeader(title, g.Target, imports)
	err = g.API.IterateResources(func(r *design.ResourceDefinition) error {
		return r.IterateActions(func(a *design.ActionDefinition) error {
			ctxName := codegen.Goify(a.Name, true) + codegen.Goify(a.Parent.Name, true) + "Context"
			headers := r.Headers.Merge(a.Headers)
			if headers != nil && len(headers.Type.ToObject()) == 0 {
				headers = nil // So that {{if .Headers}} returns false in templates
			}
			params := a.AllParams()
			if params != nil && len(params.Type.ToObject()) == 0 {
				params = nil // So that {{if .Params}} returns false in templates
			}

			non101 := make(map[string]*design.ResponseDefinition)
			for k, v := range a.Responses {
				if v.Status != 101 {
					non101[k] = v
				}
			}
			ctxData := ContextTemplateData{
				Name:         ctxName,
				ResourceName: r.Name,
				ActionName:   a.Name,
				Payload:      a.Payload,
				Params:       params,
				Headers:      headers,
				Routes:       a.Routes,
				Responses:    non101,
				API:          g.API,
				DefaultPkg:   g.Target,
				Security:     a.Security,
			}
			return ctxWr.Execute(&ctxData)
		})
	})
	if err != nil {
		return err
	}
	return ctxWr.FormatCode()
}

// generateControllers iterates through the API resources and generates the low level
// controllers.
func (g *Generator) generateControllers() error {
	ctlFile := filepath.Join(g.OutDir, "controllers.go")
	ctlWr, err := NewControllersWriter(ctlFile)
	if err != nil {
		panic(err) // bug
	}
	title := fmt.Sprintf("%s: Application Controllers", g.API.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("net/http"),
		codegen.SimpleImport("fmt"),
		codegen.SimpleImport("golang.org/x/net/context"),
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.SimpleImport("github.com/goadesign/goa/cors"),
		codegen.SimpleImport("regexp"),
	}
	encoders, err := BuildEncoders(g.API.Produces, true)
	if err != nil {
		return err
	}
	decoders, err := BuildEncoders(g.API.Consumes, false)
	if err != nil {
		return err
	}
	encoderImports := make(map[string]bool)
	for _, data := range encoders {
		encoderImports[data.PackagePath] = true
	}
	for _, data := range decoders {
		encoderImports[data.PackagePath] = true
	}
	var packagePaths []string
	for packagePath := range encoderImports {
		if packagePath != "github.com/goadesign/goa" {
			packagePaths = append(packagePaths, packagePath)
		}
	}
	sort.Strings(packagePaths)
	for _, packagePath := range packagePaths {
		imports = append(imports, codegen.SimpleImport(packagePath))
	}
	ctlWr.WriteHeader(title, g.Target, imports)
	ctlWr.WriteInitService(encoders, decoders)

	var controllersData []*ControllerTemplateData
	err = g.API.IterateResources(func(r *design.ResourceDefinition) error {
		// Create file servers for all directory file servers that serve index.html.
		fileServers := r.FileServers
		for _, fs := range r.FileServers {
			if fs.IsDir() {
				rpath := design.WildcardRegex.ReplaceAllLiteralString(fs.RequestPath, "")
				rpath += "/"
				fileServers = append(fileServers, &design.FileServerDefinition{
					Parent:      fs.Parent,
					Description: fs.Description,
					Docs:        fs.Docs,
					FilePath:    filepath.Join(fs.FilePath, "index.html"),
					RequestPath: rpath,
					Metadata:    fs.Metadata,
					Security:    fs.Security,
				})
			}
		}
		data := &ControllerTemplateData{
			API:            g.API,
			Resource:       codegen.Goify(r.Name, true),
			PreflightPaths: r.PreflightPaths(),
			FileServers:    fileServers,
		}
		ierr := r.IterateActions(func(a *design.ActionDefinition) error {
			context := fmt.Sprintf("%s%sContext", codegen.Goify(a.Name, true), codegen.Goify(r.Name, true))
			unmarshal := fmt.Sprintf("unmarshal%s%sPayload", codegen.Goify(a.Name, true), codegen.Goify(r.Name, true))
			action := map[string]interface{}{
				"Name":            codegen.Goify(a.Name, true),
				"Routes":          a.Routes,
				"Context":         context,
				"Unmarshal":       unmarshal,
				"Payload":         a.Payload,
				"PayloadOptional": a.PayloadOptional,
				"Security":        a.Security,
			}
			data.Actions = append(data.Actions, action)
			return nil
		})
		if ierr != nil {
			return ierr
		}
		if len(data.Actions) > 0 || len(data.FileServers) > 0 {
			data.Encoders = encoders
			data.Decoders = decoders
			data.Origins = r.AllOrigins()
			controllersData = append(controllersData, data)
		}
		return nil
	})
	if err != nil {
		return err
	}
	g.genfiles = append(g.genfiles, ctlFile)
	if err = ctlWr.Execute(controllersData); err != nil {
		return err
	}
	return ctlWr.FormatCode()
}

// generateControllers iterates through the API resources and generates the low level
// controllers.
func (g *Generator) generateSecurity() error {
	if len(g.API.SecuritySchemes) == 0 {
		return nil
	}

	secFile := filepath.Join(g.OutDir, "security.go")
	secWr, err := NewSecurityWriter(secFile)
	if err != nil {
		panic(err) // bug
	}

	title := fmt.Sprintf("%s: Application Security", g.API.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("net/http"),
		codegen.SimpleImport("errors"),
		codegen.SimpleImport("golang.org/x/net/context"),
		codegen.SimpleImport("github.com/goadesign/goa"),
	}
	secWr.WriteHeader(title, g.Target, imports)

	g.genfiles = append(g.genfiles, secFile)

	if err = secWr.Execute(design.Design.SecuritySchemes); err != nil {
		return err
	}

	return secWr.FormatCode()
}

// generateHrefs iterates through the API resources and generates the href factory methods.
func (g *Generator) generateHrefs() error {
	hrefFile := filepath.Join(g.OutDir, "hrefs.go")
	resWr, err := NewResourcesWriter(hrefFile)
	if err != nil {
		panic(err) // bug
	}
	title := fmt.Sprintf("%s: Application Resource Href Factories", g.API.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("fmt"),
	}
	resWr.WriteHeader(title, g.Target, imports)
	err = g.API.IterateResources(func(r *design.ResourceDefinition) error {
		m := g.API.MediaTypeWithIdentifier(r.MediaType)
		var identifier string
		if m != nil {
			identifier = m.Identifier
		} else {
			identifier = "text/plain"
		}
		data := ResourceData{
			Name:              codegen.Goify(r.Name, true),
			Identifier:        identifier,
			Description:       r.Description,
			Type:              m,
			CanonicalTemplate: codegen.CanonicalTemplate(r),
			CanonicalParams:   codegen.CanonicalParams(r),
		}
		return resWr.Execute(&data)
	})
	g.genfiles = append(g.genfiles, hrefFile)
	if err != nil {
		return err
	}
	return resWr.FormatCode()
}

// generateMediaTypes iterates through the media types and generate the data structures and
// marshaling code.
func (g *Generator) generateMediaTypes() error {
	mtFile := filepath.Join(g.OutDir, "media_types.go")
	mtWr, err := NewMediaTypesWriter(mtFile)
	if err != nil {
		panic(err) // bug
	}
	title := fmt.Sprintf("%s: Application Media Types", g.API.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.SimpleImport("fmt"),
		codegen.SimpleImport("time"),
		codegen.NewImport("uuid", "github.com/satori/go.uuid"),
	}
	mtWr.WriteHeader(title, g.Target, imports)
	err = g.API.IterateMediaTypes(func(mt *design.MediaTypeDefinition) error {
		if mt.IsError() {
			return nil
		}
		if mt.Type.IsObject() || mt.Type.IsArray() {
			return mtWr.Execute(mt)
		}
		return nil
	})
	g.genfiles = append(g.genfiles, mtFile)
	if err != nil {
		return err
	}
	return mtWr.FormatCode()
}

// generateUserTypes iterates through the user types and generates the data structures and
// marshaling code.
func (g *Generator) generateUserTypes() error {
	utFile := filepath.Join(g.OutDir, "user_types.go")
	utWr, err := NewUserTypesWriter(utFile)
	if err != nil {
		panic(err) // bug
	}
	title := fmt.Sprintf("%s: Application User Types", g.API.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("fmt"),
		codegen.SimpleImport("time"),
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.NewImport("uuid", "github.com/satori/go.uuid"),
	}
	utWr.WriteHeader(title, g.Target, imports)
	err = g.API.IterateUserTypes(func(t *design.UserTypeDefinition) error {
		return utWr.Execute(t)
	})
	g.genfiles = append(g.genfiles, utFile)
	if err != nil {
		return err
	}
	return utWr.FormatCode()
}
<|endoftext|>


<commit_before>package config

//FirebaseConfig is a sub-struct within ConfigMode that holds values specific
//to firebase.
type FirebaseConfig struct {
	ApiKey            string `json:"apiKey"`
	AuthDomain        string `json:"authDomain"`
	DatabaseURL       string `json:"databaseURL"`
	ProjectID         string `json:"projectId"`
	StorageBucket     string `json:"storageBucket"`
	MessagingSenderID string `json:"messagingSenderId"`
}

func (f *FirebaseConfig) copy() *FirebaseConfig {

	if f == nil {
		return nil
	}

	result := &FirebaseConfig{}
	(*result) = *f
	return result
}

func (f *FirebaseConfig) extend(other *FirebaseConfig) *FirebaseConfig {
	if f == nil {
		return nil
	}
	result := f.copy()

	if other == nil {
		return result
	}

	if other.ApiKey != "" {
		result.ApiKey = other.ApiKey
	}

	if other.AuthDomain != "" {
		result.AuthDomain = other.AuthDomain
	}

	if other.DatabaseURL != "" {
		result.DatabaseURL = other.DatabaseURL
	}

	if other.ProjectID != "" {
		result.ProjectID = other.ProjectID
	}

	if other.StorageBucket != "" {
		result.StorageBucket = other.StorageBucket
	}

	if other.MessagingSenderID != "" {
		result.MessagingSenderID = other.MessagingSenderID
	}

	return result
}
<commit_msg>Define FirebaseKey, FirebaseKeys, FirebaseKeyFromString. Part of #655.<commit_after>package config

import (
	"strings"
)

//FirebaseKey denotes a specific key in a firebase object
type FirebaseKey string

const (
	FirebaseInvalid           FirebaseKey = "<INVALID>"
	FirebaseApiKey                        = "apiKey"
	FirebaseAuthDomain                    = "authDomain"
	FirebaseDatabaseURL                   = "databaseURL"
	FirebaseProjectID                     = "projectId"
	FirebaseStorageBucket                 = "storageBucket"
	FirebaseMessagingSenderID             = "messagingSenderId"
)

//FirebaseKeys enumerates all firebase keys
var FirebaseKeys = map[FirebaseKey]bool{
	FirebaseApiKey:            true,
	FirebaseAuthDomain:        true,
	FirebaseDatabaseURL:       true,
	FirebaseProjectID:         true,
	FirebaseStorageBucket:     true,
	FirebaseMessagingSenderID: true,
}

//FirebaseConfig is a sub-struct within ConfigMode that holds values specific
//to firebase.
type FirebaseConfig struct {
	ApiKey            string `json:"apiKey"`
	AuthDomain        string `json:"authDomain"`
	DatabaseURL       string `json:"databaseURL"`
	ProjectID         string `json:"projectId"`
	StorageBucket     string `json:"storageBucket"`
	MessagingSenderID string `json:"messagingSenderId"`
}

func (f *FirebaseConfig) copy() *FirebaseConfig {

	if f == nil {
		return nil
	}

	result := &FirebaseConfig{}
	(*result) = *f
	return result
}

func (f *FirebaseConfig) extend(other *FirebaseConfig) *FirebaseConfig {
	if f == nil {
		return nil
	}
	result := f.copy()

	if other == nil {
		return result
	}

	if other.ApiKey != "" {
		result.ApiKey = other.ApiKey
	}

	if other.AuthDomain != "" {
		result.AuthDomain = other.AuthDomain
	}

	if other.DatabaseURL != "" {
		result.DatabaseURL = other.DatabaseURL
	}

	if other.ProjectID != "" {
		result.ProjectID = other.ProjectID
	}

	if other.StorageBucket != "" {
		result.StorageBucket = other.StorageBucket
	}

	if other.MessagingSenderID != "" {
		result.MessagingSenderID = other.MessagingSenderID
	}

	return result
}

//FirebaseKeyFromString returns the FirebseKey denoted by key (fuzzy
//matching).
func FirebaseKeyFromString(key string) FirebaseKey {

	key = strings.ToLower(key)
	key = strings.TrimSpace(key)

	for name, _ := range FirebaseKeys {
		normalizedName := strings.ToLower(string(name))

		if normalizedName == key {
			return name
		}
	}

	return FirebaseInvalid

}
<|endoftext|>