<commit_before>//  Copyright 2018 Istio 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 pilot

import (
	"context"
	"fmt"
	"net"

	xdsapi "github.com/envoyproxy/go-control-plane/envoy/api/v2"
	adsapi "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2"
	"google.golang.org/grpc"

	"github.com/hashicorp/go-multierror"

	"istio.io/istio/pilot/pkg/bootstrap"
	"istio.io/istio/pilot/pkg/config/memory"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/proxy/envoy"
	"istio.io/istio/pilot/pkg/serviceregistry"
	"istio.io/istio/pkg/test/framework/dependency"
	"istio.io/istio/pkg/test/framework/environment"
	"istio.io/istio/pkg/test/framework/environments/kubernetes"
	"istio.io/istio/pkg/test/framework/environments/local"
	"istio.io/istio/pkg/test/kube"
)

const (
	pilotService = "istio-pilot"
	grpcPortName = "grpc-xds"
)

var (
	// LocalComponent is a component for the local environment.
	LocalComponent = &localComponent{}

	// KubeComponent is a component for the Kubernetes environment.
	KubeComponent = &kubeComponent{}
)

type localComponent struct{}

// ID implements the component.Component interface.
func (c *localComponent) ID() dependency.Instance {
	return dependency.Pilot
}

// Requires implements the component.Component interface.
func (c *localComponent) Requires() []dependency.Instance {
	return make([]dependency.Instance, 0)
}

// Init implements the component.Component interface.
func (c *localComponent) Init(ctx environment.ComponentContext, deps map[dependency.Instance]interface{}) (interface{}, error) {
	e, ok := ctx.Environment().(*local.Implementation)
	if !ok {
		return nil, fmt.Errorf("unsupported environment: %q", ctx.Environment().EnvironmentID())
	}

	return NewLocalPilot(e.IstioSystemNamespace)
}

type kubeComponent struct {
}

// ID implements the component.Component interface.
func (c *kubeComponent) ID() dependency.Instance {
	return dependency.Pilot
}

// Requires implements the component.Component interface.
func (c *kubeComponent) Requires() []dependency.Instance {
	return make([]dependency.Instance, 0)
}

// Init implements the component.Component interface.
func (c *kubeComponent) Init(ctx environment.ComponentContext, deps map[dependency.Instance]interface{}) (interface{}, error) {
	e, ok := ctx.Environment().(*kubernetes.Implementation)
	if !ok {
		return nil, fmt.Errorf("unsupported environment: %q", ctx.Environment().EnvironmentID())
	}

	result, err := c.doInit(e)
	if err != nil {
		return nil, multierror.Prefix(err, "pilot init failed:")
	}
	return result, nil
}

func (c *kubeComponent) doInit(e *kubernetes.Implementation) (interface{}, error) {
	s := e.KubeSettings()

	pod, err := e.Accessor.WaitForPodBySelectors(s.IstioSystemNamespace, "istio=pilot")
	if err != nil {
		return nil, err
	}

	port, err := getGrpcPort(e)
	if err != nil {
		return nil, err
	}

	return NewKubePilot(s.KubeConfig, pod.Namespace, pod.Name, port)
}

func getGrpcPort(e *kubernetes.Implementation) (uint16, error) {
	s := e.KubeSettings()
	svc, err := e.Accessor.GetService(s.IstioSystemNamespace, pilotService)
	if err != nil {
		return 0, fmt.Errorf("failed to retrieve service %s: %v", pilotService, err)
	}
	for _, portInfo := range svc.Spec.Ports {
		if portInfo.Name == grpcPortName {
			return uint16(portInfo.TargetPort.IntValue()), nil
		}
	}
	return 0, fmt.Errorf("failed to get target port in service %s", pilotService)
}

// LocalPilot is the interface for a local pilot server.
type LocalPilot interface {
	environment.DeployedPilot
	model.ConfigStore
	GetDiscoveryAddress() *net.TCPAddr
}

type localPilot struct {
	*pilotClient
	model.ConfigStoreCache
	server   *bootstrap.Server
	stopChan chan struct{}
}

type kubePilot struct {
	*pilotClient
	forwarder kube.PortForwarder
}

// NewLocalPilot creates a new pilot for the local environment.
func NewLocalPilot(namespace string) (LocalPilot, error) {
	// Use an in-memory config store.
	configController := memory.NewController(memory.Make(model.IstioConfigTypes))

	mesh := model.DefaultMeshConfig()
	options := envoy.DiscoveryServiceOptions{
		HTTPAddr:       ":0",
		MonitoringAddr: ":0",
		GrpcAddr:       ":0",
		SecureGrpcAddr: ":0",
	}
	bootstrapArgs := bootstrap.PilotArgs{
		Namespace:        namespace,
		DiscoveryOptions: options,
		MeshConfig:       &mesh,
		Config: bootstrap.ConfigArgs{
			Controller: configController,
		},
		// Use the config store for service entries as well.
		Service: bootstrap.ServiceArgs{
			Registries: []string{
				string(serviceregistry.ConfigRegistry),
			},
		},
	}

	// Create the server for the discovery service.
	server, err := bootstrap.NewServer(bootstrapArgs)
	if err != nil {
		return nil, err
	}

	client, err := newPilotClient(server.GRPCListeningAddr.(*net.TCPAddr))
	if err != nil {
		return nil, err
	}

	// Start the server
	stopChan := make(chan struct{})
	if err := server.Start(stopChan); err != nil {
		return nil, err
	}

	return &localPilot{
		ConfigStoreCache: configController,
		pilotClient:      client,
		server:           server,
		stopChan:         stopChan,
	}, nil
}

// NewKubePilot creates a new pilot instance for the kubernetes environment
func NewKubePilot(kubeConfig, namespace, pod string, port uint16) (environment.DeployedPilot, error) {
	// Start port-forwarding for pilot.
	options := &kube.PodSelectOptions{
		PodNamespace: namespace,
		PodName:      pod,
	}
	forwarder, err := kube.NewPortForwarder(kubeConfig, options, 0, port)
	if err != nil {
		return nil, err
	}
	if err := forwarder.Start(); err != nil {
		return nil, err
	}

	addr, err := net.ResolveTCPAddr("tcp", forwarder.Address())
	if err != nil {
		return nil, err
	}
	client, err := newPilotClient(addr)
	if err != nil {
		return nil, err
	}

	return &kubePilot{
		pilotClient: client,
		forwarder:   forwarder,
	}, nil
}

// GetDiscoveryAddress gets the discovery address for pilot.
func (p *localPilot) GetDiscoveryAddress() *net.TCPAddr {
	return p.server.GRPCListeningAddr.(*net.TCPAddr)
}

// Close stops the local pilot server.
func (p *localPilot) Close() (err error) {
	if p.pilotClient != nil {
		err = multierror.Append(err, p.pilotClient.Close()).ErrorOrNil()
	}

	if p.stopChan != nil {
		p.stopChan <- struct{}{}
	}
	return
}

// Close stops the kube pilot server.
func (p *kubePilot) Close() (err error) {
	if p.pilotClient != nil {
		err = multierror.Append(err, p.pilotClient.Close()).ErrorOrNil()
	}

	if p.forwarder != nil {
		p.forwarder.Close()
	}
	return
}

type pilotClient struct {
	discoveryAddr *net.TCPAddr
	conn          *grpc.ClientConn
	stream        adsapi.AggregatedDiscoveryService_StreamAggregatedResourcesClient
}

func newPilotClient(discoveryAddr *net.TCPAddr) (*pilotClient, error) {
	conn, err := grpc.Dial(discoveryAddr.String(), grpc.WithInsecure())
	if err != nil {
		return nil, err
	}

	adsClient := adsapi.NewAggregatedDiscoveryServiceClient(conn)
	stream, err := adsClient.StreamAggregatedResources(context.Background())
	if err != nil {
		return nil, err
	}

	return &pilotClient{
		conn:          conn,
		stream:        stream,
		discoveryAddr: discoveryAddr,
	}, nil
}

func (c *pilotClient) CallDiscovery(req *xdsapi.DiscoveryRequest) (*xdsapi.DiscoveryResponse, error) {
	err := c.stream.Send(req)
	if err != nil {
		return nil, err
	}
	return c.stream.Recv()
}

func (c *pilotClient) Close() (err error) {
	if c.stream != nil {
		err = multierror.Append(err, c.stream.CloseSend()).ErrorOrNil()
	}
	if c.conn != nil {
		err = multierror.Append(err, c.conn.Close()).ErrorOrNil()
	}
	return
}
<commit_msg>[test-framework] Make Pilot depend on Mixer (#8774)<commit_after>//  Copyright 2018 Istio 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 pilot

import (
	"context"
	"fmt"
	"net"

	xdsapi "github.com/envoyproxy/go-control-plane/envoy/api/v2"
	adsapi "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2"
	"google.golang.org/grpc"

	"github.com/hashicorp/go-multierror"

	"istio.io/istio/pilot/pkg/bootstrap"
	"istio.io/istio/pilot/pkg/config/memory"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/proxy/envoy"
	"istio.io/istio/pilot/pkg/serviceregistry"
	"istio.io/istio/pkg/test/framework/dependency"
	"istio.io/istio/pkg/test/framework/environment"
	"istio.io/istio/pkg/test/framework/environments/kubernetes"
	"istio.io/istio/pkg/test/framework/environments/local"
	"istio.io/istio/pkg/test/kube"
)

const (
	pilotService = "istio-pilot"
	grpcPortName = "grpc-xds"
)

var (
	// LocalComponent is a component for the local environment.
	LocalComponent = &localComponent{}

	// KubeComponent is a component for the Kubernetes environment.
	KubeComponent = &kubeComponent{}

	requiredDeps = []dependency.Instance{
		dependency.Mixer,
	}
)

type localComponent struct{}

// ID implements the component.Component interface.
func (c *localComponent) ID() dependency.Instance {
	return dependency.Pilot
}

// Requires implements the component.Component interface.
func (c *localComponent) Requires() []dependency.Instance {
	return requiredDeps
}

// Init implements the component.Component interface.
func (c *localComponent) Init(ctx environment.ComponentContext, deps map[dependency.Instance]interface{}) (interface{}, error) {
	e, ok := ctx.Environment().(*local.Implementation)
	if !ok {
		return nil, fmt.Errorf("unsupported environment: %q", ctx.Environment().EnvironmentID())
	}

	return NewLocalPilot(e.IstioSystemNamespace)
}

type kubeComponent struct {
}

// ID implements the component.Component interface.
func (c *kubeComponent) ID() dependency.Instance {
	return dependency.Pilot
}

// Requires implements the component.Component interface.
func (c *kubeComponent) Requires() []dependency.Instance {
	return requiredDeps
}

// Init implements the component.Component interface.
func (c *kubeComponent) Init(ctx environment.ComponentContext, deps map[dependency.Instance]interface{}) (interface{}, error) {
	e, ok := ctx.Environment().(*kubernetes.Implementation)
	if !ok {
		return nil, fmt.Errorf("unsupported environment: %q", ctx.Environment().EnvironmentID())
	}

	result, err := c.doInit(e)
	if err != nil {
		return nil, multierror.Prefix(err, "pilot init failed:")
	}
	return result, nil
}

func (c *kubeComponent) doInit(e *kubernetes.Implementation) (interface{}, error) {
	s := e.KubeSettings()

	pod, err := e.Accessor.WaitForPodBySelectors(s.IstioSystemNamespace, "istio=pilot")
	if err != nil {
		return nil, err
	}

	port, err := getGrpcPort(e)
	if err != nil {
		return nil, err
	}

	return NewKubePilot(s.KubeConfig, pod.Namespace, pod.Name, port)
}

func getGrpcPort(e *kubernetes.Implementation) (uint16, error) {
	s := e.KubeSettings()
	svc, err := e.Accessor.GetService(s.IstioSystemNamespace, pilotService)
	if err != nil {
		return 0, fmt.Errorf("failed to retrieve service %s: %v", pilotService, err)
	}
	for _, portInfo := range svc.Spec.Ports {
		if portInfo.Name == grpcPortName {
			return uint16(portInfo.TargetPort.IntValue()), nil
		}
	}
	return 0, fmt.Errorf("failed to get target port in service %s", pilotService)
}

// LocalPilot is the interface for a local pilot server.
type LocalPilot interface {
	environment.DeployedPilot
	model.ConfigStore
	GetDiscoveryAddress() *net.TCPAddr
}

type localPilot struct {
	*pilotClient
	model.ConfigStoreCache
	server   *bootstrap.Server
	stopChan chan struct{}
}

type kubePilot struct {
	*pilotClient
	forwarder kube.PortForwarder
}

// NewLocalPilot creates a new pilot for the local environment.
func NewLocalPilot(namespace string) (LocalPilot, error) {
	// Use an in-memory config store.
	configController := memory.NewController(memory.Make(model.IstioConfigTypes))

	mesh := model.DefaultMeshConfig()
	options := envoy.DiscoveryServiceOptions{
		HTTPAddr:       ":0",
		MonitoringAddr: ":0",
		GrpcAddr:       ":0",
		SecureGrpcAddr: ":0",
	}
	bootstrapArgs := bootstrap.PilotArgs{
		Namespace:        namespace,
		DiscoveryOptions: options,
		MeshConfig:       &mesh,
		Config: bootstrap.ConfigArgs{
			Controller: configController,
		},
		// Use the config store for service entries as well.
		Service: bootstrap.ServiceArgs{
			Registries: []string{
				string(serviceregistry.ConfigRegistry),
			},
		},
	}

	// Create the server for the discovery service.
	server, err := bootstrap.NewServer(bootstrapArgs)
	if err != nil {
		return nil, err
	}

	client, err := newPilotClient(server.GRPCListeningAddr.(*net.TCPAddr))
	if err != nil {
		return nil, err
	}

	// Start the server
	stopChan := make(chan struct{})
	if err := server.Start(stopChan); err != nil {
		return nil, err
	}

	return &localPilot{
		ConfigStoreCache: configController,
		pilotClient:      client,
		server:           server,
		stopChan:         stopChan,
	}, nil
}

// NewKubePilot creates a new pilot instance for the kubernetes environment
func NewKubePilot(kubeConfig, namespace, pod string, port uint16) (environment.DeployedPilot, error) {
	// Start port-forwarding for pilot.
	options := &kube.PodSelectOptions{
		PodNamespace: namespace,
		PodName:      pod,
	}
	forwarder, err := kube.NewPortForwarder(kubeConfig, options, 0, port)
	if err != nil {
		return nil, err
	}
	if err := forwarder.Start(); err != nil {
		return nil, err
	}

	addr, err := net.ResolveTCPAddr("tcp", forwarder.Address())
	if err != nil {
		return nil, err
	}
	client, err := newPilotClient(addr)
	if err != nil {
		return nil, err
	}

	return &kubePilot{
		pilotClient: client,
		forwarder:   forwarder,
	}, nil
}

// GetDiscoveryAddress gets the discovery address for pilot.
func (p *localPilot) GetDiscoveryAddress() *net.TCPAddr {
	return p.server.GRPCListeningAddr.(*net.TCPAddr)
}

// Close stops the local pilot server.
func (p *localPilot) Close() (err error) {
	if p.pilotClient != nil {
		err = multierror.Append(err, p.pilotClient.Close()).ErrorOrNil()
	}

	if p.stopChan != nil {
		p.stopChan <- struct{}{}
	}
	return
}

// Close stops the kube pilot server.
func (p *kubePilot) Close() (err error) {
	if p.pilotClient != nil {
		err = multierror.Append(err, p.pilotClient.Close()).ErrorOrNil()
	}

	if p.forwarder != nil {
		p.forwarder.Close()
	}
	return
}

type pilotClient struct {
	discoveryAddr *net.TCPAddr
	conn          *grpc.ClientConn
	stream        adsapi.AggregatedDiscoveryService_StreamAggregatedResourcesClient
}

func newPilotClient(discoveryAddr *net.TCPAddr) (*pilotClient, error) {
	conn, err := grpc.Dial(discoveryAddr.String(), grpc.WithInsecure())
	if err != nil {
		return nil, err
	}

	adsClient := adsapi.NewAggregatedDiscoveryServiceClient(conn)
	stream, err := adsClient.StreamAggregatedResources(context.Background())
	if err != nil {
		return nil, err
	}

	return &pilotClient{
		conn:          conn,
		stream:        stream,
		discoveryAddr: discoveryAddr,
	}, nil
}

func (c *pilotClient) CallDiscovery(req *xdsapi.DiscoveryRequest) (*xdsapi.DiscoveryResponse, error) {
	err := c.stream.Send(req)
	if err != nil {
		return nil, err
	}
	return c.stream.Recv()
}

func (c *pilotClient) Close() (err error) {
	if c.stream != nil {
		err = multierror.Append(err, c.stream.CloseSend()).ErrorOrNil()
	}
	if c.conn != nil {
		err = multierror.Append(err, c.conn.Close()).ErrorOrNil()
	}
	return
}
<|endoftext|>


<commit_before><commit_msg>Remove checkExternal debug log.Println<commit_after><|endoftext|>


<commit_before>package persistent_volumes

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path"
	"time"

	"github.com/golang/glog"
	"github.com/openshift/origin/pkg/oc/admin/policy"
	kerrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apiserver/pkg/authentication/serviceaccount"
	rbacv1client "k8s.io/client-go/kubernetes/typed/rbac/v1"
	"k8s.io/client-go/util/retry"
	kbatch "k8s.io/kubernetes/pkg/apis/batch"
	kapi "k8s.io/kubernetes/pkg/apis/core"
	kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"

	"github.com/openshift/origin/pkg/oc/clusteradd/componentinstall"
	"github.com/openshift/origin/pkg/oc/clusterup/docker/dockerhelper"
	securityclient "github.com/openshift/origin/pkg/security/generated/internalclientset"
	securitytypedclient "github.com/openshift/origin/pkg/security/generated/internalclientset/typed/security/internalversion"
)

const (
	pvCount            = 100
	pvSetupJobName     = "persistent-volume-setup"
	pvInstallerSA      = "pvinstaller"
	pvTargetNamespace  = "default"
	pvIgnoreMarkerFile = ".skip_pv"
)

// TODO: This really should be a Go template
const createPVScript = `#/bin/bash -xe
function generate_pv() {
  local basedir="${1}"
  local name="${2}"
cat <<EOF
apiVersion: v1
kind: PersistentVolume
metadata:
  name: ${name}
  labels:
    volume: ${name}
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteOnce
    - ReadWriteMany
    - ReadOnlyMany
  hostPath:
    path: ${basedir}/${name}
  persistentVolumeReclaimPolicy: Recycle
EOF
}

function setup_pv_dir() {
  local dir="${1}"
  if [[ ! -d "${dir}" ]]; then
    mkdir -p "${dir}"
  fi
  if ! chcon -t svirt_sandbox_file_t "${dir}" &> /dev/null; then
    echo "Not setting SELinux content for ${dir}"
  fi
  chmod 770 "${dir}"
}

function create_pv() {
  local basedir="${1}"
  local name="${2}"

  setup_pv_dir "${basedir}/${name}"
  if ! oc get pv "${name}" &> /dev/null; then
    generate_pv "${basedir}" "${name}" | oc create -f -
  else
    echo "persistentvolume ${name} already exists"
  fi
}

basedir="%[2]s"
setup_pv_dir "${basedir}/registry"

for i in $(seq -f "%%04g" 1 %[1]d); do
  create_pv "${basedir}" "pv${i}"
done
`

type SetupPersistentVolumesOptions struct {
	InstallContext componentinstall.Context
}

func (c *SetupPersistentVolumesOptions) Name() string {
	return "persistent-volumes"
}

func (c *SetupPersistentVolumesOptions) Install(dockerClient dockerhelper.Interface) error {
	kclient, err := kubernetes.NewForConfig(c.InstallContext.ClusterAdminClientConfig())
	if err != nil {
		return err
	}
	securityClient, err := securityclient.NewForConfig(c.InstallContext.ClusterAdminClientConfig())
	if err != nil {
		return err
	}
	rbacClient, err := rbacv1client.NewForConfig(c.InstallContext.ClusterAdminClientConfig())
	if err != nil {
		return err
	}
	if err := ensurePVInstallerSA(rbacClient, kclient, securityClient); err != nil {
		return err
	}

	// TODO: Make the job idempotent and non-failing
	_, err = kclient.Batch().Jobs(pvTargetNamespace).Get(pvSetupJobName, metav1.GetOptions{})
	if err == nil {
		return nil
	}
	if !kerrors.IsNotFound(err) {
		return fmt.Errorf("error retrieving job to setup persistent volumes (%s/%s): %v", pvTargetNamespace, pvSetupJobName, err)
	}
	targetDir := path.Join(c.InstallContext.BaseDir(), "openshift.local.pv")

	if _, err := os.Stat(path.Join(targetDir, pvIgnoreMarkerFile)); !os.IsNotExist(err) {
		glog.Infof("Found %q marker file, skipping persistent volume setup", path.Join(targetDir, pvIgnoreMarkerFile))
		return nil
	}

	setupJob := persistentStorageSetupJob(pvSetupJobName, targetDir, c.InstallContext.ClientImage(), pvCount)
	if _, err = kclient.Batch().Jobs(pvTargetNamespace).Create(setupJob); err != nil {
		return fmt.Errorf("cannot create job to setup persistent volumes (%s/%s): %v", pvTargetNamespace, pvSetupJobName, err)
	}

	return nil
}

func ensurePVInstallerSA(rbacClient rbacv1client.RbacV1Interface, kclient kubernetes.Interface, securityClient securityclient.Interface) error {
	sa, err := kclient.Core().ServiceAccounts(pvTargetNamespace).Get(pvInstallerSA, metav1.GetOptions{})
	if err != nil {
		if !kerrors.IsNotFound(err) {
			return fmt.Errorf("error retrieving installer service account (%s/%s): %v", pvTargetNamespace, pvInstallerSA, err)
		}
		sa = &kapi.ServiceAccount{}
		sa.Name = pvInstallerSA
		if _, err := kclient.Core().ServiceAccounts(pvTargetNamespace).Create(sa); err != nil {
			return fmt.Errorf("cannot create %q service account: %v", sa.Name, err)
		}
	}

	err = wait.PollImmediate(time.Second, 5*time.Minute, func() (bool, error) {
		err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
			return addSCCToServiceAccount(securityClient.Security(), "privileged", pvInstallerSA, pvTargetNamespace, &bytes.Buffer{})
		})
		// TODO: We do need to figure out why this is sometimes giving a 404. on SCC get
		if kerrors.IsNotFound(err) {
			return false, nil
		}
		if err != nil {
			return false, fmt.Errorf("cannot add privileged SCC to %q SA: %v", sa.Name, err)
		}

		saUser := serviceaccount.MakeUsername(pvTargetNamespace, pvInstallerSA)
		err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
			return addClusterRole(rbacClient, "cluster-admin", saUser)
		})

		// TODO: we do need to figure out why this is sometimes giving a 404. on GET https://127.0.0.1:8443/apis/authorization.openshift.io/v1/clusterrolebindings
		if kerrors.IsNotFound(err) {
			return false, nil
		}
		if err != nil {
			return false, fmt.Errorf("cannot add cluster role to service account (%s/%s): %v", pvTargetNamespace, pvInstallerSA, err)
		}
		return true, nil
	})
	if err != nil {
		return err
	}

	return nil
}

func persistentStorageSetupJob(name, dir, image string, pvCount int) *kbatch.Job {
	// Job volume
	volume := kapi.Volume{}
	volume.Name = "pvdir"
	volume.HostPath = &kapi.HostPathVolumeSource{Path: dir}

	// Volume mount
	mount := kapi.VolumeMount{}
	mount.Name = "pvdir"
	mount.MountPath = dir

	// Job container
	container := kapi.Container{}
	container.Name = "setup-persistent-volumes"
	container.Image = image
	container.Command = []string{"/bin/bash", "-c", fmt.Sprintf(createPVScript, pvCount, dir)}
	privileged := true
	container.SecurityContext = &kapi.SecurityContext{
		Privileged: &privileged,
	}
	container.VolumeMounts = []kapi.VolumeMount{mount}

	// Job
	completions := int32(1)
	deadline := int64(60 * 20)
	job := &kbatch.Job{}
	job.Name = name
	job.Spec.Completions = &completions
	job.Spec.ActiveDeadlineSeconds = &deadline
	job.Spec.Template.Spec.Volumes = []kapi.Volume{volume}
	job.Spec.Template.Spec.RestartPolicy = kapi.RestartPolicyNever
	job.Spec.Template.Spec.ServiceAccountName = pvInstallerSA
	job.Spec.Template.Spec.Containers = []kapi.Container{container}
	return job
}

func addClusterRole(rbacClient rbacv1client.RbacV1Interface, role, user string) error {
	addClusterReaderRole := policy.RoleModificationOptions{
		RoleName:   role,
		RoleKind:   "ClusterRole",
		RbacClient: rbacClient,
		Users:      []string{user},
	}
	return addClusterReaderRole.AddRole()
}

func addSCCToServiceAccount(securityClient securitytypedclient.SecurityContextConstraintsGetter, scc, sa, namespace string, out io.Writer) error {
	modifySCC := policy.SCCModificationOptions{
		SCCName:      scc,
		SCCInterface: securityClient.SecurityContextConstraints(),
		Subjects: []kapi.ObjectReference{
			{
				Namespace: namespace,
				Name:      sa,
				Kind:      "ServiceAccount",
			},
		},

		Out: out,
	}
	return modifySCC.AddSCC()
}
<commit_msg>pv component should not touch registry dir<commit_after>package persistent_volumes

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path"
	"time"

	"github.com/golang/glog"
	"github.com/openshift/origin/pkg/oc/admin/policy"
	kerrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apiserver/pkg/authentication/serviceaccount"
	rbacv1client "k8s.io/client-go/kubernetes/typed/rbac/v1"
	"k8s.io/client-go/util/retry"
	kbatch "k8s.io/kubernetes/pkg/apis/batch"
	kapi "k8s.io/kubernetes/pkg/apis/core"
	kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"

	"github.com/openshift/origin/pkg/oc/clusteradd/componentinstall"
	"github.com/openshift/origin/pkg/oc/clusterup/docker/dockerhelper"
	securityclient "github.com/openshift/origin/pkg/security/generated/internalclientset"
	securitytypedclient "github.com/openshift/origin/pkg/security/generated/internalclientset/typed/security/internalversion"
)

const (
	pvCount            = 100
	pvSetupJobName     = "persistent-volume-setup"
	pvInstallerSA      = "pvinstaller"
	pvTargetNamespace  = "default"
	pvIgnoreMarkerFile = ".skip_pv"
)

// TODO: This really should be a Go template
const createPVScript = `#/bin/bash -xe
function generate_pv() {
  local basedir="${1}"
  local name="${2}"
cat <<EOF
apiVersion: v1
kind: PersistentVolume
metadata:
  name: ${name}
  labels:
    volume: ${name}
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteOnce
    - ReadWriteMany
    - ReadOnlyMany
  hostPath:
    path: ${basedir}/${name}
  persistentVolumeReclaimPolicy: Recycle
EOF
}

function setup_pv_dir() {
  local dir="${1}"
  if [[ ! -d "${dir}" ]]; then
    mkdir -p "${dir}"
  fi
  if ! chcon -t svirt_sandbox_file_t "${dir}" &> /dev/null; then
    echo "Not setting SELinux content for ${dir}"
  fi
  chmod 770 "${dir}"
}

function create_pv() {
  local basedir="${1}"
  local name="${2}"

  setup_pv_dir "${basedir}/${name}"
  if ! oc get pv "${name}" &> /dev/null; then
    generate_pv "${basedir}" "${name}" | oc create -f -
  else
    echo "persistentvolume ${name} already exists"
  fi
}

basedir="%[2]s"

for i in $(seq -f "%%04g" 1 %[1]d); do
  create_pv "${basedir}" "pv${i}"
done
`

type SetupPersistentVolumesOptions struct {
	InstallContext componentinstall.Context
}

func (c *SetupPersistentVolumesOptions) Name() string {
	return "persistent-volumes"
}

func (c *SetupPersistentVolumesOptions) Install(dockerClient dockerhelper.Interface) error {
	kclient, err := kubernetes.NewForConfig(c.InstallContext.ClusterAdminClientConfig())
	if err != nil {
		return err
	}
	securityClient, err := securityclient.NewForConfig(c.InstallContext.ClusterAdminClientConfig())
	if err != nil {
		return err
	}
	rbacClient, err := rbacv1client.NewForConfig(c.InstallContext.ClusterAdminClientConfig())
	if err != nil {
		return err
	}
	if err := ensurePVInstallerSA(rbacClient, kclient, securityClient); err != nil {
		return err
	}

	// TODO: Make the job idempotent and non-failing
	_, err = kclient.Batch().Jobs(pvTargetNamespace).Get(pvSetupJobName, metav1.GetOptions{})
	if err == nil {
		return nil
	}
	if !kerrors.IsNotFound(err) {
		return fmt.Errorf("error retrieving job to setup persistent volumes (%s/%s): %v", pvTargetNamespace, pvSetupJobName, err)
	}
	targetDir := path.Join(c.InstallContext.BaseDir(), "openshift.local.pv")

	if _, err := os.Stat(path.Join(targetDir, pvIgnoreMarkerFile)); !os.IsNotExist(err) {
		glog.Infof("Found %q marker file, skipping persistent volume setup", path.Join(targetDir, pvIgnoreMarkerFile))
		return nil
	}

	setupJob := persistentStorageSetupJob(pvSetupJobName, targetDir, c.InstallContext.ClientImage(), pvCount)
	if _, err = kclient.Batch().Jobs(pvTargetNamespace).Create(setupJob); err != nil {
		return fmt.Errorf("cannot create job to setup persistent volumes (%s/%s): %v", pvTargetNamespace, pvSetupJobName, err)
	}

	return nil
}

func ensurePVInstallerSA(rbacClient rbacv1client.RbacV1Interface, kclient kubernetes.Interface, securityClient securityclient.Interface) error {
	sa, err := kclient.Core().ServiceAccounts(pvTargetNamespace).Get(pvInstallerSA, metav1.GetOptions{})
	if err != nil {
		if !kerrors.IsNotFound(err) {
			return fmt.Errorf("error retrieving installer service account (%s/%s): %v", pvTargetNamespace, pvInstallerSA, err)
		}
		sa = &kapi.ServiceAccount{}
		sa.Name = pvInstallerSA
		if _, err := kclient.Core().ServiceAccounts(pvTargetNamespace).Create(sa); err != nil {
			return fmt.Errorf("cannot create %q service account: %v", sa.Name, err)
		}
	}

	err = wait.PollImmediate(time.Second, 5*time.Minute, func() (bool, error) {
		err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
			return addSCCToServiceAccount(securityClient.Security(), "privileged", pvInstallerSA, pvTargetNamespace, &bytes.Buffer{})
		})
		// TODO: We do need to figure out why this is sometimes giving a 404. on SCC get
		if kerrors.IsNotFound(err) {
			return false, nil
		}
		if err != nil {
			return false, fmt.Errorf("cannot add privileged SCC to %q SA: %v", sa.Name, err)
		}

		saUser := serviceaccount.MakeUsername(pvTargetNamespace, pvInstallerSA)
		err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
			return addClusterRole(rbacClient, "cluster-admin", saUser)
		})

		// TODO: we do need to figure out why this is sometimes giving a 404. on GET https://127.0.0.1:8443/apis/authorization.openshift.io/v1/clusterrolebindings
		if kerrors.IsNotFound(err) {
			return false, nil
		}
		if err != nil {
			return false, fmt.Errorf("cannot add cluster role to service account (%s/%s): %v", pvTargetNamespace, pvInstallerSA, err)
		}
		return true, nil
	})
	if err != nil {
		return err
	}

	return nil
}

func persistentStorageSetupJob(name, dir, image string, pvCount int) *kbatch.Job {
	// Job volume
	volume := kapi.Volume{}
	volume.Name = "pvdir"
	volume.HostPath = &kapi.HostPathVolumeSource{Path: dir}

	// Volume mount
	mount := kapi.VolumeMount{}
	mount.Name = "pvdir"
	mount.MountPath = dir

	// Job container
	container := kapi.Container{}
	container.Name = "setup-persistent-volumes"
	container.Image = image
	container.Command = []string{"/bin/bash", "-c", fmt.Sprintf(createPVScript, pvCount, dir)}
	privileged := true
	container.SecurityContext = &kapi.SecurityContext{
		Privileged: &privileged,
	}
	container.VolumeMounts = []kapi.VolumeMount{mount}

	// Job
	completions := int32(1)
	deadline := int64(60 * 20)
	job := &kbatch.Job{}
	job.Name = name
	job.Spec.Completions = &completions
	job.Spec.ActiveDeadlineSeconds = &deadline
	job.Spec.Template.Spec.Volumes = []kapi.Volume{volume}
	job.Spec.Template.Spec.RestartPolicy = kapi.RestartPolicyNever
	job.Spec.Template.Spec.ServiceAccountName = pvInstallerSA
	job.Spec.Template.Spec.Containers = []kapi.Container{container}
	return job
}

func addClusterRole(rbacClient rbacv1client.RbacV1Interface, role, user string) error {
	addClusterReaderRole := policy.RoleModificationOptions{
		RoleName:   role,
		RoleKind:   "ClusterRole",
		RbacClient: rbacClient,
		Users:      []string{user},
	}
	return addClusterReaderRole.AddRole()
}

func addSCCToServiceAccount(securityClient securitytypedclient.SecurityContextConstraintsGetter, scc, sa, namespace string, out io.Writer) error {
	modifySCC := policy.SCCModificationOptions{
		SCCName:      scc,
		SCCInterface: securityClient.SecurityContextConstraints(),
		Subjects: []kapi.ObjectReference{
			{
				Namespace: namespace,
				Name:      sa,
				Kind:      "ServiceAccount",
			},
		},

		Out: out,
	}
	return modifySCC.AddSCC()
}
<|endoftext|>


<commit_before>package suggest

import (
	"bytes"
	"go/scanner"
	"go/token"
)

type token_iterator struct {
	tokens      []token_item
	token_index int
}

type token_item struct {
	off int
	tok token.Token
	lit string
}

func (i token_item) literal() string {
	if i.tok.IsLiteral() {
		return i.lit
	}
	return i.tok.String()
}

func new_token_iterator(src []byte, cursor int) token_iterator {
	tokens := make([]token_item, 0, 1000)
	var s scanner.Scanner
	fset := token.NewFileSet()
	file := fset.AddFile("", fset.Base(), len(src))
	s.Init(file, src, nil, 0)
	for {
		pos, tok, lit := s.Scan()
		off := fset.Position(pos).Offset
		if tok == token.EOF || cursor <= off {
			break
		}
		tokens = append(tokens, token_item{
			off: off,
			tok: tok,
			lit: lit,
		})
	}
	return token_iterator{
		tokens:      tokens,
		token_index: len(tokens) - 1,
	}
}

func (this *token_iterator) token() token_item {
	return this.tokens[this.token_index]
}

func (this *token_iterator) go_back() bool {
	if this.token_index <= 0 {
		return false
	}
	this.token_index--
	return true
}

var bracket_pairs_map = map[token.Token]token.Token{
	token.RPAREN: token.LPAREN,
	token.RBRACK: token.LBRACK,
	token.RBRACE: token.LBRACE,
}

func (ti *token_iterator) skip_to_left(left, right token.Token) bool {
	if ti.token().tok == left {
		return true
	}
	balance := 1
	for balance != 0 {
		if !ti.go_back() {
			return false
		}
		switch ti.token().tok {
		case right:
			balance++
		case left:
			balance--
		}
	}
	return true
}

// when the cursor is at the ')' or ']' or '}', move the cursor to an opposite
// bracket pair, this functions takes nested bracket pairs into account
func (this *token_iterator) skip_to_balanced_pair() bool {
	right := this.token().tok
	left := bracket_pairs_map[right]
	return this.skip_to_left(left, right)
}

// Move the cursor to the open brace of the current block, taking nested blocks
// into account.
func (this *token_iterator) skip_to_left_curly() bool {
	return this.skip_to_left(token.LBRACE, token.RBRACE)
}

// Extract the type expression right before the enclosing curly bracket block.
// Examples (# - the cursor):
//   &lib.Struct{Whatever: 1, Hel#} // returns "lib.Struct"
//   X{#}                           // returns X
// The idea is that we check if this type expression is a type and it is, we
// can apply special filtering for autocompletion results.
// Sadly, this doesn't cover anonymous structs.
func (ti *token_iterator) extract_struct_type() (res string) {
	if !ti.skip_to_left_curly() {
		return ""
	}
	if !ti.go_back() {
		return ""
	}
	if ti.token().tok != token.IDENT {
		return ""
	}
	b := ti.token().literal()
	if !ti.go_back() {
		return b
	}
	if ti.token().tok != token.PERIOD {
		return b
	}
	if !ti.go_back() {
		return b
	}
	if ti.token().tok != token.IDENT {
		return b
	}
	return ti.token().literal() + "." + b
}

// Starting from the token under the cursor move back and extract something
// that resembles a valid Go primary expression. Examples of primary expressions
// from Go spec:
//   x
//   2
//   (s + ".txt")
//   f(3.1415, true)
//   Point{1, 2}
//   m["foo"]
//   s[i : j + 1]
//   obj.color
//   f.p[i].x()
//
// As you can see we can move through all of them using balanced bracket
// matching and applying simple rules
// E.g.
//   Point{1, 2}.m["foo"].s[i : j + 1].MethodCall(a, func(a, b int) int { return a + b }).
// Can be seen as:
//   Point{    }.m[     ].s[         ].MethodCall(                                      ).
// Which boils the rules down to these connected via dots:
//   ident
//   ident[]
//   ident{}
//   ident()
// Of course there are also slightly more complicated rules for brackets:
//   ident{}.ident()[5][4](), etc.
func (this *token_iterator) extract_go_expr() string {
	orig := this.token_index

	// Contains the type of the previously scanned token (initialized with
	// the token right under the cursor). This is the token to the *right* of
	// the current one.
	prev := this.token().tok
loop:
	for {
		if !this.go_back() {
			return token_items_to_string(this.tokens[:orig])
		}
		switch this.token().tok {
		case token.PERIOD:
			// If the '.' is not followed by IDENT, it's invalid.
			if prev != token.IDENT {
				break loop
			}
		case token.IDENT:
			// Valid tokens after IDENT are '.', '[', '{' and '('.
			switch prev {
			case token.PERIOD, token.LBRACK, token.LBRACE, token.LPAREN:
				// all ok
			default:
				break loop
			}
		case token.RBRACE:
			// This one can only be a part of type initialization, like:
			//   Dummy{}.Hello()
			// It is valid Go if Hello method is defined on a non-pointer receiver.
			if prev != token.PERIOD {
				break loop
			}
			this.skip_to_balanced_pair()
		case token.RPAREN, token.RBRACK:
			// After ']' and ')' their opening counterparts are valid '[', '(',
			// as well as the dot.
			switch prev {
			case token.PERIOD, token.LBRACK, token.LPAREN:
				// all ok
			default:
				break loop
			}
			this.skip_to_balanced_pair()
		default:
			break loop
		}
		prev = this.token().tok
	}
	return token_items_to_string(this.tokens[this.token_index+1 : orig])
}

// Given a slice of token_item, reassembles them into the original literal
// expression.
func token_items_to_string(tokens []token_item) string {
	var buf bytes.Buffer
	for _, t := range tokens {
		buf.WriteString(t.literal())
	}
	return buf.String()
}

type cursorContext int

const (
	unknownContext cursorContext = iota
	importContext
	selectContext
	compositeLiteralContext
)

func deduce_cursor_context_helper(file []byte, cursor int) (cursorContext, string, string) {
	iter := new_token_iterator(file, cursor)
	if len(iter.tokens) == 0 {
		return unknownContext, "", ""
	}

	// Figure out what is just before the cursor.
	if tok := iter.token(); tok.tok == token.STRING {
		// Make sure cursor is inside the string.
		path := tok.literal()
		off := cursor - tok.off
		if off >= len(path) {
			return unknownContext, "", ""
		}

		// Now figure out if inside an import declaration.
		for {
			if !iter.go_back() {
				break
			}
			if itok := iter.token().tok; itok == token.IDENT || itok == token.PERIOD {
				if !iter.go_back() {
					break
				}
			}
			if iter.token().tok == token.SEMICOLON {
				if !iter.go_back() {
					break
				}
				if iter.token().tok != token.STRING {
					break
				}
				continue
			}
			if iter.token().tok == token.LPAREN {
				if !iter.go_back() {
					break
				}
			}
			if iter.token().tok != token.IMPORT {
				break
			}
			return importContext, "", path[1:off]
		}
		return unknownContext, "", ""
	}

	// See if we have a partial identifier to work with.
	var partial string
	switch tok := iter.token(); tok.tok {
	case token.IDENT, token.TYPE, token.CONST, token.VAR, token.FUNC, token.PACKAGE:
		// we're '<whatever>.<ident>'
		// parse <ident> as Partial and figure out decl

		partial = tok.literal()
		if tok.tok == token.IDENT {
			// Calculate the offset of the cursor position within the identifier.
			// For instance, if we are 'ab#c', we want partial_len = 2 and partial = ab.
			off := cursor - tok.off

			// If it happens that the cursor is past the end of the literal,
			// means there is a space between the literal and the cursor, think
			// of it as no context, because that's what it really is.
			if off > len(tok.literal()) {
				return unknownContext, "", ""
			}
			partial = partial[:off]
		}

		if !iter.go_back() {
			return unknownContext, "", partial
		}
	}

	switch iter.token().tok {
	case token.PERIOD:
		return selectContext, iter.extract_go_expr(), partial
	case token.COMMA, token.LBRACE:
		// This can happen for struct fields:
		// &Struct{Hello: 1, Wor#} // (# - the cursor)
		// Let's try to find the struct type
		return compositeLiteralContext, iter.extract_struct_type(), partial
	}

	return unknownContext, "", partial
}
<commit_msg>suggest: remove token_item.off<commit_after>package suggest

import (
	"bytes"
	"go/scanner"
	"go/token"
)

type token_iterator struct {
	tokens      []token_item
	token_index int
}

type token_item struct {
	tok token.Token
	lit string
}

func (i token_item) literal() string {
	if i.tok.IsLiteral() {
		return i.lit
	}
	return i.tok.String()
}

func new_token_iterator(src []byte, cursor int) (token_iterator, int) {
	fset := token.NewFileSet()
	file := fset.AddFile("", fset.Base(), len(src))
	cursorPos := file.Pos(cursor)

	var s scanner.Scanner
	s.Init(file, src, nil, 0)
	tokens := make([]token_item, 0, 1000)
	lastPos := token.NoPos
	for {
		pos, tok, lit := s.Scan()
		if tok == token.EOF || pos >= cursorPos {
			break
		}
		tokens = append(tokens, token_item{
			tok: tok,
			lit: lit,
		})
		lastPos = pos
	}
	return token_iterator{
		tokens:      tokens,
		token_index: len(tokens) - 1,
	}, int(cursorPos - lastPos)
}

func (this *token_iterator) token() token_item {
	return this.tokens[this.token_index]
}

func (this *token_iterator) go_back() bool {
	if this.token_index <= 0 {
		return false
	}
	this.token_index--
	return true
}

var bracket_pairs_map = map[token.Token]token.Token{
	token.RPAREN: token.LPAREN,
	token.RBRACK: token.LBRACK,
	token.RBRACE: token.LBRACE,
}

func (ti *token_iterator) skip_to_left(left, right token.Token) bool {
	if ti.token().tok == left {
		return true
	}
	balance := 1
	for balance != 0 {
		if !ti.go_back() {
			return false
		}
		switch ti.token().tok {
		case right:
			balance++
		case left:
			balance--
		}
	}
	return true
}

// when the cursor is at the ')' or ']' or '}', move the cursor to an opposite
// bracket pair, this functions takes nested bracket pairs into account
func (this *token_iterator) skip_to_balanced_pair() bool {
	right := this.token().tok
	left := bracket_pairs_map[right]
	return this.skip_to_left(left, right)
}

// Move the cursor to the open brace of the current block, taking nested blocks
// into account.
func (this *token_iterator) skip_to_left_curly() bool {
	return this.skip_to_left(token.LBRACE, token.RBRACE)
}

// Extract the type expression right before the enclosing curly bracket block.
// Examples (# - the cursor):
//   &lib.Struct{Whatever: 1, Hel#} // returns "lib.Struct"
//   X{#}                           // returns X
// The idea is that we check if this type expression is a type and it is, we
// can apply special filtering for autocompletion results.
// Sadly, this doesn't cover anonymous structs.
func (ti *token_iterator) extract_struct_type() (res string) {
	if !ti.skip_to_left_curly() {
		return ""
	}
	if !ti.go_back() {
		return ""
	}
	if ti.token().tok != token.IDENT {
		return ""
	}
	b := ti.token().literal()
	if !ti.go_back() {
		return b
	}
	if ti.token().tok != token.PERIOD {
		return b
	}
	if !ti.go_back() {
		return b
	}
	if ti.token().tok != token.IDENT {
		return b
	}
	return ti.token().literal() + "." + b
}

// Starting from the token under the cursor move back and extract something
// that resembles a valid Go primary expression. Examples of primary expressions
// from Go spec:
//   x
//   2
//   (s + ".txt")
//   f(3.1415, true)
//   Point{1, 2}
//   m["foo"]
//   s[i : j + 1]
//   obj.color
//   f.p[i].x()
//
// As you can see we can move through all of them using balanced bracket
// matching and applying simple rules
// E.g.
//   Point{1, 2}.m["foo"].s[i : j + 1].MethodCall(a, func(a, b int) int { return a + b }).
// Can be seen as:
//   Point{    }.m[     ].s[         ].MethodCall(                                      ).
// Which boils the rules down to these connected via dots:
//   ident
//   ident[]
//   ident{}
//   ident()
// Of course there are also slightly more complicated rules for brackets:
//   ident{}.ident()[5][4](), etc.
func (this *token_iterator) extract_go_expr() string {
	orig := this.token_index

	// Contains the type of the previously scanned token (initialized with
	// the token right under the cursor). This is the token to the *right* of
	// the current one.
	prev := this.token().tok
loop:
	for {
		if !this.go_back() {
			return token_items_to_string(this.tokens[:orig])
		}
		switch this.token().tok {
		case token.PERIOD:
			// If the '.' is not followed by IDENT, it's invalid.
			if prev != token.IDENT {
				break loop
			}
		case token.IDENT:
			// Valid tokens after IDENT are '.', '[', '{' and '('.
			switch prev {
			case token.PERIOD, token.LBRACK, token.LBRACE, token.LPAREN:
				// all ok
			default:
				break loop
			}
		case token.RBRACE:
			// This one can only be a part of type initialization, like:
			//   Dummy{}.Hello()
			// It is valid Go if Hello method is defined on a non-pointer receiver.
			if prev != token.PERIOD {
				break loop
			}
			this.skip_to_balanced_pair()
		case token.RPAREN, token.RBRACK:
			// After ']' and ')' their opening counterparts are valid '[', '(',
			// as well as the dot.
			switch prev {
			case token.PERIOD, token.LBRACK, token.LPAREN:
				// all ok
			default:
				break loop
			}
			this.skip_to_balanced_pair()
		default:
			break loop
		}
		prev = this.token().tok
	}
	return token_items_to_string(this.tokens[this.token_index+1 : orig])
}

// Given a slice of token_item, reassembles them into the original literal
// expression.
func token_items_to_string(tokens []token_item) string {
	var buf bytes.Buffer
	for _, t := range tokens {
		buf.WriteString(t.literal())
	}
	return buf.String()
}

type cursorContext int

const (
	unknownContext cursorContext = iota
	importContext
	selectContext
	compositeLiteralContext
)

func deduce_cursor_context_helper(file []byte, cursor int) (cursorContext, string, string) {
	iter, off := new_token_iterator(file, cursor)
	if len(iter.tokens) == 0 {
		return unknownContext, "", ""
	}

	// Figure out what is just before the cursor.
	if tok := iter.token(); tok.tok == token.STRING {
		// Make sure cursor is inside the string.
		path := tok.literal()
		if off >= len(path) {
			return unknownContext, "", ""
		}

		// Now figure out if inside an import declaration.
		for {
			if !iter.go_back() {
				break
			}
			if itok := iter.token().tok; itok == token.IDENT || itok == token.PERIOD {
				if !iter.go_back() {
					break
				}
			}
			if iter.token().tok == token.SEMICOLON {
				if !iter.go_back() {
					break
				}
				if iter.token().tok != token.STRING {
					break
				}
				continue
			}
			if iter.token().tok == token.LPAREN {
				if !iter.go_back() {
					break
				}
			}
			if iter.token().tok != token.IMPORT {
				break
			}
			return importContext, "", path[1:off]
		}
		return unknownContext, "", ""
	}

	// See if we have a partial identifier to work with.
	var partial string
	switch tok := iter.token(); tok.tok {
	case token.IDENT, token.TYPE, token.CONST, token.VAR, token.FUNC, token.PACKAGE:
		// we're '<whatever>.<ident>'
		// parse <ident> as Partial and figure out decl

		partial = tok.literal()
		if tok.tok == token.IDENT {
			// If it happens that the cursor is past the end of the literal,
			// means there is a space between the literal and the cursor, think
			// of it as no context, because that's what it really is.
			if off > len(tok.literal()) {
				return unknownContext, "", ""
			}
			partial = partial[:off]
		}

		if !iter.go_back() {
			return unknownContext, "", partial
		}
	}

	switch iter.token().tok {
	case token.PERIOD:
		return selectContext, iter.extract_go_expr(), partial
	case token.COMMA, token.LBRACE:
		// This can happen for struct fields:
		// &Struct{Hello: 1, Wor#} // (# - the cursor)
		// Let's try to find the struct type
		return compositeLiteralContext, iter.extract_struct_type(), partial
	}

	return unknownContext, "", partial
}
<|endoftext|>


<commit_before>package supervisor

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"sync"
	"sync/atomic"
	"time"
)

type (
	Event struct {
		Code    int
		Message string
		Time    time.Time
	}

	Process struct {
		// communication
		Stdout chan *[]byte
		Stderr chan *[]byte
		Stdin  chan *[]byte

		// internal usage
		done    chan string
		closed  int32
		killed  int32
		stopped int32

		command string
		options *Options

		// safe variables
		mu               *sync.Mutex
		cmd              *exec.Cmd
		pid              int
		needToNotifyDone bool
		needToSendEvents bool
		doneChannel      chan bool
		eventsChannel    chan *Event
		lastError        error
	}

	Options struct {
		Args                    []string // argumets to pass
		SpawnAttempts           int      // attempts before giving up
		AttemptsBeforeTerminate int      // on Stop() terminate process after X interrupt attempts
		Debug                   bool     // print events to stdout
		Dir                     string   // run dir
		Id                      string   // will be added to every log print
		MaxSpawns               int      // Max spawn limit
		StdoutIdleTime          int      // stop worker if we didn't recived stdout message in X seconds
		StderrIdleTime          int      // stop worker if we didn't recived stderr message in X seconds

		DelayBetweenSpawns func(currentSleep int) (sleep int) // in seconds
	}
)

// public

func Supervise(command string, opt ...Options) (p *Process, err error) {
	options := &Options{}
	if len(opt) > 0 {
		options = &opt[0]
	}

	if options.Args == nil {
		options.Args = make([]string, 0)
	}

	if options.AttemptsBeforeTerminate == 0 {
		options.AttemptsBeforeTerminate = 10
	}

	if options.DelayBetweenSpawns == nil {
		options.DelayBetweenSpawns = func(currentSleep int) (sleepTime int) {
			if currentSleep > 500 {
				sleepTime = 1
			} else {
				sleepTime = currentSleep * 2
			}
			return sleepTime
		}
	}

	if options.Id == "" {
		options.Id = "ID"
	}

	if options.SpawnAttempts == 0 {
		options.SpawnAttempts = 10
	}

	if options.MaxSpawns == 0 {
		options.MaxSpawns = 1
	}

	p = &Process{
		command: command,
		options: options,
		Stdout:  make(chan *[]byte, 1000),
		Stderr:  make(chan *[]byte, 1000),
		Stdin:   make(chan *[]byte, 1000),
		done:    make(chan string),
		mu:      &sync.Mutex{},
	}

	if err := p.start(); err != nil {
		return p, err
	}
	go p.watch()
	return p, nil
}

func (p *Process) LastError() error {
	p.mu.Lock()
	defer p.mu.Unlock()
	return p.lastError
}

func (p *Process) Pid() int {
	p.mu.Lock()
	defer p.mu.Unlock()
	return p.pid
}

func (p *Process) NotifyEvents(c chan *Event) (channel chan *Event) {
	p.mu.Lock()
	defer p.mu.Unlock()
	p.needToSendEvents = true
	p.eventsChannel = c
	return c
}

func (p *Process) NotifyDone(c chan bool) (channel chan bool) {
	p.mu.Lock()
	defer p.mu.Unlock()
	p.needToNotifyDone = true
	p.doneChannel = c
	return c
}

func (p *Process) Running() bool {
	if p.cmd == nil {
		return false
	} else if p.isKilled() {
		return false
	} else if p.cmd.ProcessState != nil {
		return !p.cmd.ProcessState.Exited()
	} else {
		return true
	}
}

func (p *Process) Stop() {
	if p.isClosed(true) {
		done := make(chan bool)
		p.stop()
		if p.needToNotifyDone {
			go func() {
				p.doneChannel <- true
				p.closeChannels()
				done <- true
			}()
		} else {
			go func() {
				p.closeChannels()
				done <- true
			}()
		}
		<-done
	}
}

func (p *Process) IsDone() bool {
	return p.isClosed()
}

// private
func (p *Process) closeChannels() {
	close(p.Stdin)
	close(p.Stderr)
	close(p.Stdout)
	if p.needToSendEvents {
		close(p.eventsChannel)
	}
	if p.needToNotifyDone {
		close(p.doneChannel)
	}
}

func (p *Process) start() error {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.isClosed() {
		return nil
	}

	var err error

	p.cmd = exec.Command(p.command, p.options.Args...)

	if p.options.Dir != "" {
		p.cmd.Dir = p.options.Dir
	}

	stdout, stderr, stdin, err := p.openPipes()
	if err != nil {
		return err
	}

	p.isStopped(false)
	p.isKilled(false)

	go p.handleIn(stdin, p.Stdin)
	go p.handleOut("stdout", stdout, p.Stdout, p.options.StdoutIdleTime)
	go p.handleOut("stderr", stderr, p.Stderr, p.options.StderrIdleTime)

	p.event(8, "starting instance...")
	err = p.cmd.Start()
	if err != nil {
		return err
	}

	p.pid = p.cmd.Process.Pid

	p.event(22, "instance ready...")

	return nil
}

// run in its own goroutine
func (p *Process) watch() {
	attempt := 1
	currentSleep := 1
	numSpawns := 1
	for {
		start := time.Now()
		p.lastError = p.cmd.Wait()
		if p.isClosed() {
			break
		}

		p.event(7, "instance crashed...")

		if numSpawns >= p.options.MaxSpawns {
			p.event(13, "reached max spawns...")
			p.Stop() // cleanup
			break
		}

		if (time.Now().Sub(start).Seconds()) > 60 {
			attempt = 1
			currentSleep = 1
		} else {
			attempt += 1
			currentSleep = p.options.DelayBetweenSpawns(currentSleep)
		}
		if attempt > p.options.SpawnAttempts {
			p.event(9, "giving up, instance failed to start...")
			p.Stop() // shutting down instance and send done notification...
			break
		}

		p.event(10, "going to sleep for %d seconds...", currentSleep)
		p.stop() // cleanup
		p.event(29, "entering sleep stage...")

		milliseconds := currentSleep * 1000
		waited := 0
		for waited < milliseconds {
			time.Sleep(10 * time.Millisecond)
			waited += 10
			if p.isClosed() {
				break
			}
		}

		p.start()
	}
	p.event(11, "watch daemon is off...")
}

func (p *Process) Restart() {
	p.stop()
}

func (p *Process) stop() {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.isStopped() {
		return
	}

	defer p.isStopped(true)

	p.event(20, "going to kill process..")

	attempts := 0
	for p.Running() {
		if p.cmd != nil && p.cmd.Process != nil {
			attempts++
			if attempts < p.options.AttemptsBeforeTerminate {
				p.event(3, "sending interrupt to process - attempt %d", attempts)
				p.cmd.Process.Signal(os.Interrupt)
				time.Sleep(1 * time.Second)
			} else {
				p.event(4, "refuse to quit, kill it (pid %d)...", p.cmd.Process.Pid)
				p.cmd.Process.Kill()
				p.cmd.Process.Signal(os.Kill)
				p.isKilled(true)
				time.Sleep(1 * time.Second)
				break
			}
		}
	}

	i := 0
	t := 0
	for {
		select {
		case who := <-p.done:
			i++
			p.event(5, "%s goroutine is done...", who)
			if i >= 3 {
				return
			}
		case <-time.After(time.Second * 1):
			p.event(6, "waiting for goroutines to quit...")
			t++
			if t > 60 {
				p.event(14, "waited too long exiting... some goroutines are still alive...")
				return
			}
		}
	}

}

// runs in its own goroutine
func (p *Process) handleIn(in io.WriteCloser, channel chan *[]byte) {
	p.event(0, "opening stdin handler...")
Loop:
	for {
		select {
		case message, ok := <-channel:
			if ok {
				buff := bytes.NewBuffer(*message)
				_, _ = buff.WriteString("\n")
				_, err := in.Write(buff.Bytes())
				if err != nil {
					p.event(0, "can't write STDIN %s", err)
					p.done <- "stdin"
					break Loop
				}
			}
		case f := <-p.done:
			p.done <- f
			p.done <- "stdin"
			break Loop
		}
	}
	p.event(19, "closing stdin handler...")
}

func (p *Process) getHeartbeater(name string, seconds int) chan bool {
	c := make(chan bool, 1000)

	go func() {
		for {
			select {
			case msg := <-c:
				if !msg {
					return
				}
			case <-time.After(time.Second * time.Duration(seconds)):
				p.event(15, "%s - reached timeout, restarting instance...", name)
				p.stop()
				return
			}
		}
	}()

	return c
}

// runs in its own goroutine
func (p *Process) handleOut(name string, out *bufio.Reader, channel chan *[]byte, heartbeat int) {
	p.event(0, "opening %v handler...", name)
	var heartbeatChannel chan bool
	shouldHeartbeat := heartbeat > 0

	if shouldHeartbeat {
		heartbeatChannel = p.getHeartbeater(name, heartbeat)
	}
	beat := func(k bool) {
		if shouldHeartbeat {
			heartbeatChannel <- k
		}
	}

	for {
		line, err := out.ReadBytes('\n')
		beat(true)

		if err != nil {
			p.event(1, "can't read from %s: %s", name, err)
			break
		}
		channel <- &line
	}

	beat(false)
	p.done <- name
}

func (p *Process) event(code int, message string, format ...interface{}) {
	msg := &Event{
		Message: fmt.Sprintf(("[%s] " + message), append([]interface{}{p.options.Id}, format...)...),
		Time:    time.Now(),
		Code:    code,
	}

	if p.options.Debug {
		log.Printf("%s", msg.Message)
	}

	if p.needToSendEvents && !p.isClosed() {
		p.eventsChannel <- msg
	}
}

func (p *Process) openPipes() (stdout, stderr *bufio.Reader, stdin io.WriteCloser, err error) {
	stdin, err = p.cmd.StdinPipe()

	if err != nil {
		return nil, nil, nil, fmt.Errorf("failed to get stdin pipe: %s", err)
	}

	out, err := p.cmd.StdoutPipe()
	if err != nil {
		return nil, nil, nil, fmt.Errorf("failed to get stdout pipe: %s", err)
	}
	stdout = bufio.NewReader(out)

	er, err := p.cmd.StderrPipe()
	if err != nil {
		return nil, nil, nil, fmt.Errorf("failed to get stderr pipe: %s", err)
	}
	stderr = bufio.NewReader(er)

	return stdout, stderr, stdin, nil
}

func (p *Process) isKilled(killed ...bool) bool {
	return isSomething(&p.killed, killed)
}

func (p *Process) isClosed(closed ...bool) bool {
	return isSomething(&p.closed, closed)
}

func (p *Process) isStopped(stop ...bool) bool {
	return isSomething(&p.stopped, stop)
}

func isSomething(n *int32, o []bool) bool {
	if len(o) > 0 {
		if o[0] {
			return atomic.CompareAndSwapInt32(n, 0, 1)
		} else {
			return atomic.CompareAndSwapInt32(n, 1, 0)
		}
	} else {
		return atomic.LoadInt32(n) == 1
	}

}
<commit_msg>increased buffer channels<commit_after>package supervisor

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"sync"
	"sync/atomic"
	"time"
)

type (
	Event struct {
		Code    int
		Message string
		Time    time.Time
	}

	Process struct {
		// communication
		Stdout chan *[]byte
		Stderr chan *[]byte
		Stdin  chan *[]byte

		// internal usage
		done    chan string
		closed  int32
		killed  int32
		stopped int32

		command string
		options *Options

		// safe variables
		mu               *sync.Mutex
		cmd              *exec.Cmd
		pid              int
		needToNotifyDone bool
		needToSendEvents bool
		doneChannel      chan bool
		eventsChannel    chan *Event
		lastError        error
	}

	Options struct {
		Args                    []string // argumets to pass
		SpawnAttempts           int      // attempts before giving up
		AttemptsBeforeTerminate int      // on Stop() terminate process after X interrupt attempts
		Debug                   bool     // print events to stdout
		Dir                     string   // run dir
		Id                      string   // will be added to every log print
		MaxSpawns               int      // Max spawn limit
		StdoutIdleTime          int      // stop worker if we didn't recived stdout message in X seconds
		StderrIdleTime          int      // stop worker if we didn't recived stderr message in X seconds

		DelayBetweenSpawns func(currentSleep int) (sleep int) // in seconds
	}
)

// public

func Supervise(command string, opt ...Options) (p *Process, err error) {
	options := &Options{}
	if len(opt) > 0 {
		options = &opt[0]
	}

	if options.Args == nil {
		options.Args = make([]string, 0)
	}

	if options.AttemptsBeforeTerminate == 0 {
		options.AttemptsBeforeTerminate = 10
	}

	if options.DelayBetweenSpawns == nil {
		options.DelayBetweenSpawns = func(currentSleep int) (sleepTime int) {
			if currentSleep > 500 {
				sleepTime = 1
			} else {
				sleepTime = currentSleep * 2
			}
			return sleepTime
		}
	}

	if options.Id == "" {
		options.Id = "ID"
	}

	if options.SpawnAttempts == 0 {
		options.SpawnAttempts = 10
	}

	if options.MaxSpawns == 0 {
		options.MaxSpawns = 1
	}

	p = &Process{
		command: command,
		options: options,
		Stdout:  make(chan *[]byte, 1000000),
		Stderr:  make(chan *[]byte, 1000000),
		Stdin:   make(chan *[]byte, 1000000),
		done:    make(chan string),
		mu:      &sync.Mutex{},
	}

	if err := p.start(); err != nil {
		return p, err
	}
	go p.watch()
	return p, nil
}

func (p *Process) LastError() error {
	p.mu.Lock()
	defer p.mu.Unlock()
	return p.lastError
}

func (p *Process) Pid() int {
	p.mu.Lock()
	defer p.mu.Unlock()
	return p.pid
}

func (p *Process) NotifyEvents(c chan *Event) (channel chan *Event) {
	p.mu.Lock()
	defer p.mu.Unlock()
	p.needToSendEvents = true
	p.eventsChannel = c
	return c
}

func (p *Process) NotifyDone(c chan bool) (channel chan bool) {
	p.mu.Lock()
	defer p.mu.Unlock()
	p.needToNotifyDone = true
	p.doneChannel = c
	return c
}

func (p *Process) Running() bool {
	if p.cmd == nil {
		return false
	} else if p.isKilled() {
		return false
	} else if p.cmd.ProcessState != nil {
		return !p.cmd.ProcessState.Exited()
	} else {
		return true
	}
}

func (p *Process) Stop() {
	if p.isClosed(true) {
		done := make(chan bool)
		p.stop()
		if p.needToNotifyDone {
			go func() {
				p.doneChannel <- true
				p.closeChannels()
				done <- true
			}()
		} else {
			go func() {
				p.closeChannels()
				done <- true
			}()
		}
		<-done
	}
}

func (p *Process) IsDone() bool {
	return p.isClosed()
}

// private
func (p *Process) closeChannels() {
	close(p.Stdin)
	close(p.Stderr)
	close(p.Stdout)
	if p.needToSendEvents {
		close(p.eventsChannel)
	}
	if p.needToNotifyDone {
		close(p.doneChannel)
	}
}

func (p *Process) start() error {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.isClosed() {
		return nil
	}

	var err error

	p.cmd = exec.Command(p.command, p.options.Args...)

	if p.options.Dir != "" {
		p.cmd.Dir = p.options.Dir
	}

	stdout, stderr, stdin, err := p.openPipes()
	if err != nil {
		return err
	}

	p.isStopped(false)
	p.isKilled(false)

	go p.handleIn(stdin, p.Stdin)
	go p.handleOut("stdout", stdout, p.Stdout, p.options.StdoutIdleTime)
	go p.handleOut("stderr", stderr, p.Stderr, p.options.StderrIdleTime)

	p.event(8, "starting instance...")
	err = p.cmd.Start()
	if err != nil {
		return err
	}

	p.pid = p.cmd.Process.Pid

	p.event(22, "instance ready...")

	return nil
}

// run in its own goroutine
func (p *Process) watch() {
	attempt := 1
	currentSleep := 1
	numSpawns := 1
	for {
		start := time.Now()
		p.lastError = p.cmd.Wait()
		if p.isClosed() {
			break
		}

		p.event(7, "instance crashed...")

		if numSpawns >= p.options.MaxSpawns {
			p.event(13, "reached max spawns...")
			p.Stop() // cleanup
			break
		}

		if (time.Now().Sub(start).Seconds()) > 60 {
			attempt = 1
			currentSleep = 1
		} else {
			attempt += 1
			currentSleep = p.options.DelayBetweenSpawns(currentSleep)
		}
		if attempt > p.options.SpawnAttempts {
			p.event(9, "giving up, instance failed to start...")
			p.Stop() // shutting down instance and send done notification...
			break
		}

		p.event(10, "going to sleep for %d seconds...", currentSleep)
		p.stop() // cleanup
		p.event(29, "entering sleep stage...")

		milliseconds := currentSleep * 1000
		waited := 0
		for waited < milliseconds {
			time.Sleep(10 * time.Millisecond)
			waited += 10
			if p.isClosed() {
				break
			}
		}

		p.start()
	}
	p.event(11, "watch daemon is off...")
}

func (p *Process) Restart() {
	p.stop()
}

func (p *Process) stop() {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.isStopped() {
		return
	}

	defer p.isStopped(true)

	p.event(20, "going to kill process..")

	attempts := 0
	for p.Running() {
		if p.cmd != nil && p.cmd.Process != nil {
			attempts++
			if attempts < p.options.AttemptsBeforeTerminate {
				p.event(3, "sending interrupt to process - attempt %d", attempts)
				p.cmd.Process.Signal(os.Interrupt)
				time.Sleep(1 * time.Second)
			} else {
				p.event(4, "refuse to quit, kill it (pid %d)...", p.cmd.Process.Pid)
				p.cmd.Process.Kill()
				p.cmd.Process.Signal(os.Kill)
				p.isKilled(true)
				time.Sleep(1 * time.Second)
				break
			}
		}
	}

	i := 0
	t := 0
	for {
		select {
		case who := <-p.done:
			i++
			p.event(5, "%s goroutine is done...", who)
			if i >= 3 {
				return
			}
		case <-time.After(time.Second * 1):
			p.event(6, "waiting for goroutines to quit...")
			t++
			if t > 60 {
				p.event(14, "waited too long exiting... some goroutines are still alive...")
				return
			}
		}
	}

}

// runs in its own goroutine
func (p *Process) handleIn(in io.WriteCloser, channel chan *[]byte) {
	p.event(0, "opening stdin handler...")
Loop:
	for {
		select {
		case message, ok := <-channel:
			if ok {
				buff := bytes.NewBuffer(*message)
				_, _ = buff.WriteString("\n")
				_, err := in.Write(buff.Bytes())
				if err != nil {
					p.event(0, "can't write STDIN %s", err)
					p.done <- "stdin"
					break Loop
				}
			}
		case f := <-p.done:
			p.done <- f
			p.done <- "stdin"
			break Loop
		}
	}
	p.event(19, "closing stdin handler...")
}

func (p *Process) getHeartbeater(name string, seconds int) chan bool {
	c := make(chan bool, 1000)

	go func() {
		for {
			select {
			case msg := <-c:
				if !msg {
					return
				}
			case <-time.After(time.Second * time.Duration(seconds)):
				p.event(15, "%s - reached timeout, restarting instance...", name)
				p.stop()
				return
			}
		}
	}()

	return c
}

// runs in its own goroutine
func (p *Process) handleOut(name string, out *bufio.Reader, channel chan *[]byte, heartbeat int) {
	p.event(0, "opening %v handler...", name)
	var heartbeatChannel chan bool
	shouldHeartbeat := heartbeat > 0

	if shouldHeartbeat {
		heartbeatChannel = p.getHeartbeater(name, heartbeat)
	}
	beat := func(k bool) {
		if shouldHeartbeat {
			heartbeatChannel <- k
		}
	}

	for {
		line, err := out.ReadBytes('\n')
		beat(true)

		if err != nil {
			p.event(1, "can't read from %s: %s", name, err)
			break
		}
		channel <- &line
	}

	beat(false)
	p.done <- name
}

func (p *Process) event(code int, message string, format ...interface{}) {
	msg := &Event{
		Message: fmt.Sprintf(("[%s] " + message), append([]interface{}{p.options.Id}, format...)...),
		Time:    time.Now(),
		Code:    code,
	}

	if p.options.Debug {
		log.Printf("%s", msg.Message)
	}

	if p.needToSendEvents && !p.isClosed() {
		p.eventsChannel <- msg
	}
}

func (p *Process) openPipes() (stdout, stderr *bufio.Reader, stdin io.WriteCloser, err error) {
	stdin, err = p.cmd.StdinPipe()

	if err != nil {
		return nil, nil, nil, fmt.Errorf("failed to get stdin pipe: %s", err)
	}

	out, err := p.cmd.StdoutPipe()
	if err != nil {
		return nil, nil, nil, fmt.Errorf("failed to get stdout pipe: %s", err)
	}
	stdout = bufio.NewReader(out)

	er, err := p.cmd.StderrPipe()
	if err != nil {
		return nil, nil, nil, fmt.Errorf("failed to get stderr pipe: %s", err)
	}
	stderr = bufio.NewReader(er)

	return stdout, stderr, stdin, nil
}

func (p *Process) isKilled(killed ...bool) bool {
	return isSomething(&p.killed, killed)
}

func (p *Process) isClosed(closed ...bool) bool {
	return isSomething(&p.closed, closed)
}

func (p *Process) isStopped(stop ...bool) bool {
	return isSomething(&p.stopped, stop)
}

func isSomething(n *int32, o []bool) bool {
	if len(o) > 0 {
		if o[0] {
			return atomic.CompareAndSwapInt32(n, 0, 1)
		} else {
			return atomic.CompareAndSwapInt32(n, 1, 0)
		}
	} else {
		return atomic.LoadInt32(n) == 1
	}

}
<|endoftext|>


<commit_before>package main

import (
  "testing"
)

// Test the haversineDistance between London and Paris
// The result should be around 343 km.
func TestHaversineDistance(t *testing.T) {
  london := Location{51.5073, -0.12755}
  paris := Location{48.853363, 2.348749}

  result := haversineDistance(london, paris)

  // We allow 1% as margin of error
  if result > 346.43 || result < 339.57 {
    t.Fatalf("Expected %d but got %d.", 343, result)
  }
}
<commit_msg>Add TestDegreesToRadians<commit_after>package main

import (
  "testing"
)

// Test degreesToRadians
func TestDegreesToRadians(t *testing.T) {
  values := []struct {
    input float64 // value in degrees
    output float64 // expected output
  }{
    {0, 0.0},
    {90, 1.5708},
    {180, 3.141593},
    {360, 0.0},
  }

  for i, v := range values {
    actual := degreesToRadians(v.input)

    // We allow 1‰ as margin of error through rounding
    if actual > 1.001 * v.output || actual < 0.999 * v.output {
      t.Fatalf("Expected %f in iteration %d, got %f.", v.output, i, actual)
    }
  }
}

// Test the haversineDistance between London and Paris
// The result should be around 343 km.
func TestHaversineDistance(t *testing.T) {
  london := Location{51.5073, -0.12755}
  paris := Location{48.853363, 2.348749}

  result := haversineDistance(london, paris)

  // We allow 1% as margin of error
  if result > 346.43 || result < 339.57 {
    t.Fatalf("Expected %d but got %d.", 343, result)
  }
}
<|endoftext|>


<commit_before>// Package convey contains all of the public-facing entry points to this project.
// This means that it should never be required of the user to import any other
// packages from this project as they serve internal purposes.
package convey

import (
	"github.com/smartystreets/goconvey/execution"
	"github.com/smartystreets/goconvey/reporting"
)

// Convey is the method intended for use when declaring the scopes
// of a specification. Each scope has a description and a func()
// which may contain other calls to Convey(), Reset() or Should-style
// assertions. Convey calls can be nested as far as you see fit.
//
// IMPORTANT NOTE: The top-level Convey() within a Test method
// must conform to the following signature:
//
//     Convey(description string, t *testing.T, action func())
//
// All other calls should like like this (no need to pass in *testing.T):
//
//     Convey(description string, action func())
//
// Don't worry, the goconvey will panic if you get it wrong so you can fix it.
//
// See the examples package for, well, examples.
func Convey(items ...interface{}) {
	entry := discover(items)
	register(entry)
}

// SkipConvey is analagous to Convey except that the scope is not executed
// (which means that child scopes defined within this scope are not run either).
// The reporter will be notified that this step was skipped.
func SkipConvey(items ...interface{}) {
	entry := discover(items)
	entry.Action = execution.NewSkippedAction(skipReport)
	register(entry)
}

func register(entry *execution.Registration) {
	if entry.IsTopLevel() {
		suites.Run(entry)
	} else {
		suites.CurrentRunner().Register(entry)
	}
}

func skipReport() {
	suites.CurrentReporter().Report(reporting.NewSkipReport())
}

// Reset registers a cleanup function to be run after each Convey()
// in the same scope. See the examples package for a simple use case.
func Reset(action func()) {
	suites.CurrentRunner().RegisterReset(execution.NewAction(action))
}

// So is the means by which assertions are made against the system under test.
// The majority of exported names in this package begin with the word 'Should'
// and describe how the first argument (actual) should compare with any of the
// final (expected) arguments. How many final arguments are accepted depends on
// the particular assertion that is passed in as the assert argument.
// See the examples package for use cases and the assertions package for
// documentation on specific assertion methods.
func So(actual interface{}, assert assertion, expected ...interface{}) {
	if result := assert(actual, expected...); result == assertionSuccess {
		suites.CurrentReporter().Report(reporting.NewSuccessReport())
	} else {
		suites.CurrentReporter().Report(reporting.NewFailureReport(result))
	}
}

// SkipSo is analagous to So except that the assertion that would have been passed
// to So is not executed and the reporter is notified that the assertion was skipped.
func SkipSo(stuff ...interface{}) {
	skipReport()
}

// assertion is an alias for a function with a signature that the convey.So()
// method can handle. Any future or custom assertions should conform to this
// method signature. The return value should be an empty string if the assertion
// passes and a well-formed failure message if not.
type assertion func(actual interface{}, expected ...interface{}) string

const assertionSuccess = ""
<commit_msg>Corrected godoc.<commit_after>// Package convey contains all of the public-facing entry points to this project.
// This means that it should never be required of the user to import any other
// packages from this project as they serve internal purposes.
package convey

import (
	"github.com/smartystreets/goconvey/execution"
	"github.com/smartystreets/goconvey/reporting"
)

// Convey is the method intended for use when declaring the scopes
// of a specification. Each scope has a description and a func()
// which may contain other calls to Convey(), Reset() or Should-style
// assertions. Convey calls can be nested as far as you see fit.
//
// IMPORTANT NOTE: The top-level Convey() within a Test method
// must conform to the following signature:
//
//     Convey(description string, t *testing.T, action func())
//
// All other calls should like like this (no need to pass in *testing.T):
//
//     Convey(description string, action func())
//
// Don't worry, the goconvey will panic if you get it wrong so you can fix it.
//
// See the examples package for, well, examples.
func Convey(items ...interface{}) {
	entry := discover(items)
	register(entry)
}

// SkipConvey is analagous to Convey except that the scope is not executed
// (which means that child scopes defined within this scope are not run either).
// The reporter will be notified that this step was skipped.
func SkipConvey(items ...interface{}) {
	entry := discover(items)
	entry.Action = execution.NewSkippedAction(skipReport)
	register(entry)
}

func register(entry *execution.Registration) {
	if entry.IsTopLevel() {
		suites.Run(entry)
	} else {
		suites.CurrentRunner().Register(entry)
	}
}

func skipReport() {
	suites.CurrentReporter().Report(reporting.NewSkipReport())
}

// Reset registers a cleanup function to be run after each Convey()
// in the same scope. See the examples package for a simple use case.
func Reset(action func()) {
	suites.CurrentRunner().RegisterReset(execution.NewAction(action))
}

// So is the means by which assertions are made against the system under test.
// The majority of exported names in the assertions package begin with the word
// 'Should' and describe how the first argument (actual) should compare with any
// of the final (expected) arguments. How many final arguments are accepted
// depends on the particular assertion that is passed in as the assert argument.
// See the examples package for use cases and the assertions package for
// documentation on specific assertion methods.
func So(actual interface{}, assert assertion, expected ...interface{}) {
	if result := assert(actual, expected...); result == assertionSuccess {
		suites.CurrentReporter().Report(reporting.NewSuccessReport())
	} else {
		suites.CurrentReporter().Report(reporting.NewFailureReport(result))
	}
}

// SkipSo is analagous to So except that the assertion that would have been passed
// to So is not executed and the reporter is notified that the assertion was skipped.
func SkipSo(stuff ...interface{}) {
	skipReport()
}

// assertion is an alias for a function with a signature that the convey.So()
// method can handle. Any future or custom assertions should conform to this
// method signature. The return value should be an empty string if the assertion
// passes and a well-formed failure message if not.
type assertion func(actual interface{}, expected ...interface{}) string

const assertionSuccess = ""
<|endoftext|>


<commit_before><commit_msg>Send all questions if q.Id is nil<commit_after><|endoftext|>


<commit_before>package svnwatch

import (
	"encoding/xml"
	"fmt"
	"os"
	"os/exec"
	"strconv"

	"github.com/jackwilsdon/svnwatch/svn"
	shellwords "github.com/mattn/go-shellwords"
	"github.com/pkg/errors"
)

type Command struct {
	XMLName      xml.Name `xml:"command"`
	ArgumentType string   `xml:"argument-type,attr,omitempty"`
	Command      string   `xml:",chardata"`
}

func (c Command) Execute(repo Repository, revision svn.Revision) error {
	pieces, err := shellwords.Parse(c.Command)

	if err != nil {
		return errors.Wrapf(err, "failed to parse \"%s\"", c.Command)
	}

	if len(pieces) == 0 {
		return fmt.Errorf("failed to parse \"%s\"", c.Command)
	}

	cmd := exec.Command(pieces[0], pieces[1:]...)
	cmd.Env = os.Environ()

	if c.ArgumentType == "normal" {
		cmd.Args = append(cmd.Args, repo.URL, strconv.Itoa(repo.Revision))
	} else if c.ArgumentType == "env" {
		cmd.Env = append(
			os.Environ(),
			fmt.Sprintf("SVN_URL=%s", repo.URL),
			fmt.Sprintf("SVN_REVISION=%d", repo.Revision),
		)
	} else {
		return fmt.Errorf("invalid argument type \"%s\" for \"%s\"", c.ArgumentType, c.Command)
	}

	if err := cmd.Run(); err != nil {
		return errors.Wrapf(err, "failed to execute \"%s\"", c.Command)
	}

	return nil
}

func (c *Command) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	cmd := struct {
		ArgumentType *string `xml:"argument-type,attr,omitempty"`
		Command      string  `xml:"chardata"`
	}{}

	if err := d.DecodeElement(&cmd, &start); err != nil {
		return err
	}

	if cmd.ArgumentType == nil {
		c.ArgumentType = "normal"
	} else if *cmd.ArgumentType == "normal" || *cmd.ArgumentType == "env" {
		c.ArgumentType = *cmd.ArgumentType
	} else {
		return fmt.Errorf("invalid argument type \"%s\" for \"%s\"", *cmd.ArgumentType, cmd.Command)
	}

	c.Command = cmd.Command

	return nil
}
<commit_msg>Correctly retrieve command contents when unmarshalling<commit_after>package svnwatch

import (
	"encoding/xml"
	"fmt"
	"os"
	"os/exec"
	"strconv"

	"github.com/jackwilsdon/svnwatch/svn"
	shellwords "github.com/mattn/go-shellwords"
	"github.com/pkg/errors"
)

type Command struct {
	XMLName      xml.Name `xml:"command"`
	ArgumentType string   `xml:"argument-type,attr,omitempty"`
	Command      string   `xml:",chardata"`
}

func (c Command) Execute(repo Repository, revision svn.Revision) error {
	pieces, err := shellwords.Parse(c.Command)

	if err != nil {
		return errors.Wrapf(err, "failed to parse \"%s\"", c.Command)
	}

	if len(pieces) == 0 {
		return fmt.Errorf("failed to parse \"%s\"", c.Command)
	}

	cmd := exec.Command(pieces[0], pieces[1:]...)
	cmd.Env = os.Environ()

	if c.ArgumentType == "normal" {
		cmd.Args = append(cmd.Args, repo.URL, strconv.Itoa(repo.Revision))
	} else if c.ArgumentType == "env" {
		cmd.Env = append(
			os.Environ(),
			fmt.Sprintf("SVN_URL=%s", repo.URL),
			fmt.Sprintf("SVN_REVISION=%d", repo.Revision),
		)
	} else {
		return fmt.Errorf("invalid argument type \"%s\" for \"%s\"", c.ArgumentType, c.Command)
	}

	if err := cmd.Run(); err != nil {
		return errors.Wrapf(err, "failed to execute \"%s\"", c.Command)
	}

	return nil
}

func (c *Command) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	cmd := struct {
		ArgumentType *string `xml:"argument-type,attr,omitempty"`
		Command      string  `xml:",chardata"`
	}{}

	if err := d.DecodeElement(&cmd, &start); err != nil {
		return err
	}

	if cmd.ArgumentType == nil {
		c.ArgumentType = "normal"
	} else if *cmd.ArgumentType == "normal" || *cmd.ArgumentType == "env" {
		c.ArgumentType = *cmd.ArgumentType
	} else {
		return fmt.Errorf("invalid argument type \"%s\" for \"%s\"", *cmd.ArgumentType, cmd.Command)
	}

	c.Command = cmd.Command

	return nil
}
<|endoftext|>


<commit_before>package cloudwatch

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awsutil"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
)

type actionHandler func(*cwRequest, *middleware.Context)

var actionHandlers map[string]actionHandler

type cwRequest struct {
	Region     string `json:"region"`
	Action     string `json:"action"`
	Body       []byte `json:"-"`
	DataSource *m.DataSource
}

func init() {
	actionHandlers = map[string]actionHandler{
		"GetMetricStatistics":     handleGetMetricStatistics,
		"ListMetrics":             handleListMetrics,
		"DescribeAlarmsForMetric": handleDescribeAlarmsForMetric,
		"DescribeAlarmHistory":    handleDescribeAlarmHistory,
		"DescribeInstances":       handleDescribeInstances,
		"__GetRegions":            handleGetRegions,
		"__GetNamespaces":         handleGetNamespaces,
		"__GetMetrics":            handleGetMetrics,
		"__GetDimensions":         handleGetDimensions,
	}
}

func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) {
	sess := session.New()
	creds := credentials.NewChainCredentials(
		[]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
			&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
		})

	cfg := &aws.Config{
		Region:      aws.String(req.Region),
		Credentials: creds,
	}

	svc := cloudwatch.New(session.New(cfg), cfg)

	reqParam := &struct {
		Parameters struct {
			Namespace  string                  `json:"namespace"`
			MetricName string                  `json:"metricName"`
			Dimensions []*cloudwatch.Dimension `json:"dimensions"`
			Statistics []*string               `json:"statistics"`
			StartTime  int64                   `json:"startTime"`
			EndTime    int64                   `json:"endTime"`
			Period     int64                   `json:"period"`
		} `json:"parameters"`
	}{}
	json.Unmarshal(req.Body, reqParam)

	params := &cloudwatch.GetMetricStatisticsInput{
		Namespace:  aws.String(reqParam.Parameters.Namespace),
		MetricName: aws.String(reqParam.Parameters.MetricName),
		Dimensions: reqParam.Parameters.Dimensions,
		Statistics: reqParam.Parameters.Statistics,
		StartTime:  aws.Time(time.Unix(reqParam.Parameters.StartTime, 0)),
		EndTime:    aws.Time(time.Unix(reqParam.Parameters.EndTime, 0)),
		Period:     aws.Int64(reqParam.Parameters.Period),
	}

	resp, err := svc.GetMetricStatistics(params)
	if err != nil {
		c.JsonApiErr(500, "Unable to call AWS API", err)
		return
	}

	c.JSON(200, resp)
}

func handleListMetrics(req *cwRequest, c *middleware.Context) {
	sess := session.New()
	creds := credentials.NewChainCredentials(
		[]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
			&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
		})

	cfg := &aws.Config{
		Region:      aws.String(req.Region),
		Credentials: creds,
	}

	svc := cloudwatch.New(session.New(cfg), cfg)

	reqParam := &struct {
		Parameters struct {
			Namespace  string                        `json:"namespace"`
			MetricName string                        `json:"metricName"`
			Dimensions []*cloudwatch.DimensionFilter `json:"dimensions"`
		} `json:"parameters"`
	}{}
	json.Unmarshal(req.Body, reqParam)

	params := &cloudwatch.ListMetricsInput{
		Namespace:  aws.String(reqParam.Parameters.Namespace),
		MetricName: aws.String(reqParam.Parameters.MetricName),
		Dimensions: reqParam.Parameters.Dimensions,
	}

	var resp cloudwatch.ListMetricsOutput
	err := svc.ListMetricsPages(params,
		func(page *cloudwatch.ListMetricsOutput, lastPage bool) bool {
			metrics, _ := awsutil.ValuesAtPath(page, "Metrics")
			for _, metric := range metrics {
				resp.Metrics = append(resp.Metrics, metric.(*cloudwatch.Metric))
			}
			return !lastPage
		})
	if err != nil {
		c.JsonApiErr(500, "Unable to call AWS API", err)
		return
	}

	c.JSON(200, resp)
}

func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
	creds := credentials.NewChainCredentials(
		[]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
			&ec2rolecreds.EC2RoleProvider{ExpiryWindow: 5 * time.Minute},
		})

	cfg := &aws.Config{
		Region:      aws.String(req.Region),
		Credentials: creds,
	}

	svc := cloudwatch.New(session.New(cfg), cfg)

	reqParam := &struct {
		Parameters struct {
			Namespace  string                  `json:"namespace"`
			MetricName string                  `json:"metricName"`
			Dimensions []*cloudwatch.Dimension `json:"dimensions"`
			Statistic  string                  `json:"statistic"`
			Period     int64                   `json:"period"`
		} `json:"parameters"`
	}{}
	json.Unmarshal(req.Body, reqParam)

	params := &cloudwatch.DescribeAlarmsForMetricInput{
		Namespace:  aws.String(reqParam.Parameters.Namespace),
		MetricName: aws.String(reqParam.Parameters.MetricName),
		Period:     aws.Int64(reqParam.Parameters.Period),
	}
	if len(reqParam.Parameters.Dimensions) != 0 {
		params.Dimensions = reqParam.Parameters.Dimensions
	}
	if reqParam.Parameters.Statistic != "" {
		params.Statistic = aws.String(reqParam.Parameters.Statistic)
	}

	resp, err := svc.DescribeAlarmsForMetric(params)
	if err != nil {
		c.JsonApiErr(500, "Unable to call AWS API", err)
		return
	}

	c.JSON(200, resp)
}

func handleDescribeAlarmHistory(req *cwRequest, c *middleware.Context) {
	creds := credentials.NewChainCredentials(
		[]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
			&ec2rolecreds.EC2RoleProvider{ExpiryWindow: 5 * time.Minute},
		})

	cfg := &aws.Config{
		Region:      aws.String(req.Region),
		Credentials: creds,
	}

	svc := cloudwatch.New(session.New(cfg), cfg)

	reqParam := &struct {
		Parameters struct {
			AlarmName       string `json:"alarmName"`
			HistoryItemType string `json:"historyItemType"`
			StartDate       int64  `json:"startDate"`
			EndDate         int64  `json:"endDate"`
		} `json:"parameters"`
	}{}
	json.Unmarshal(req.Body, reqParam)

	params := &cloudwatch.DescribeAlarmHistoryInput{
		AlarmName: aws.String(reqParam.Parameters.AlarmName),
		StartDate: aws.Time(time.Unix(reqParam.Parameters.StartDate, 0)),
		EndDate:   aws.Time(time.Unix(reqParam.Parameters.EndDate, 0)),
	}
	if reqParam.Parameters.HistoryItemType != "" {
		params.HistoryItemType = aws.String(reqParam.Parameters.HistoryItemType)
	}

	resp, err := svc.DescribeAlarmHistory(params)
	if err != nil {
		c.JsonApiErr(500, "Unable to call AWS API", err)
		return
	}

	c.JSON(200, resp)
}

func handleDescribeInstances(req *cwRequest, c *middleware.Context) {
	sess := session.New()
	creds := credentials.NewChainCredentials(
		[]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
			&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
		})

	cfg := &aws.Config{
		Region:      aws.String(req.Region),
		Credentials: creds,
	}

	svc := ec2.New(session.New(cfg), cfg)

	reqParam := &struct {
		Parameters struct {
			Filters     []*ec2.Filter `json:"filters"`
			InstanceIds []*string     `json:"instanceIds"`
		} `json:"parameters"`
	}{}
	json.Unmarshal(req.Body, reqParam)

	params := &ec2.DescribeInstancesInput{}
	if len(reqParam.Parameters.Filters) > 0 {
		params.Filters = reqParam.Parameters.Filters
	}
	if len(reqParam.Parameters.InstanceIds) > 0 {
		params.InstanceIds = reqParam.Parameters.InstanceIds
	}

	var resp ec2.DescribeInstancesOutput
	err := svc.DescribeInstancesPages(params,
		func(page *ec2.DescribeInstancesOutput, lastPage bool) bool {
			reservations, _ := awsutil.ValuesAtPath(page, "Reservations")
			for _, reservation := range reservations {
				resp.Reservations = append(resp.Reservations, reservation.(*ec2.Reservation))
			}
			return !lastPage
		})
	if err != nil {
		c.JsonApiErr(500, "Unable to call AWS API", err)
		return
	}

	c.JSON(200, resp)
}

func HandleRequest(c *middleware.Context, ds *m.DataSource) {
	var req cwRequest
	req.Body, _ = ioutil.ReadAll(c.Req.Request.Body)
	req.DataSource = ds
	json.Unmarshal(req.Body, &req)

	if handler, found := actionHandlers[req.Action]; !found {
		c.JsonApiErr(500, "Unexpected AWS Action", errors.New(req.Action))
		return
	} else {
		handler(&req, c)
	}
}
<commit_msg>fix EC2RoleProvider parameter<commit_after>package cloudwatch

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awsutil"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
)

type actionHandler func(*cwRequest, *middleware.Context)

var actionHandlers map[string]actionHandler

type cwRequest struct {
	Region     string `json:"region"`
	Action     string `json:"action"`
	Body       []byte `json:"-"`
	DataSource *m.DataSource
}

func init() {
	actionHandlers = map[string]actionHandler{
		"GetMetricStatistics":     handleGetMetricStatistics,
		"ListMetrics":             handleListMetrics,
		"DescribeAlarmsForMetric": handleDescribeAlarmsForMetric,
		"DescribeAlarmHistory":    handleDescribeAlarmHistory,
		"DescribeInstances":       handleDescribeInstances,
		"__GetRegions":            handleGetRegions,
		"__GetNamespaces":         handleGetNamespaces,
		"__GetMetrics":            handleGetMetrics,
		"__GetDimensions":         handleGetDimensions,
	}
}

func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) {
	sess := session.New()
	creds := credentials.NewChainCredentials(
		[]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
			&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
		})

	cfg := &aws.Config{
		Region:      aws.String(req.Region),
		Credentials: creds,
	}

	svc := cloudwatch.New(session.New(cfg), cfg)

	reqParam := &struct {
		Parameters struct {
			Namespace  string                  `json:"namespace"`
			MetricName string                  `json:"metricName"`
			Dimensions []*cloudwatch.Dimension `json:"dimensions"`
			Statistics []*string               `json:"statistics"`
			StartTime  int64                   `json:"startTime"`
			EndTime    int64                   `json:"endTime"`
			Period     int64                   `json:"period"`
		} `json:"parameters"`
	}{}
	json.Unmarshal(req.Body, reqParam)

	params := &cloudwatch.GetMetricStatisticsInput{
		Namespace:  aws.String(reqParam.Parameters.Namespace),
		MetricName: aws.String(reqParam.Parameters.MetricName),
		Dimensions: reqParam.Parameters.Dimensions,
		Statistics: reqParam.Parameters.Statistics,
		StartTime:  aws.Time(time.Unix(reqParam.Parameters.StartTime, 0)),
		EndTime:    aws.Time(time.Unix(reqParam.Parameters.EndTime, 0)),
		Period:     aws.Int64(reqParam.Parameters.Period),
	}

	resp, err := svc.GetMetricStatistics(params)
	if err != nil {
		c.JsonApiErr(500, "Unable to call AWS API", err)
		return
	}

	c.JSON(200, resp)
}

func handleListMetrics(req *cwRequest, c *middleware.Context) {
	sess := session.New()
	creds := credentials.NewChainCredentials(
		[]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
			&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
		})

	cfg := &aws.Config{
		Region:      aws.String(req.Region),
		Credentials: creds,
	}

	svc := cloudwatch.New(session.New(cfg), cfg)

	reqParam := &struct {
		Parameters struct {
			Namespace  string                        `json:"namespace"`
			MetricName string                        `json:"metricName"`
			Dimensions []*cloudwatch.DimensionFilter `json:"dimensions"`
		} `json:"parameters"`
	}{}
	json.Unmarshal(req.Body, reqParam)

	params := &cloudwatch.ListMetricsInput{
		Namespace:  aws.String(reqParam.Parameters.Namespace),
		MetricName: aws.String(reqParam.Parameters.MetricName),
		Dimensions: reqParam.Parameters.Dimensions,
	}

	var resp cloudwatch.ListMetricsOutput
	err := svc.ListMetricsPages(params,
		func(page *cloudwatch.ListMetricsOutput, lastPage bool) bool {
			metrics, _ := awsutil.ValuesAtPath(page, "Metrics")
			for _, metric := range metrics {
				resp.Metrics = append(resp.Metrics, metric.(*cloudwatch.Metric))
			}
			return !lastPage
		})
	if err != nil {
		c.JsonApiErr(500, "Unable to call AWS API", err)
		return
	}

	c.JSON(200, resp)
}

func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
	sess := session.New()
	creds := credentials.NewChainCredentials(
		[]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
			&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
		})

	cfg := &aws.Config{
		Region:      aws.String(req.Region),
		Credentials: creds,
	}

	svc := cloudwatch.New(session.New(cfg), cfg)

	reqParam := &struct {
		Parameters struct {
			Namespace  string                  `json:"namespace"`
			MetricName string                  `json:"metricName"`
			Dimensions []*cloudwatch.Dimension `json:"dimensions"`
			Statistic  string                  `json:"statistic"`
			Period     int64                   `json:"period"`
		} `json:"parameters"`
	}{}
	json.Unmarshal(req.Body, reqParam)

	params := &cloudwatch.DescribeAlarmsForMetricInput{
		Namespace:  aws.String(reqParam.Parameters.Namespace),
		MetricName: aws.String(reqParam.Parameters.MetricName),
		Period:     aws.Int64(reqParam.Parameters.Period),
	}
	if len(reqParam.Parameters.Dimensions) != 0 {
		params.Dimensions = reqParam.Parameters.Dimensions
	}
	if reqParam.Parameters.Statistic != "" {
		params.Statistic = aws.String(reqParam.Parameters.Statistic)
	}

	resp, err := svc.DescribeAlarmsForMetric(params)
	if err != nil {
		c.JsonApiErr(500, "Unable to call AWS API", err)
		return
	}

	c.JSON(200, resp)
}

func handleDescribeAlarmHistory(req *cwRequest, c *middleware.Context) {
	sess := session.New()
	creds := credentials.NewChainCredentials(
		[]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
			&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
		})

	cfg := &aws.Config{
		Region:      aws.String(req.Region),
		Credentials: creds,
	}

	svc := cloudwatch.New(session.New(cfg), cfg)

	reqParam := &struct {
		Parameters struct {
			AlarmName       string `json:"alarmName"`
			HistoryItemType string `json:"historyItemType"`
			StartDate       int64  `json:"startDate"`
			EndDate         int64  `json:"endDate"`
		} `json:"parameters"`
	}{}
	json.Unmarshal(req.Body, reqParam)

	params := &cloudwatch.DescribeAlarmHistoryInput{
		AlarmName: aws.String(reqParam.Parameters.AlarmName),
		StartDate: aws.Time(time.Unix(reqParam.Parameters.StartDate, 0)),
		EndDate:   aws.Time(time.Unix(reqParam.Parameters.EndDate, 0)),
	}
	if reqParam.Parameters.HistoryItemType != "" {
		params.HistoryItemType = aws.String(reqParam.Parameters.HistoryItemType)
	}

	resp, err := svc.DescribeAlarmHistory(params)
	if err != nil {
		c.JsonApiErr(500, "Unable to call AWS API", err)
		return
	}

	c.JSON(200, resp)
}

func handleDescribeInstances(req *cwRequest, c *middleware.Context) {
	sess := session.New()
	creds := credentials.NewChainCredentials(
		[]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: req.DataSource.Database},
			&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute},
		})

	cfg := &aws.Config{
		Region:      aws.String(req.Region),
		Credentials: creds,
	}

	svc := ec2.New(session.New(cfg), cfg)

	reqParam := &struct {
		Parameters struct {
			Filters     []*ec2.Filter `json:"filters"`
			InstanceIds []*string     `json:"instanceIds"`
		} `json:"parameters"`
	}{}
	json.Unmarshal(req.Body, reqParam)

	params := &ec2.DescribeInstancesInput{}
	if len(reqParam.Parameters.Filters) > 0 {
		params.Filters = reqParam.Parameters.Filters
	}
	if len(reqParam.Parameters.InstanceIds) > 0 {
		params.InstanceIds = reqParam.Parameters.InstanceIds
	}

	var resp ec2.DescribeInstancesOutput
	err := svc.DescribeInstancesPages(params,
		func(page *ec2.DescribeInstancesOutput, lastPage bool) bool {
			reservations, _ := awsutil.ValuesAtPath(page, "Reservations")
			for _, reservation := range reservations {
				resp.Reservations = append(resp.Reservations, reservation.(*ec2.Reservation))
			}
			return !lastPage
		})
	if err != nil {
		c.JsonApiErr(500, "Unable to call AWS API", err)
		return
	}

	c.JSON(200, resp)
}

func HandleRequest(c *middleware.Context, ds *m.DataSource) {
	var req cwRequest
	req.Body, _ = ioutil.ReadAll(c.Req.Request.Body)
	req.DataSource = ds
	json.Unmarshal(req.Body, &req)

	if handler, found := actionHandlers[req.Action]; !found {
		c.JsonApiErr(500, "Unexpected AWS Action", errors.New(req.Action))
		return
	} else {
		handler(&req, c)
	}
}
<|endoftext|>