<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/applicationautoscaling"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSAppautoScalingTarget_basic(t *testing.T) {
	var target applicationautoscaling.ScalableTarget

	randClusterName := fmt.Sprintf("cluster-%s", acctest.RandString(10))

	resource.Test(t, resource.TestCase{
		PreCheck:      func() { testAccPreCheck(t) },
		IDRefreshName: "aws_appautoscaling_target.bar",
		Providers:     testAccProviders,
		CheckDestroy:  testAccCheckAWSAppautoscalingTargetDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSAppautoscalingTargetConfig(randClusterName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSAppautoscalingTargetExists("aws_appautoscaling_target.bar", &target),
					resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "service_namespace", "ecs"),
					resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "scalable_dimension", "ecs:service:DesiredCount"),
					resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "min_capacity", "1"),
					resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "max_capacity", "3"),
				),
			},

			resource.TestStep{
				Config: testAccAWSAppautoscalingTargetConfigUpdate(randClusterName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSAppautoscalingTargetExists("aws_appautoscaling_target.bar", &target),
					resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "min_capacity", "2"),
					resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "max_capacity", "8"),
				),
			},
		},
	})
}

func testAccCheckAWSAppautoscalingTargetDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_appautoscaling_target" {
			continue
		}

		// Try to find the target
		describeTargets, err := conn.DescribeScalableTargets(
			&applicationautoscaling.DescribeScalableTargetsInput{
				ResourceIds:      []*string{aws.String(rs.Primary.ID)},
				ServiceNamespace: aws.String(rs.Primary.Attributes["service_namespace"]),
			},
		)

		if err == nil {
			if len(describeTargets.ScalableTargets) != 0 &&
				*describeTargets.ScalableTargets[0].ResourceId == rs.Primary.ID {
				return fmt.Errorf("Application AutoScaling Target still exists")
			}
		}

		// Verify error
		e, ok := err.(awserr.Error)
		if !ok {
			return err
		}
		if e.Code() != "" {
			return e
		}
	}

	return nil
}

func testAccCheckAWSAppautoscalingTargetExists(n string, target *applicationautoscaling.ScalableTarget) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Application AutoScaling Target ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn

		describeTargets, err := conn.DescribeScalableTargets(
			&applicationautoscaling.DescribeScalableTargetsInput{
				ResourceIds:      []*string{aws.String(rs.Primary.ID)},
				ServiceNamespace: aws.String(rs.Primary.Attributes["service_namespace"]),
			},
		)

		if err != nil {
			return err
		}

		if len(describeTargets.ScalableTargets) != 1 || *describeTargets.ScalableTargets[0].ResourceId != rs.Primary.ID {
			return fmt.Errorf("Application AutoScaling ResourceId not found")
		}

		target = describeTargets.ScalableTargets[0]

		return nil
	}
}

func testAccAWSAppautoscalingTargetConfig(
	randClusterName string) string {
	return fmt.Sprintf(`
resource "aws_iam_role" "autoscale_role" {
	name = "autoscalerole%s"
	path = "/"

	assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "application-autoscaling.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "autoscale_role_policy" {
	name = "autoscalepolicy%s"
	role = "${aws_iam_role.autoscale_role.id}"

	policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecs:DescribeServices",
                "ecs:UpdateService"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:DescribeAlarms"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
EOF
}

resource "aws_ecs_cluster" "foo" {
	name = "%s"
}
resource "aws_ecs_task_definition" "task" {
	family = "foobar"
	container_definitions = <<EOF
[
    {
        "name": "busybox",
        "image": "busybox:latest",
        "cpu": 10,
        "memory": 128,
        "essential": true
    }
]
EOF
}
resource "aws_ecs_service" "service" {
	name = "foobar"
	cluster = "${aws_ecs_cluster.foo.id}"
	task_definition = "${aws_ecs_task_definition.task.arn}"
	desired_count = 1

	deployment_maximum_percent = 200
	deployment_minimum_healthy_percent = 50
}
resource "aws_appautoscaling_target" "bar" {
	service_namespace = "ecs"
	resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
	scalable_dimension = "ecs:service:DesiredCount"
	role_arn = "${aws_iam_role.autoscale_role.arn}"
	min_capacity = 1
	max_capacity = 3
}
`, randClusterName, randClusterName, randClusterName)
}

func testAccAWSAppautoscalingTargetConfigUpdate(
	randClusterName string) string {
	return fmt.Sprintf(`
resource "aws_iam_role" "autoscale_role" {
	name = "autoscalerole%s"
	path = "/"

	assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "application-autoscaling.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "autoscale_role_policy" {
	name = "autoscalepolicy%s"
	role = "${aws_iam_role.autoscale_role.id}"

	policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecs:DescribeServices",
                "ecs:UpdateService"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:DescribeAlarms"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
EOF
}

resource "aws_ecs_cluster" "foo" {
	name = "%s"
}
resource "aws_ecs_task_definition" "task" {
	family = "foobar"
	container_definitions = <<EOF
[
    {
        "name": "busybox",
        "image": "busybox:latest",
        "cpu": 10,
        "memory": 128,
        "essential": true
    }
]
EOF
}
resource "aws_ecs_service" "service" {
	name = "foobar"
	cluster = "${aws_ecs_cluster.foo.id}"
	task_definition = "${aws_ecs_task_definition.task.arn}"
	desired_count = 1

	deployment_maximum_percent = 200
	deployment_minimum_healthy_percent = 50
}
resource "aws_appautoscaling_target" "bar" {
	service_namespace = "ecs"
	resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
	scalable_dimension = "ecs:service:DesiredCount"
	role_arn = "${aws_iam_role.autoscale_role.arn}"
	min_capacity = 2
	max_capacity = 8
}
`, randClusterName, randClusterName, randClusterName)
}
<commit_msg>provider/aws: Update aws_appautoscaling_target_test (#9736)<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/applicationautoscaling"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSAppautoScalingTarget_basic(t *testing.T) {
	var target applicationautoscaling.ScalableTarget

	randClusterName := fmt.Sprintf("cluster-%s", acctest.RandString(10))

	resource.Test(t, resource.TestCase{
		PreCheck:      func() { testAccPreCheck(t) },
		IDRefreshName: "aws_appautoscaling_target.bar",
		Providers:     testAccProviders,
		CheckDestroy:  testAccCheckAWSAppautoscalingTargetDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSAppautoscalingTargetConfig(randClusterName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSAppautoscalingTargetExists("aws_appautoscaling_target.bar", &target),
					resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "service_namespace", "ecs"),
					resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "scalable_dimension", "ecs:service:DesiredCount"),
					resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "min_capacity", "1"),
					resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "max_capacity", "3"),
				),
			},

			resource.TestStep{
				Config: testAccAWSAppautoscalingTargetConfigUpdate(randClusterName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSAppautoscalingTargetExists("aws_appautoscaling_target.bar", &target),
					resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "min_capacity", "2"),
					resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "max_capacity", "8"),
				),
			},
		},
	})
}

func testAccCheckAWSAppautoscalingTargetDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_appautoscaling_target" {
			continue
		}

		// Try to find the target
		describeTargets, err := conn.DescribeScalableTargets(
			&applicationautoscaling.DescribeScalableTargetsInput{
				ResourceIds:      []*string{aws.String(rs.Primary.ID)},
				ServiceNamespace: aws.String(rs.Primary.Attributes["service_namespace"]),
			},
		)

		if err == nil {
			if len(describeTargets.ScalableTargets) != 0 &&
				*describeTargets.ScalableTargets[0].ResourceId == rs.Primary.ID {
				return fmt.Errorf("Application AutoScaling Target still exists")
			}
		}

		// Verify error
		e, ok := err.(awserr.Error)
		if !ok {
			return err
		}
		if e.Code() != "" {
			return e
		}
	}

	return nil
}

func testAccCheckAWSAppautoscalingTargetExists(n string, target *applicationautoscaling.ScalableTarget) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Application AutoScaling Target ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn

		describeTargets, err := conn.DescribeScalableTargets(
			&applicationautoscaling.DescribeScalableTargetsInput{
				ResourceIds:      []*string{aws.String(rs.Primary.ID)},
				ServiceNamespace: aws.String(rs.Primary.Attributes["service_namespace"]),
			},
		)

		if err != nil {
			return err
		}

		if len(describeTargets.ScalableTargets) != 1 || *describeTargets.ScalableTargets[0].ResourceId != rs.Primary.ID {
			return fmt.Errorf("Application AutoScaling ResourceId not found")
		}

		target = describeTargets.ScalableTargets[0]

		return nil
	}
}

func testAccAWSAppautoscalingTargetConfig(
	randClusterName string) string {
	return fmt.Sprintf(`
resource "aws_iam_role" "autoscale_role" {
	name = "autoscalerole%s"
	path = "/"

	assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "application-autoscaling.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "autoscale_role_policy" {
	name = "autoscalepolicy%s"
	role = "${aws_iam_role.autoscale_role.id}"

	policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecs:DescribeServices",
                "ecs:UpdateService"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:DescribeAlarms"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
EOF
}

resource "aws_ecs_cluster" "foo" {
	name = "%s"
}
resource "aws_ecs_task_definition" "task" {
	family = "foobar"
	container_definitions = <<EOF
[
    {
        "name": "busybox",
        "image": "busybox:latest",
        "cpu": 10,
        "memory": 128,
        "essential": true
    }
]
EOF
}
resource "aws_ecs_service" "service" {
	name = "foobar"
	cluster = "${aws_ecs_cluster.foo.id}"
	task_definition = "${aws_ecs_task_definition.task.arn}"
	desired_count = 1

	deployment_maximum_percent = 200
	deployment_minimum_healthy_percent = 50
}
resource "aws_appautoscaling_target" "bar" {
	service_namespace = "ecs"
	resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
	scalable_dimension = "ecs:service:DesiredCount"
	role_arn = "${aws_iam_role.autoscale_role.arn}"
	min_capacity = 1
	max_capacity = 3
}
`, randClusterName, randClusterName, randClusterName)
}

func testAccAWSAppautoscalingTargetConfigUpdate(
	randClusterName string) string {
	return fmt.Sprintf(`
resource "aws_iam_role" "autoscale_role" {
	name = "autoscalerole%s"
	path = "/"

	assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "application-autoscaling.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "autoscale_role_policy" {
	name = "autoscalepolicy%s"
	role = "${aws_iam_role.autoscale_role.id}"

	policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecs:DescribeServices",
                "ecs:UpdateService"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:DescribeAlarms"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
EOF
}

resource "aws_ecs_cluster" "foo" {
	name = "%s"
}
resource "aws_ecs_task_definition" "task" {
	family = "foobar"
	container_definitions = <<EOF
[
    {
        "name": "busybox",
        "image": "busybox:latest",
        "cpu": 10,
        "memory": 128,
        "essential": true
    }
]
EOF
}
resource "aws_ecs_service" "service" {
	name = "foobar"
	cluster = "${aws_ecs_cluster.foo.id}"
	task_definition = "${aws_ecs_task_definition.task.arn}"
	desired_count = 2

	deployment_maximum_percent = 200
	deployment_minimum_healthy_percent = 50
}
resource "aws_appautoscaling_target" "bar" {
	service_namespace = "ecs"
	resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
	scalable_dimension = "ecs:service:DesiredCount"
	role_arn = "${aws_iam_role.autoscale_role.arn}"
	min_capacity = 2
	max_capacity = 8
}
`, randClusterName, randClusterName, randClusterName)
}
<|endoftext|>


<commit_before>package main

import (
	"reflect"
	"testing"

	"veyron2/vdl/test_base"
)

func init() {
	registerInterface((*test_base.ServiceB)(nil))
}

func compareType(t *testing.T, method string, got, want interface{}, argKind string) {
	if gotT, wantT := reflect.TypeOf(got), reflect.TypeOf(want); gotT != wantT {
		t.Errorf("type mismatch in %q's %s argument: got %v , want %v ", method, argKind, gotT, wantT)
	}
}

func compareTypes(t *testing.T, method string, got, want []interface{}, argKind string) {
	if len(got) != len(want) {
		 t.Errorf("mismatch in input arguments: got %v , want %v ", got, want)
	}
	for i, _ := range got {
		compareType(t, method, got[i], want[i], argKind)
	}
}

func TestGetter(t *testing.T) {
	iface := "veyron2/vdl/test_base/ServiceB"
	getter := newArgGetter(iface)
	if getter == nil {
		t.Fatalf("no getter for interface: %v ", iface)
	}
	if got, want := getter.vdlPath, iface; got != want {
		t.Errorf("invalid pathname: got %v , want %v ", got, want)
	}
	data := []struct{
		Method string
		NumInArgs int
		in, out []interface{}
		sSend, sRecv interface{}
		sFinish []interface{}
	}{
		{"MethodA1", 0, nil, nil, nil, nil, nil},
		{"MethodA2", 2, []interface{}{(*int32)(nil), (*string)(nil)}, []interface{}{(*string)(nil)}, nil, nil, nil},
		{"MethodA3", 1, []interface{}{(*int32)(nil)}, nil, nil, (*test_base.Scalars)(nil), []interface{}{(*string)(nil)}},
		{"MethodA4", 1, []interface{}{(*int32)(nil)}, nil, (*int32)(nil), (*string)(nil), nil},
		{"MethodB1", 2, []interface{}{(*test_base.Scalars)(nil), (*test_base.Composites)(nil)}, []interface{}{(*test_base.CompComp)(nil)}, nil, nil, nil},
	}
	for _, d := range data {
		m := getter.FindMethod(d.Method, d.NumInArgs)
		if m == nil {
			t.Errorf("couldn't find method %q with %d args", d.Method, d.NumInArgs)
			continue
		}
		// Compare arguments.
		compareTypes(t, d.Method, m.InPtrs(), d.in, "input")
		compareTypes(t, d.Method, m.OutPtrs(), d.out, "output")
		compareType(t, d.Method, m.StreamSendPtr(), d.sSend, "stream send")
		compareType(t, d.Method, m.StreamRecvPtr(), d.sRecv, "stream recv")
		compareTypes(t, d.Method, m.StreamFinishPtrs(), d.sFinish, "stream finish")
	}
}<commit_msg>veyron/runtimes/google/ipc/jni: running go fmt for Srdjan<commit_after>package main

import (
	"reflect"
	"testing"

	"veyron2/vdl/test_base"
)

func init() {
	registerInterface((*test_base.ServiceB)(nil))
}

func compareType(t *testing.T, method string, got, want interface{}, argKind string) {
	if gotT, wantT := reflect.TypeOf(got), reflect.TypeOf(want); gotT != wantT {
		t.Errorf("type mismatch in %q's %s argument: got %v , want %v ", method, argKind, gotT, wantT)
	}
}

func compareTypes(t *testing.T, method string, got, want []interface{}, argKind string) {
	if len(got) != len(want) {
		t.Errorf("mismatch in input arguments: got %v , want %v ", got, want)
	}
	for i, _ := range got {
		compareType(t, method, got[i], want[i], argKind)
	}
}

func TestGetter(t *testing.T) {
	iface := "veyron2/vdl/test_base/ServiceB"
	getter := newArgGetter(iface)
	if getter == nil {
		t.Fatalf("no getter for interface: %v ", iface)
	}
	if got, want := getter.vdlPath, iface; got != want {
		t.Errorf("invalid pathname: got %v , want %v ", got, want)
	}
	data := []struct {
		Method       string
		NumInArgs    int
		in, out      []interface{}
		sSend, sRecv interface{}
		sFinish      []interface{}
	}{
		{"MethodA1", 0, nil, nil, nil, nil, nil},
		{"MethodA2", 2, []interface{}{(*int32)(nil), (*string)(nil)}, []interface{}{(*string)(nil)}, nil, nil, nil},
		{"MethodA3", 1, []interface{}{(*int32)(nil)}, nil, nil, (*test_base.Scalars)(nil), []interface{}{(*string)(nil)}},
		{"MethodA4", 1, []interface{}{(*int32)(nil)}, nil, (*int32)(nil), (*string)(nil), nil},
		{"MethodB1", 2, []interface{}{(*test_base.Scalars)(nil), (*test_base.Composites)(nil)}, []interface{}{(*test_base.CompComp)(nil)}, nil, nil, nil},
	}
	for _, d := range data {
		m := getter.FindMethod(d.Method, d.NumInArgs)
		if m == nil {
			t.Errorf("couldn't find method %q with %d args", d.Method, d.NumInArgs)
			continue
		}
		// Compare arguments.
		compareTypes(t, d.Method, m.InPtrs(), d.in, "input")
		compareTypes(t, d.Method, m.OutPtrs(), d.out, "output")
		compareType(t, d.Method, m.StreamSendPtr(), d.sSend, "stream send")
		compareType(t, d.Method, m.StreamRecvPtr(), d.sRecv, "stream recv")
		compareTypes(t, d.Method, m.StreamFinishPtrs(), d.sFinish, "stream finish")
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	"github.com/rancher/event-subscriber/events"
	"github.com/rancher/go-rancher/v2"
)

const (
	bootstrapContName   = "rancher-agent-bootstrap"
	maxWait             = time.Duration(time.Second * 10)
	bootstrappedAtField = "bootstrappedAt"
	parseMessage        = "Failed to parse config: [%v]"
	bootStrappedFile    = "bootstrapped"
	fingerprintStart    = "CA_FINGERPRINT="
)

var endpointRegEx = regexp.MustCompile("-H=[[:alnum:]]*[[:graph:]]*")

func ActivateMachine(event *events.Event, apiClient *client.RancherClient) (err error) {
	log.WithFields(log.Fields{
		"resourceId": event.ResourceID,
		"eventId":    event.ID,
	}).Info("Activating Machine")

	machine, err := getMachine(event.ResourceID, apiClient)
	if err != nil {
		return err
	}
	if machine == nil {
		return notAMachineReply(event, apiClient)
	}

	baseMachineDir, err := getBaseMachineDir(machine.ExternalId)
	if err != nil {
		return fmt.Errorf("Unable to get base machine directory. Cannot activate machine %v. Error: %v", machine.Name, err)
	}

	dExists, err := dirExists(baseMachineDir)
	if err != nil {
		return fmt.Errorf("Unable to determine if machine directory exists. Cannot activate machine %v. Error: %v", machine.Name, err)
	}

	if !dExists {
		if ignoreExtractedConfig(machine.Driver) {
			reply := newReply(event)
			return publishReply(reply, apiClient)
		}

		err := reinitFromExtractedConfig(machine, filepath.Dir(baseMachineDir))
		if err != nil {
			return err
		}
	}

	machineDir, err := getMachineDir(machine)
	if err != nil {
		return err
	}

	defer func() {
		if err != nil {
			cleanupResources(machineDir, machine.Name)
		}
	}()

	dataUpdates := map[string]interface{}{}
	eventDataWrapper := map[string]interface{}{"+data": dataUpdates}

	bootstrappedFilePath := filepath.Join(machineDir, "machines", machine.Name, bootStrappedFile)

	// If the resource has the bootstrapped file, then it has been bootstrapped.
	if _, err := os.Stat(bootstrappedFilePath); !os.IsNotExist(err) {
		data, err := ioutil.ReadFile(bootstrappedFilePath)
		if err != nil {
			return fmt.Errorf("Unable to determine if machine was activated: %v. Error: %v", machine.Name, err)
		}
		dataUpdates[bootstrappedAtField] = string(data)
		extractedConfig, extractionErr := getIdempotentExtractedConfig(machine, machineDir, apiClient)
		if extractionErr != nil {
			return fmt.Errorf("Unable to get extracted config. Cannot activate machine %v. Error: %v", machine.Name, err)
		}
		dataUpdates["+fields"] = map[string]interface{}{"extractedConfig": extractedConfig}
		reply := newReply(event)
		reply.Data = eventDataWrapper
		return publishReply(reply, apiClient)
	}

	// Setup republishing timer
	publishChan := make(chan string, 10)
	defer close(publishChan)
	go republishTransitioningReply(publishChan, event, apiClient)

	publishChan <- "Installing Rancher agent"

	registrationURL, imageRepo, imageTag, fingerprint, err := getRegistrationURLAndImage(machine.AccountId, apiClient)
	if err != nil {
		return err
	}

	dockerClient, err := GetDockerClient(machineDir, machine.Name)
	if err != nil {
		return err
	}

	err = pullImage(dockerClient, imageRepo, imageTag)
	if err != nil {
		return err
	}

	publishChan <- "Creating agent container"

	container, err := createContainer(registrationURL, machine, dockerClient, imageRepo, imageTag, fingerprint)
	if err != nil {
		return err
	}
	log.WithFields(log.Fields{
		"resourceId":  event.ResourceID,
		"machineId":   machine.Id,
		"containerId": container.ID,
	}).Info("Container created for machine")

	publishChan <- "Starting agent container"

	err = dockerClient.StartContainer(container.ID, nil)
	if err != nil {
		return err
	}

	log.WithFields(log.Fields{
		"resourceId":        event.ResourceID,
		"machineExternalId": machine.ExternalId,
		"containerId":       container.ID,
	}).Info("Rancher-agent for machine started")

	t := time.Now()
	bootstrappedAt := t.Format(time.RFC3339)
	f, err := os.OpenFile(bootstrappedFilePath, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return err
	}
	f.WriteString(bootstrappedAt)
	f.Close()
	dataUpdates[bootstrappedAtField] = bootstrappedAt

	destFile, err := createExtractedConfig(event, machine)
	if err != nil {
		return err
	}

	if destFile != "" {
		publishChan <- "Saving Machine Config"
		extractedConf, err := getExtractedConfig(destFile, machine, apiClient)
		if err != nil {
			return err
		}
		dataUpdates["+fields"] = map[string]string{"extractedConfig": extractedConf}
	}

	reply := newReply(event)
	reply.Data = eventDataWrapper
	return publishReply(reply, apiClient)
}

func createContainer(registrationURL string, machine *client.Machine,
	dockerClient *docker.Client, imageRepo, imageTag, fingerprint string) (*docker.Container, error) {
	containerCmd := []string{registrationURL}
	containerConfig := buildContainerConfig(containerCmd, machine, imageRepo, imageTag, fingerprint)
	hostConfig := buildHostConfig()

	opts := docker.CreateContainerOptions{
		Name:       bootstrapContName,
		Config:     containerConfig,
		HostConfig: hostConfig}

	return dockerClient.CreateContainer(opts)
}

func buildHostConfig() *docker.HostConfig {
	bindConfig := []string{
		"/var/run/docker.sock:/var/run/docker.sock",
		"/var/lib/rancher:/var/lib/rancher",
	}
	hostConfig := &docker.HostConfig{
		Privileged: true,
		Binds:      bindConfig,
	}
	return hostConfig
}

func buildContainerConfig(containerCmd []string, machine *client.Machine, imgRepo, imgTag, fingerprint string) *docker.Config {
	image := imgRepo + ":" + imgTag

	volConfig := map[string]struct{}{
		"/var/run/docker.sock": {},
		"/var/lib/rancher":     {},
	}
	envVars := []string{"CATTLE_PHYSICAL_HOST_UUID=" + machine.ExternalId,
		"CATTLE_DOCKER_UUID=" + machine.ExternalId}
	labelVars := []string{}
	for key, value := range machine.Labels {
		label := ""
		switch value.(type) {
		case string:
			label = value.(string)
		default:
			continue
		}
		labelPair := key + "=" + label
		labelVars = append(labelVars, labelPair)
	}
	if len(labelVars) > 0 {
		labelVarsString := strings.Join(labelVars, "&")
		labelVarsString = "CATTLE_HOST_LABELS=" + labelVarsString
		envVars = append(envVars, labelVarsString)
	}
	if fingerprint != "" {
		envVars = append(envVars, strings.Replace(fingerprint, "\"", "", -1))
	}
	config := &docker.Config{
		AttachStdin: true,
		Tty:         true,
		Image:       image,
		Volumes:     volConfig,
		Cmd:         containerCmd,
		Env:         envVars,
	}
	return config
}

func pullImage(dockerClient *docker.Client, imageRepo, imageTag string) error {
	imageOptions := docker.PullImageOptions{
		Repository: imageRepo,
		Tag:        imageTag,
	}
	imageAuth := docker.AuthConfiguration{}
	log.Printf("pulling %v:%v image.", imageRepo, imageTag)
	err := dockerClient.PullImage(imageOptions, imageAuth)
	if err != nil {
		return err
	}
	return nil
}

var getRegistrationURLAndImage = func(accountID string, apiClient *client.RancherClient) (string, string, string, string, error) {
	listOpts := client.NewListOpts()
	listOpts.Filters["accountId"] = accountID
	listOpts.Filters["state"] = "active"
	tokenCollection, err := apiClient.RegistrationToken.List(listOpts)
	if err != nil {
		return "", "", "", "", err
	}

	var token client.RegistrationToken
	if len(tokenCollection.Data) >= 1 {
		log.WithFields(log.Fields{
			"accountId": accountID,
		}).Debug("Found token for account")
		token = tokenCollection.Data[0]
	} else {
		log.WithFields(log.Fields{
			"accountId": accountID,
		}).Debug("Creating new token for account")
		createToken := &client.RegistrationToken{
			AccountId: accountID,
		}

		createToken, err = apiClient.RegistrationToken.Create(createToken)
		if err != nil {
			return "", "", "", "", err
		}
		createToken, err = waitForTokenToActivate(createToken, apiClient)
		if err != nil {
			return "", "", "", "", err
		}
		token = *createToken
	}

	regURL, ok := token.Links["registrationUrl"]
	if !ok {
		return "", "", "", "", fmt.Errorf("no registration url on token [%v] for account [%v]", token.Id, accountID)
	}

	imageParts := strings.Split(token.Image, ":")
	if len(imageParts) != 2 {
		return "", "", "", "", fmt.Errorf("invalid Image format in token [%v] for account [%v]", token.Id, accountID)
	}

	regURL = tweakRegistrationURL(regURL)

	return regURL, imageParts[0], imageParts[1], parseFingerprint(token), nil
}

func parseFingerprint(token client.RegistrationToken) string {
	for _, part := range strings.Fields(token.Command) {
		if strings.HasPrefix(part, fingerprintStart) {
			return part
		}
	}
	return ""
}

func tweakRegistrationURL(regURL string) string {
	// We do this to accomodate end-to-end workflow in our local development environments.
	// Containers running in a vm won't be able to reach an api running on "localhost"
	// because typically that localhost is referring to the real computer, not the vm.
	localHostReplace := os.Getenv("CATTLE_AGENT_LOCALHOST_REPLACE")
	if localHostReplace == "" {
		return regURL
	}

	regURL = strings.Replace(regURL, "localhost", localHostReplace, 1)
	return regURL
}

func waitForTokenToActivate(token *client.RegistrationToken,
	apiClient *client.RancherClient) (*client.RegistrationToken, error) {
	timeoutAt := time.Now().Add(maxWait)
	ticker := time.NewTicker(time.Millisecond * 250)
	defer ticker.Stop()
	tokenID := token.Id
	for t := range ticker.C {
		token, err := apiClient.RegistrationToken.ById(tokenID)
		if err != nil {
			return nil, err
		}
		if token == nil {
			return nil, fmt.Errorf("couldn't find token %v", tokenID)
		}
		if token.State == "active" {
			return token, nil
		}
		if t.After(timeoutAt) {
			return nil, fmt.Errorf("timed out waiting for token to activate")
		}
	}
	return nil, fmt.Errorf("Couldn't get active token")
}

type tlsConnectionConfig struct {
	endpoint string
	cert     string
	key      string
	caCert   string
}

// GetDockerClient Returns a TLS-enabled docker client for the specified machine.
func GetDockerClient(machineDir string, machineName string) (*docker.Client, error) {
	conf, err := getConnectionConfig(machineDir, machineName)
	if err != nil {
		return nil, fmt.Errorf("Error getting connection cofig: %v", err)
	}

	client, err := docker.NewTLSClient(conf.endpoint, conf.cert, conf.key, conf.caCert)
	if err != nil {
		return nil, fmt.Errorf("Error getting docker client: %v", err)
	}
	return client, nil
}

func getConnectionConfig(machineDir string, machineName string) (*tlsConnectionConfig, error) {
	command := buildCommand(machineDir, []string{"config", machineName})
	output, err := command.Output()
	if err != nil {
		return nil, err
	}
	args := string(bytes.TrimSpace(output))

	connConfig, err := parseConnectionArgs(args)
	if err != nil {
		return nil, err
	}

	return connConfig, nil
}

func parseConnectionArgs(args string) (*tlsConnectionConfig, error) {
	// Extract the -H (host) parameter
	endpointMatches := endpointRegEx.FindAllString(args, -1)
	if len(endpointMatches) != 1 {
		return nil, fmt.Errorf(parseMessage, args)
	}
	endpointKV := strings.Split(endpointMatches[0], "=")
	if len(endpointKV) != 2 {
		return nil, fmt.Errorf(parseMessage, args)
	}
	endpoint := strings.Replace(endpointKV[1], "\"", "", -1)
	config := &tlsConnectionConfig{endpoint: endpoint}
	args = endpointRegEx.ReplaceAllString(args, "")

	// Extract the tls args: tlscacert tlscert tlskey
	whitespaceSplit := regexp.MustCompile("\\w*--")
	tlsArgs := whitespaceSplit.Split(args, -1)
	for _, arg := range tlsArgs {
		kv := strings.Split(arg, "=")
		if len(kv) == 2 {
			key := strings.TrimSpace(kv[0])
			val := strings.Trim(strings.TrimSpace(kv[1]), "\" ")
			switch key {
			case "tlscacert":
				config.caCert = val
			case "tlscert":
				config.cert = val
			case "tlskey":
				config.key = val
			}
		}
	}

	return config, nil
}
<commit_msg>image fix<commit_after>package handlers

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/distribution/reference"
	"github.com/fsouza/go-dockerclient"
	"github.com/rancher/event-subscriber/events"
	"github.com/rancher/go-rancher/v2"
)

const (
	bootstrapContName   = "rancher-agent-bootstrap"
	maxWait             = time.Duration(time.Second * 10)
	bootstrappedAtField = "bootstrappedAt"
	parseMessage        = "Failed to parse config: [%v]"
	bootStrappedFile    = "bootstrapped"
	fingerprintStart    = "CA_FINGERPRINT="
)

var endpointRegEx = regexp.MustCompile("-H=[[:alnum:]]*[[:graph:]]*")

func ActivateMachine(event *events.Event, apiClient *client.RancherClient) (err error) {
	log.WithFields(log.Fields{
		"resourceId": event.ResourceID,
		"eventId":    event.ID,
	}).Info("Activating Machine")

	machine, err := getMachine(event.ResourceID, apiClient)
	if err != nil {
		return err
	}
	if machine == nil {
		return notAMachineReply(event, apiClient)
	}

	baseMachineDir, err := getBaseMachineDir(machine.ExternalId)
	if err != nil {
		return fmt.Errorf("Unable to get base machine directory. Cannot activate machine %v. Error: %v", machine.Name, err)
	}

	dExists, err := dirExists(baseMachineDir)
	if err != nil {
		return fmt.Errorf("Unable to determine if machine directory exists. Cannot activate machine %v. Error: %v", machine.Name, err)
	}

	if !dExists {
		if ignoreExtractedConfig(machine.Driver) {
			reply := newReply(event)
			return publishReply(reply, apiClient)
		}

		err := reinitFromExtractedConfig(machine, filepath.Dir(baseMachineDir))
		if err != nil {
			return err
		}
	}

	machineDir, err := getMachineDir(machine)
	if err != nil {
		return err
	}

	defer func() {
		if err != nil {
			cleanupResources(machineDir, machine.Name)
		}
	}()

	dataUpdates := map[string]interface{}{}
	eventDataWrapper := map[string]interface{}{"+data": dataUpdates}

	bootstrappedFilePath := filepath.Join(machineDir, "machines", machine.Name, bootStrappedFile)

	// If the resource has the bootstrapped file, then it has been bootstrapped.
	if _, err := os.Stat(bootstrappedFilePath); !os.IsNotExist(err) {
		data, err := ioutil.ReadFile(bootstrappedFilePath)
		if err != nil {
			return fmt.Errorf("Unable to determine if machine was activated: %v. Error: %v", machine.Name, err)
		}
		dataUpdates[bootstrappedAtField] = string(data)
		extractedConfig, extractionErr := getIdempotentExtractedConfig(machine, machineDir, apiClient)
		if extractionErr != nil {
			return fmt.Errorf("Unable to get extracted config. Cannot activate machine %v. Error: %v", machine.Name, err)
		}
		dataUpdates["+fields"] = map[string]interface{}{"extractedConfig": extractedConfig}
		reply := newReply(event)
		reply.Data = eventDataWrapper
		return publishReply(reply, apiClient)
	}

	// Setup republishing timer
	publishChan := make(chan string, 10)
	defer close(publishChan)
	go republishTransitioningReply(publishChan, event, apiClient)

	publishChan <- "Installing Rancher agent"

	registrationURL, imageRepo, imageTag, fingerprint, err := getRegistrationURLAndImage(machine.AccountId, apiClient)
	if err != nil {
		return err
	}

	dockerClient, err := GetDockerClient(machineDir, machine.Name)
	if err != nil {
		return err
	}

	err = pullImage(dockerClient, imageRepo, imageTag)
	if err != nil {
		return err
	}

	publishChan <- "Creating agent container"

	container, err := createContainer(registrationURL, machine, dockerClient, imageRepo, imageTag, fingerprint)
	if err != nil {
		return err
	}
	log.WithFields(log.Fields{
		"resourceId":  event.ResourceID,
		"machineId":   machine.Id,
		"containerId": container.ID,
	}).Info("Container created for machine")

	publishChan <- "Starting agent container"

	err = dockerClient.StartContainer(container.ID, nil)
	if err != nil {
		return err
	}

	log.WithFields(log.Fields{
		"resourceId":        event.ResourceID,
		"machineExternalId": machine.ExternalId,
		"containerId":       container.ID,
	}).Info("Rancher-agent for machine started")

	t := time.Now()
	bootstrappedAt := t.Format(time.RFC3339)
	f, err := os.OpenFile(bootstrappedFilePath, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return err
	}
	f.WriteString(bootstrappedAt)
	f.Close()
	dataUpdates[bootstrappedAtField] = bootstrappedAt

	destFile, err := createExtractedConfig(event, machine)
	if err != nil {
		return err
	}

	if destFile != "" {
		publishChan <- "Saving Machine Config"
		extractedConf, err := getExtractedConfig(destFile, machine, apiClient)
		if err != nil {
			return err
		}
		dataUpdates["+fields"] = map[string]string{"extractedConfig": extractedConf}
	}

	reply := newReply(event)
	reply.Data = eventDataWrapper
	return publishReply(reply, apiClient)
}

func createContainer(registrationURL string, machine *client.Machine,
	dockerClient *docker.Client, imageRepo, imageTag, fingerprint string) (*docker.Container, error) {
	containerCmd := []string{registrationURL}
	containerConfig := buildContainerConfig(containerCmd, machine, imageRepo, imageTag, fingerprint)
	hostConfig := buildHostConfig()

	opts := docker.CreateContainerOptions{
		Name:       bootstrapContName,
		Config:     containerConfig,
		HostConfig: hostConfig}

	return dockerClient.CreateContainer(opts)
}

func buildHostConfig() *docker.HostConfig {
	bindConfig := []string{
		"/var/run/docker.sock:/var/run/docker.sock",
		"/var/lib/rancher:/var/lib/rancher",
	}
	hostConfig := &docker.HostConfig{
		Privileged: true,
		Binds:      bindConfig,
	}
	return hostConfig
}

func buildContainerConfig(containerCmd []string, machine *client.Machine, imgRepo, imgTag, fingerprint string) *docker.Config {
	image := imgRepo + ":" + imgTag

	volConfig := map[string]struct{}{
		"/var/run/docker.sock": {},
		"/var/lib/rancher":     {},
	}
	envVars := []string{"CATTLE_PHYSICAL_HOST_UUID=" + machine.ExternalId,
		"CATTLE_DOCKER_UUID=" + machine.ExternalId}
	labelVars := []string{}
	for key, value := range machine.Labels {
		label := ""
		switch value.(type) {
		case string:
			label = value.(string)
		default:
			continue
		}
		labelPair := key + "=" + label
		labelVars = append(labelVars, labelPair)
	}
	if len(labelVars) > 0 {
		labelVarsString := strings.Join(labelVars, "&")
		labelVarsString = "CATTLE_HOST_LABELS=" + labelVarsString
		envVars = append(envVars, labelVarsString)
	}
	if fingerprint != "" {
		envVars = append(envVars, strings.Replace(fingerprint, "\"", "", -1))
	}
	config := &docker.Config{
		AttachStdin: true,
		Tty:         true,
		Image:       image,
		Volumes:     volConfig,
		Cmd:         containerCmd,
		Env:         envVars,
	}
	return config
}

func pullImage(dockerClient *docker.Client, imageRepo, imageTag string) error {
	imageOptions := docker.PullImageOptions{
		Repository: imageRepo,
		Tag:        imageTag,
	}
	imageAuth := docker.AuthConfiguration{}
	log.Printf("pulling %v:%v image.", imageRepo, imageTag)
	err := dockerClient.PullImage(imageOptions, imageAuth)
	if err != nil {
		return err
	}
	return nil
}

var getRegistrationURLAndImage = func(accountID string, apiClient *client.RancherClient) (string, string, string, string, error) {
	listOpts := client.NewListOpts()
	listOpts.Filters["accountId"] = accountID
	listOpts.Filters["state"] = "active"
	tokenCollection, err := apiClient.RegistrationToken.List(listOpts)
	if err != nil {
		return "", "", "", "", err
	}

	var token client.RegistrationToken
	if len(tokenCollection.Data) >= 1 {
		log.WithFields(log.Fields{
			"accountId": accountID,
		}).Debug("Found token for account")
		token = tokenCollection.Data[0]
	} else {
		log.WithFields(log.Fields{
			"accountId": accountID,
		}).Debug("Creating new token for account")
		createToken := &client.RegistrationToken{
			AccountId: accountID,
		}

		createToken, err = apiClient.RegistrationToken.Create(createToken)
		if err != nil {
			return "", "", "", "", err
		}
		createToken, err = waitForTokenToActivate(createToken, apiClient)
		if err != nil {
			return "", "", "", "", err
		}
		token = *createToken
	}

	regURL, ok := token.Links["registrationUrl"]
	if !ok {
		return "", "", "", "", fmt.Errorf("no registration url on token [%v] for account [%v]", token.Id, accountID)
	}

	repo, tag, err := parseImage(token.Image)
	if err != nil {
		return "", "", "", "", fmt.Errorf("invalid Image format in token [%v] for account [%v]", token.Id, accountID)
	}

	regURL = tweakRegistrationURL(regURL)

	return regURL, repo, tag, parseFingerprint(token), nil
}

func parseFingerprint(token client.RegistrationToken) string {
	for _, part := range strings.Fields(token.Command) {
		if strings.HasPrefix(part, fingerprintStart) {
			return part
		}
	}
	return ""
}

func tweakRegistrationURL(regURL string) string {
	// We do this to accomodate end-to-end workflow in our local development environments.
	// Containers running in a vm won't be able to reach an api running on "localhost"
	// because typically that localhost is referring to the real computer, not the vm.
	localHostReplace := os.Getenv("CATTLE_AGENT_LOCALHOST_REPLACE")
	if localHostReplace == "" {
		return regURL
	}

	regURL = strings.Replace(regURL, "localhost", localHostReplace, 1)
	return regURL
}

func waitForTokenToActivate(token *client.RegistrationToken,
	apiClient *client.RancherClient) (*client.RegistrationToken, error) {
	timeoutAt := time.Now().Add(maxWait)
	ticker := time.NewTicker(time.Millisecond * 250)
	defer ticker.Stop()
	tokenID := token.Id
	for t := range ticker.C {
		token, err := apiClient.RegistrationToken.ById(tokenID)
		if err != nil {
			return nil, err
		}
		if token == nil {
			return nil, fmt.Errorf("couldn't find token %v", tokenID)
		}
		if token.State == "active" {
			return token, nil
		}
		if t.After(timeoutAt) {
			return nil, fmt.Errorf("timed out waiting for token to activate")
		}
	}
	return nil, fmt.Errorf("Couldn't get active token")
}

type tlsConnectionConfig struct {
	endpoint string
	cert     string
	key      string
	caCert   string
}

// GetDockerClient Returns a TLS-enabled docker client for the specified machine.
func GetDockerClient(machineDir string, machineName string) (*docker.Client, error) {
	conf, err := getConnectionConfig(machineDir, machineName)
	if err != nil {
		return nil, fmt.Errorf("Error getting connection cofig: %v", err)
	}

	client, err := docker.NewTLSClient(conf.endpoint, conf.cert, conf.key, conf.caCert)
	if err != nil {
		return nil, fmt.Errorf("Error getting docker client: %v", err)
	}
	return client, nil
}

func getConnectionConfig(machineDir string, machineName string) (*tlsConnectionConfig, error) {
	command := buildCommand(machineDir, []string{"config", machineName})
	output, err := command.Output()
	if err != nil {
		return nil, err
	}
	args := string(bytes.TrimSpace(output))

	connConfig, err := parseConnectionArgs(args)
	if err != nil {
		return nil, err
	}

	return connConfig, nil
}

func parseConnectionArgs(args string) (*tlsConnectionConfig, error) {
	// Extract the -H (host) parameter
	endpointMatches := endpointRegEx.FindAllString(args, -1)
	if len(endpointMatches) != 1 {
		return nil, fmt.Errorf(parseMessage, args)
	}
	endpointKV := strings.Split(endpointMatches[0], "=")
	if len(endpointKV) != 2 {
		return nil, fmt.Errorf(parseMessage, args)
	}
	endpoint := strings.Replace(endpointKV[1], "\"", "", -1)
	config := &tlsConnectionConfig{endpoint: endpoint}
	args = endpointRegEx.ReplaceAllString(args, "")

	// Extract the tls args: tlscacert tlscert tlskey
	whitespaceSplit := regexp.MustCompile("\\w*--")
	tlsArgs := whitespaceSplit.Split(args, -1)
	for _, arg := range tlsArgs {
		kv := strings.Split(arg, "=")
		if len(kv) == 2 {
			key := strings.TrimSpace(kv[0])
			val := strings.Trim(strings.TrimSpace(kv[1]), "\" ")
			switch key {
			case "tlscacert":
				config.caCert = val
			case "tlscert":
				config.cert = val
			case "tlskey":
				config.key = val
			}
		}
	}

	return config, nil
}

func parseImage(image string) (string, string, error) {
	ref, err := reference.Parse(image)
	if err != nil {
		return "", "", err
	}
	repo, tag := "", ""
	if named, ok := ref.(reference.Named); ok {
		repo = named.Name()
	}
	if tagged, ok := ref.(reference.Tagged); ok {
		tag = tagged.Tag()
	}
	return repo, tag, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//
// VMDK Docker driver sanity tests.
//

package main

import (
	"flag"
	"fmt"
	"strconv"
	"testing"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/container"
	"github.com/docker/engine-api/types/filters"
	"github.com/docker/engine-api/types/strslice"
	"github.com/vmware/docker-volume-vsphere/vmdk_plugin/utils/config"
	"golang.org/x/net/context"
)

const (
	defaultMountLocation = "/mnt/testvol"
	// tests are often run under regular account and have no access to /var/log
	defaultTestLogPath = "/tmp/test-docker-volume-vsphere.log"
	// Number of volumes per client for parallel tests
	parallelVolumes = 5
)

var (
	// flag vars - see init() for help
	endPoint1        string
	endPoint2        string
	volumeName       string
	removeContainers bool
)

// prepares the environment. Kind of "main"-ish code for tests.
// Parses flags and inits logs and mount ref counters (the latter waits on Docker
// actuallu replyimg). As any other init(), it is called somewhere during init phase
// so do not expect ALL inits from other tests (if any) to compete by now.
func init() {
	logLevel := flag.String("log_level", "debug", "Logging Level")
	logFile := flag.String("log_file", config.DefaultLogPath, "Log file path")
	configFile := flag.String("config", config.DefaultConfigPath, "Configuration file path")

	flag.StringVar(&endPoint1, "H1", dockerUSocket, "Endpoint (Host1) to connect to")
	flag.StringVar(&endPoint2, "H2", dockerUSocket, "Endpoint (Host2) to connect to")
	flag.StringVar(&volumeName, "v", "TestVol", "Volume name to use in sanity tests")
	flag.BoolVar(&removeContainers, "rm", true, "rm container after run")
	flag.StringVar(&driverName, "d", "vmdk", "Driver name. We refcount volumes on this driver")
	flag.Parse()

	usingConfigFileDefaults := logInit(logLevel, logFile, configFile)

	defaultHeaders = map[string]string{"User-Agent": "engine-api-client-1.0"}

	log.WithFields(log.Fields{
		"driver":                   driverName,
		"log_level":                *logLevel,
		"log_file":                 *logFile,
		"conf_file":                *configFile,
		"using_conf_file_defaults": usingConfigFileDefaults,
	}).Info("VMDK plugin tests started ")
}

// returns in-container mount point for a volume
func getMountpoint(vol string) string {
	return defaultMountLocation + "/" + vol
}

// runs a command in a container , with volume mounted
// returns completion code.
// exits (t.Fatal() or create/start/wait errors
func runContainerCmd(t *testing.T, client *client.Client, volumeName string,
	image string, cmd *strslice.StrSlice, addr string) int {

	mountPoint := getMountpoint(volumeName)
	bind := volumeName + ":" + mountPoint
	t.Logf("Running cmd=%v with vol=%s on client %s", cmd, volumeName, addr)

	r, err := client.ContainerCreate(context.Background(),
		&container.Config{Image: image, Cmd: *cmd,
			Volumes: map[string]struct{}{mountPoint: {}}},
		&container.HostConfig{Binds: []string{bind}}, nil, "")
	if err != nil {
		t.Fatalf("\tContainer create failed: %v", err)
	}

	err = client.ContainerStart(context.Background(), r.ID,
		types.ContainerStartOptions{})
	if err != nil {
		t.Fatalf("\tContainer start failed: id=%s, err %v", r.ID, err)
	}

	code, err := client.ContainerWait(context.Background(), r.ID)
	if err != nil {
		t.Fatalf("\tContainer wait failed: id=%s, err %v", r.ID, err)
	}

	if removeContainers == false {
		t.Logf("\tSkipping container removal, id=%s (removeContainers == false)",
			r.ID)
		return code
	}

	err = client.ContainerRemove(context.Background(), r.ID,
		types.ContainerRemoveOptions{
			RemoveVolumes: true,
			Force:         true,
		})
	if err != nil {
		t.Fatalf("\nContainer removal failed: %v", err)
	}

	return code
}

// Checks that we can touch a file in one container and then stat it
// in another container, using the same (vmdk-based) volume
//
// goes over 'cases' and runs commands, then checks expected return code
func checkTouch(t *testing.T, c *client.Client, vol string,
	file string, addr string) {

	cases := []struct {
		image    string             // Container image to use
		cmd      *strslice.StrSlice // Command to run under busybox
		expected int                // expected results
	}{
		{"busybox", &strslice.StrSlice{"touch", getMountpoint(vol) + "/" + file}, 0},
		{"busybox", &strslice.StrSlice{"stat", getMountpoint(vol) + "/" + file}, 0},
	}

	for _, i := range cases {
		code := runContainerCmd(t, c, vol, i.image, i.cmd, addr)
		if code != i.expected {
			t.Errorf("Expected  %d, got %d (cmd: %v)", i.expected, code, i.cmd)
		}
	}
}

// returns nil for NOT_FOUND and  if volume exists
// still fails the test if driver for this volume is not vmdk
func volumeVmdkExists(t *testing.T, c *client.Client, vol string) *types.Volume {
	reply, err := c.VolumeList(context.Background(), filters.Args{})
	if err != nil {
		t.Fatalf("Failed to enumerate  volumes: %v", err)
	}

	for _, v := range reply.Volumes {
		//	t.Log(v.Name, v.Driver, v.Mountpoint)
		if v.Name == vol {
			return v
		}
	}
	return nil
}

// Sanity test for VMDK volumes
// - check we can attach/detach correct volume (we use 'touch' and 'stat' to validate
// - check volumes are correctly created and deleted.
// - check we see it properly from another docker VM (-H2 flag)
func TestSanity(t *testing.T) {

	fmt.Printf("Running tests on  %s (may take a while)...\n", endPoint1)
	clients := []struct {
		endPoint string
		client   *client.Client
	}{
		{endPoint1, new(client.Client)},
		{endPoint2, new(client.Client)},
	}

	for idx, elem := range clients {
		c, err := client.NewClient(elem.endPoint, apiVersion, nil, defaultHeaders)
		if err != nil {
			t.Fatalf("Failed to connect to %s, err: %v", elem.endPoint, err)
		}
		t.Logf("Successfully connected to %s", elem.endPoint)
		clients[idx].client = c
	}

	c := clients[0].client // this is the endpoint we use as master
	t.Logf("Creating vol=%s on client %s.", volumeName, clients[0].endPoint)
	_, err := c.VolumeCreate(context.Background(),
		types.VolumeCreateRequest{
			Name:   volumeName,
			Driver: driverName,
			DriverOpts: map[string]string{
				"size": "1gb",
			},
		})
	if err != nil {
		t.Fatal(err)
	}

	checkTouch(t, c, volumeName, "file_to_touch", clients[0].endPoint)

	for _, elem := range clients {
		v := volumeVmdkExists(t, elem.client, volumeName)
		if v == nil {
			t.Fatalf("Volume=%s is missing on %s after create",
				volumeName, elem.endPoint)
		}
		if v.Driver != driverName {
			t.Fatalf("wrong driver (%s) for volume %s", v.Driver, v.Name)
		}
	}

	err = c.VolumeRemove(context.Background(), volumeName)
	if err != nil {
		t.Fatalf("Failed to delete volume, err: %v", err)
	}

	for _, elem := range clients {
		if volumeVmdkExists(t, elem.client, volumeName) != nil {
			t.Errorf("Volume=%s is still present on %s after removal",
				volumeName, elem.endPoint)
		}
	}

	fmt.Printf("Running parallel tests on %s and %s (may take a while)...\n", endPoint1, endPoint2)
	// Create a short buffered channel to introduce random pauses
	results := make(chan error, 5)
	createRequest := types.VolumeCreateRequest{
		Name:   volumeName,
		Driver: driverName,
		DriverOpts: map[string]string{
			"size": "1gb",
		},
	}
	// Create/delete routine
	for idx, elem := range clients {
		go func(idx int, c *client.Client) {
			for i := 0; i < parallelVolumes; i++ {
				volName := "volTestP" + strconv.Itoa(idx) + strconv.Itoa(i)
				createRequest.Name = volName
				_, err := c.VolumeCreate(context.Background(), createRequest)
				results <- err
				err = c.VolumeRemove(context.Background(), volName)
				results <- err
			}
		}(idx, elem.client)
	}
	// We need to read #clients * #volumes * 2 operations from the channel
	for i := 0; i < len(clients)*parallelVolumes*2; i++ {
		err := <-results
		if err != nil {
			t.Fatalf("Parallel test failed, err: %v", err)
		}
	}
}
<commit_msg>Increase the amount of parallelism in sanity_test.go<commit_after>// Copyright 2016 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//
// VMDK Docker driver sanity tests.
//

package main

import (
	"flag"
	"fmt"
	"strconv"
	"testing"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/container"
	"github.com/docker/engine-api/types/filters"
	"github.com/docker/engine-api/types/strslice"
	"github.com/vmware/docker-volume-vsphere/vmdk_plugin/utils/config"
	"golang.org/x/net/context"
)

const (
	defaultMountLocation = "/mnt/testvol"
	// tests are often run under regular account and have no access to /var/log
	defaultTestLogPath = "/tmp/test-docker-volume-vsphere.log"
	// Number of volumes per client for parallel tests
	parallelVolumes = 9
)

var (
	// flag vars - see init() for help
	endPoint1        string
	endPoint2        string
	volumeName       string
	removeContainers bool
)

// prepares the environment. Kind of "main"-ish code for tests.
// Parses flags and inits logs and mount ref counters (the latter waits on Docker
// actuallu replyimg). As any other init(), it is called somewhere during init phase
// so do not expect ALL inits from other tests (if any) to compete by now.
func init() {
	logLevel := flag.String("log_level", "debug", "Logging Level")
	logFile := flag.String("log_file", config.DefaultLogPath, "Log file path")
	configFile := flag.String("config", config.DefaultConfigPath, "Configuration file path")

	flag.StringVar(&endPoint1, "H1", dockerUSocket, "Endpoint (Host1) to connect to")
	flag.StringVar(&endPoint2, "H2", dockerUSocket, "Endpoint (Host2) to connect to")
	flag.StringVar(&volumeName, "v", "TestVol", "Volume name to use in sanity tests")
	flag.BoolVar(&removeContainers, "rm", true, "rm container after run")
	flag.StringVar(&driverName, "d", "vmdk", "Driver name. We refcount volumes on this driver")
	flag.Parse()

	usingConfigFileDefaults := logInit(logLevel, logFile, configFile)

	defaultHeaders = map[string]string{"User-Agent": "engine-api-client-1.0"}

	log.WithFields(log.Fields{
		"driver":                   driverName,
		"log_level":                *logLevel,
		"log_file":                 *logFile,
		"conf_file":                *configFile,
		"using_conf_file_defaults": usingConfigFileDefaults,
	}).Info("VMDK plugin tests started ")
}

// returns in-container mount point for a volume
func getMountpoint(vol string) string {
	return defaultMountLocation + "/" + vol
}

// runs a command in a container , with volume mounted
// returns completion code.
// exits (t.Fatal() or create/start/wait errors
func runContainerCmd(t *testing.T, client *client.Client, volumeName string,
	image string, cmd *strslice.StrSlice, addr string) int {

	mountPoint := getMountpoint(volumeName)
	bind := volumeName + ":" + mountPoint
	t.Logf("Running cmd=%v with vol=%s on client %s", cmd, volumeName, addr)

	r, err := client.ContainerCreate(context.Background(),
		&container.Config{Image: image, Cmd: *cmd,
			Volumes: map[string]struct{}{mountPoint: {}}},
		&container.HostConfig{Binds: []string{bind}}, nil, "")
	if err != nil {
		t.Fatalf("\tContainer create failed: %v", err)
	}

	err = client.ContainerStart(context.Background(), r.ID,
		types.ContainerStartOptions{})
	if err != nil {
		t.Fatalf("\tContainer start failed: id=%s, err %v", r.ID, err)
	}

	code, err := client.ContainerWait(context.Background(), r.ID)
	if err != nil {
		t.Fatalf("\tContainer wait failed: id=%s, err %v", r.ID, err)
	}

	if removeContainers == false {
		t.Logf("\tSkipping container removal, id=%s (removeContainers == false)",
			r.ID)
		return code
	}

	err = client.ContainerRemove(context.Background(), r.ID,
		types.ContainerRemoveOptions{
			RemoveVolumes: true,
			Force:         true,
		})
	if err != nil {
		t.Fatalf("\nContainer removal failed: %v", err)
	}

	return code
}

// Checks that we can touch a file in one container and then stat it
// in another container, using the same (vmdk-based) volume
//
// goes over 'cases' and runs commands, then checks expected return code
func checkTouch(t *testing.T, c *client.Client, vol string,
	file string, addr string) {

	cases := []struct {
		image    string             // Container image to use
		cmd      *strslice.StrSlice // Command to run under busybox
		expected int                // expected results
	}{
		{"busybox", &strslice.StrSlice{"touch", getMountpoint(vol) + "/" + file}, 0},
		{"busybox", &strslice.StrSlice{"stat", getMountpoint(vol) + "/" + file}, 0},
	}

	for _, i := range cases {
		code := runContainerCmd(t, c, vol, i.image, i.cmd, addr)
		if code != i.expected {
			t.Errorf("Expected  %d, got %d (cmd: %v)", i.expected, code, i.cmd)
		}
	}
}

// returns nil for NOT_FOUND and  if volume exists
// still fails the test if driver for this volume is not vmdk
func volumeVmdkExists(t *testing.T, c *client.Client, vol string) *types.Volume {
	reply, err := c.VolumeList(context.Background(), filters.Args{})
	if err != nil {
		t.Fatalf("Failed to enumerate  volumes: %v", err)
	}

	for _, v := range reply.Volumes {
		//	t.Log(v.Name, v.Driver, v.Mountpoint)
		if v.Name == vol {
			return v
		}
	}
	return nil
}

// Sanity test for VMDK volumes
// - check we can attach/detach correct volume (we use 'touch' and 'stat' to validate
// - check volumes are correctly created and deleted.
// - check we see it properly from another docker VM (-H2 flag)
func TestSanity(t *testing.T) {

	fmt.Printf("Running tests on  %s (may take a while)...\n", endPoint1)
	clients := []struct {
		endPoint string
		client   *client.Client
	}{
		{endPoint1, new(client.Client)},
		{endPoint2, new(client.Client)},
	}

	for idx, elem := range clients {
		c, err := client.NewClient(elem.endPoint, apiVersion, nil, defaultHeaders)
		if err != nil {
			t.Fatalf("Failed to connect to %s, err: %v", elem.endPoint, err)
		}
		t.Logf("Successfully connected to %s", elem.endPoint)
		clients[idx].client = c
	}

	c := clients[0].client // this is the endpoint we use as master
	t.Logf("Creating vol=%s on client %s.", volumeName, clients[0].endPoint)
	_, err := c.VolumeCreate(context.Background(),
		types.VolumeCreateRequest{
			Name:   volumeName,
			Driver: driverName,
			DriverOpts: map[string]string{
				"size": "1gb",
			},
		})
	if err != nil {
		t.Fatal(err)
	}

	checkTouch(t, c, volumeName, "file_to_touch", clients[0].endPoint)

	for _, elem := range clients {
		v := volumeVmdkExists(t, elem.client, volumeName)
		if v == nil {
			t.Fatalf("Volume=%s is missing on %s after create",
				volumeName, elem.endPoint)
		}
		if v.Driver != driverName {
			t.Fatalf("wrong driver (%s) for volume %s", v.Driver, v.Name)
		}
	}

	err = c.VolumeRemove(context.Background(), volumeName)
	if err != nil {
		t.Fatalf("Failed to delete volume, err: %v", err)
	}

	for _, elem := range clients {
		if volumeVmdkExists(t, elem.client, volumeName) != nil {
			t.Errorf("Volume=%s is still present on %s after removal",
				volumeName, elem.endPoint)
		}
	}

	fmt.Printf("Running parallel tests on %s and %s (may take a while)...\n", endPoint1, endPoint2)
	// Create a short buffered channel to introduce random pauses
	results := make(chan error, parallelVolumes)
	createRequest := types.VolumeCreateRequest{
		Name:   volumeName,
		Driver: driverName,
		DriverOpts: map[string]string{
			"size": "1gb",
		},
	}
	// Create/delete routine
	for idx, elem := range clients {
		go func(idx int, c *client.Client) {
			for i := 0; i < parallelVolumes; i++ {
				volName := "volTestP" + strconv.Itoa(idx) + strconv.Itoa(i)
				createRequest.Name = volName
				_, err := c.VolumeCreate(context.Background(), createRequest)
				results <- err
				err = c.VolumeRemove(context.Background(), volName)
				results <- err
			}
		}(idx, elem.client)
	}
	// We need to read #clients * #volumes * 2 operations from the channel
	for i := 0; i < len(clients)*parallelVolumes*2; i++ {
		err := <-results
		if err != nil {
			t.Fatalf("Parallel test failed, err: %v", err)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build linux

// This is the filesystem interface for mounting volumes on the guest.

package fs

import (
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
)

const sysPciDevs = "/sys/bus/pci/devices" // All PCI devices on the host
const sysPciSlots = "/sys/bus/pci/slots"  // PCI slots on the host
const pciAddrLen = 10                     // Length of PCI dev addr

// FstypeDefault contains the default FS when not specified by the user
const FstypeDefault = "ext4"

// VolumeDevSpec - volume spec returned from the server on an attach
type VolumeDevSpec struct {
	Unit                    string
	ControllerPciSlotNumber string
}

// Mkdir creates a directory at the specified path
func Mkdir(path string) error {
	stat, err := os.Lstat(path)
	if os.IsNotExist(err) {
		if err := os.MkdirAll(path, 0755); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}

	if stat != nil && !stat.IsDir() {
		return fmt.Errorf("%v already exist and it's not a directory", path)
	}
	return nil
}

// Mkfs creates a filesystem at the specified device
func Mkfs(mkfscmd string, label string, device string) error {
	out, err := exec.Command(mkfscmd, "-L", label, device).CombinedOutput()
	if err != nil {
		return fmt.Errorf("Failed to create filesystem on %s: %s. Output = %s",
			device, err, out)
	}
	return nil
}

// MkfsLookup finds existent filesystem tools
func MkfsLookup() map[string]string {
	mkftools, _ := filepath.Glob("/sbin/mkfs.*")
	supportedFs := make(map[string]string)
	for _, mkfs := range mkftools {
		supportedFs[strings.Split(mkfs, ".")[1]] = mkfs
	}
	return supportedFs
}

// Mount the filesystem (`fs`) on the device at the given mount point.
func Mount(mountpoint string, fstype string, device string, isReadOnly bool) error {
	log.WithFields(log.Fields{
		"device":     device,
		"fstype":     fstype,
		"mountpoint": mountpoint,
	}).Debug("Calling syscall.Mount() ")

	flags := 0
	if isReadOnly {
		flags = syscall.MS_RDONLY
	}
	err := syscall.Mount(device, mountpoint, fstype, uintptr(flags), "")
	if err != nil {
		return fmt.Errorf("Failed to mount device %s at %s: %s", device, mountpoint, err)
	}
	return nil
}

// Unmount a device from the given mount point.
func Unmount(mountpoint string) error {
	return syscall.Unmount(mountpoint, 0)
}

// GetDevicePath - return device path or error
func GetDevicePath(str []byte) (string, error) {
	var volDev VolumeDevSpec
	err := json.Unmarshal(str, &volDev)
	if err != nil && len(err.Error()) != 0 {
		return "", err
	}

	// Get the device node for the unit returned from the attach.
	// Lookup each device that has a label and if that label matches
	// the one for the given bus number.
	// The device we need is then constructed from the dir name with
	// the matching label.
	pciSlotAddr := fmt.Sprintf("%s/%s/address", sysPciSlots, volDev.ControllerPciSlotNumber)

	fh, err := os.Open(pciSlotAddr)
	if err != nil {
		log.WithFields(log.Fields{"Error": err}).Warn("Get device path failed for unit# %s @ PCI slot %s: ",
			volDev.Unit, volDev.ControllerPciSlotNumber)
		return "", fmt.Errorf("Device not found")
	}

	buf := make([]byte, pciAddrLen)
	_, err = fh.Read(buf)

	fh.Close()
	if err != nil && err != io.EOF {
		log.WithFields(log.Fields{"Error": err}).Warn("Get device path failed for unit# %s @ PCI slot %s: ",
			volDev.Unit, volDev.ControllerPciSlotNumber)
		return "", fmt.Errorf("Device not found")
	}
	return fmt.Sprintf("/dev/disk/by-path/pci-%s.0-scsi-0:0:%s:0", string(buf), volDev.Unit), nil

}
<commit_msg>Workaround older versions of e2fsprogs (#631)<commit_after>// Copyright 2016 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build linux

// This is the filesystem interface for mounting volumes on the guest.

package fs

import (
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
)

const sysPciDevs = "/sys/bus/pci/devices" // All PCI devices on the host
const sysPciSlots = "/sys/bus/pci/slots"  // PCI slots on the host
const pciAddrLen = 10                     // Length of PCI dev addr

// FstypeDefault contains the default FS when not specified by the user
const FstypeDefault = "ext4"

// BinSearchPath contains search paths for host binaries
var BinSearchPath = []string{"/bin", "/sbin", "/usr/bin", "/usr/sbin"}

// VolumeDevSpec - volume spec returned from the server on an attach
type VolumeDevSpec struct {
	Unit                    string
	ControllerPciSlotNumber string
}

// Mkdir creates a directory at the specified path
func Mkdir(path string) error {
	stat, err := os.Lstat(path)
	if os.IsNotExist(err) {
		if err := os.MkdirAll(path, 0755); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}

	if stat != nil && !stat.IsDir() {
		return fmt.Errorf("%v already exist and it's not a directory", path)
	}
	return nil
}

// Mkfs creates a filesystem at the specified device
func Mkfs(mkfscmd string, label string, device string) error {
	var err error
	var out []byte

	// Workaround older versions of e2fsprogs, issue 629.
	// If mkfscmd is of an ext* filesystem use -F flag
	// to avoid having mkfs command to expect user confirmation.
	if strings.Split(mkfscmd, ".")[1][0:3] == "ext" {
		out, err = exec.Command(mkfscmd, "-F", "-L", label, device).CombinedOutput()
	} else {
		out, err = exec.Command(mkfscmd, "-L", label, device).CombinedOutput()
	}
	if err != nil {
		return fmt.Errorf("Failed to create filesystem on %s: %s. Output = %s",
			device, err, out)
	}
	return nil
}

// MkfsLookup finds existent filesystem tools
func MkfsLookup() map[string]string {
	supportedFs := make(map[string]string)

	for _, sp := range BinSearchPath {
		mkftools, _ := filepath.Glob(sp + "/mkfs.*")
		for _, mkfs := range mkftools {
			supportedFs[strings.Split(mkfs, ".")[1]] = mkfs
		}
	}
	return supportedFs
}

// Mount the filesystem (`fs`) on the device at the given mount point.
func Mount(mountpoint string, fstype string, device string, isReadOnly bool) error {
	log.WithFields(log.Fields{
		"device":     device,
		"fstype":     fstype,
		"mountpoint": mountpoint,
	}).Debug("Calling syscall.Mount() ")

	flags := 0
	if isReadOnly {
		flags = syscall.MS_RDONLY
	}
	err := syscall.Mount(device, mountpoint, fstype, uintptr(flags), "")
	if err != nil {
		return fmt.Errorf("Failed to mount device %s at %s: %s", device, mountpoint, err)
	}
	return nil
}

// Unmount a device from the given mount point.
func Unmount(mountpoint string) error {
	return syscall.Unmount(mountpoint, 0)
}

// GetDevicePath - return device path or error
func GetDevicePath(str []byte) (string, error) {
	var volDev VolumeDevSpec
	err := json.Unmarshal(str, &volDev)
	if err != nil && len(err.Error()) != 0 {
		return "", err
	}

	// Get the device node for the unit returned from the attach.
	// Lookup each device that has a label and if that label matches
	// the one for the given bus number.
	// The device we need is then constructed from the dir name with
	// the matching label.
	pciSlotAddr := fmt.Sprintf("%s/%s/address", sysPciSlots, volDev.ControllerPciSlotNumber)

	fh, err := os.Open(pciSlotAddr)
	if err != nil {
		log.WithFields(log.Fields{"Error": err}).Warn("Get device path failed for unit# %s @ PCI slot %s: ",
			volDev.Unit, volDev.ControllerPciSlotNumber)
		return "", fmt.Errorf("Device not found")
	}

	buf := make([]byte, pciAddrLen)
	_, err = fh.Read(buf)

	fh.Close()
	if err != nil && err != io.EOF {
		log.WithFields(log.Fields{"Error": err}).Warn("Get device path failed for unit# %s @ PCI slot %s: ",
			volDev.Unit, volDev.ControllerPciSlotNumber)
		return "", fmt.Errorf("Device not found")
	}
	return fmt.Sprintf("/dev/disk/by-path/pci-%s.0-scsi-0:0:%s:0", string(buf), volDev.Unit), nil

}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 l3plugin

import (
	"bytes"
	"fmt"
	"net"
	"sort"

	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/cn-infra/utils/addrs"
	"github.com/ligato/vpp-agent/plugins/vpp/ifplugin/ifaceidx"
	"github.com/ligato/vpp-agent/plugins/vpp/l3plugin/vppcalls"
	"github.com/ligato/vpp-agent/plugins/vpp/model/l3"
)

// SortedRoutes type is used to implement sort interface for slice of Route.
type SortedRoutes []*vppcalls.Route

// Return length of slice.
// Implements sort.Interface
func (arr SortedRoutes) Len() int {
	return len(arr)
}

// Swap swaps two items in slice identified by indices.
// Implements sort.Interface
func (arr SortedRoutes) Swap(i, j int) {
	arr[i], arr[j] = arr[j], arr[i]
}

// Less returns true if the item at index i in slice
// should be sorted before the element with index j.
// Implements sort.Interface
func (arr SortedRoutes) Less(i, j int) bool {
	return lessRoute(arr[i], arr[j])
}

func eqRoutes(a *vppcalls.Route, b *vppcalls.Route) bool {
	return a.Type == b.Type &&
		a.VrfID == b.VrfID &&
		bytes.Equal(a.DstAddr.IP, b.DstAddr.IP) &&
		bytes.Equal(a.DstAddr.Mask, b.DstAddr.Mask) &&
		bytes.Equal(a.NextHopAddr, b.NextHopAddr) &&
		a.ViaVrfId == b.ViaVrfId &&
		a.OutIface == b.OutIface &&
		a.Weight == b.Weight &&
		a.Preference == b.Preference
}

func lessRoute(a *vppcalls.Route, b *vppcalls.Route) bool {
	if a.Type != b.Type {
		return a.Type < b.Type
	}
	if a.VrfID != b.VrfID {
		return a.VrfID < b.VrfID
	}
	if !bytes.Equal(a.DstAddr.IP, b.DstAddr.IP) {
		return bytes.Compare(a.DstAddr.IP, b.DstAddr.IP) < 0
	}
	if !bytes.Equal(a.DstAddr.Mask, b.DstAddr.Mask) {
		return bytes.Compare(a.DstAddr.Mask, b.DstAddr.Mask) < 0
	}
	if !bytes.Equal(a.NextHopAddr, b.NextHopAddr) {
		return bytes.Compare(a.NextHopAddr, b.NextHopAddr) < 0
	}
	if a.ViaVrfId != b.ViaVrfId {
		return a.ViaVrfId < b.ViaVrfId
	}
	if a.OutIface != b.OutIface {
		return a.OutIface < b.OutIface
	}
	if a.Preference != b.Preference {
		return a.Preference < b.Preference
	}
	return a.Weight < b.Weight

}

// TransformRoute converts raw route data to Route object.
func TransformRoute(routeInput *l3.StaticRoutes_Route, swIndex uint32, log logging.Logger) (*vppcalls.Route, error) {
	if routeInput == nil {
		log.Infof("Route input is empty")
		return nil, nil
	}
	if routeInput.DstIpAddr == "" {
		if routeInput.Type != l3.StaticRoutes_Route_INTER_VRF {
			// no destination address is only allowed for inter-VRF routes
			log.Infof("Route does not contain destination address")
			return nil, nil
		}
	}
	parsedDestIP, isIpv6, err := addrs.ParseIPWithPrefix(routeInput.DstIpAddr)
	if err != nil {
		return nil, err
	}
	vrfID := routeInput.VrfId

	nextHopIP := net.ParseIP(routeInput.NextHopAddr)
	if isIpv6 {
		nextHopIP = nextHopIP.To16()
	} else {
		nextHopIP = nextHopIP.To4()
	}
	route := &vppcalls.Route{
		Type:        vppcalls.RouteType(routeInput.Type),
		VrfID:       vrfID,
		DstAddr:     *parsedDestIP,
		NextHopAddr: nextHopIP,
		ViaVrfId:    routeInput.ViaVrfId,
		OutIface:    swIndex,
		Weight:      routeInput.Weight,
		Preference:  routeInput.Preference,
	}
	return route, nil
}

func resolveInterfaceSwIndex(ifName string, index ifaceidx.SwIfIndex) (uint32, error) {
	ifIndex := vppcalls.NextHopOutgoingIfUnset
	if ifName != "" {
		var exists bool
		ifIndex, _, exists = index.LookupIdx(ifName)
		if !exists {
			return ifIndex, fmt.Errorf("route outgoing interface %v not found", ifName)
		}
	}
	return ifIndex, nil
}

func (plugin *RouteConfigurator) diffRoutes(new []*vppcalls.Route, old []*vppcalls.Route) (toBeDeleted []*vppcalls.Route, toBeAdded []*vppcalls.Route) {
	newSorted := SortedRoutes(new)
	oldSorted := SortedRoutes(old)
	sort.Sort(newSorted)
	sort.Sort(oldSorted)

	// Compare.
	i := 0
	j := 0
	for i < len(newSorted) && j < len(oldSorted) {
		if eqRoutes(newSorted[i], oldSorted[j]) {
			i++
			j++
		} else {
			if lessRoute(newSorted[i], oldSorted[j]) {
				toBeAdded = append(toBeAdded, newSorted[i])
				i++
			} else {
				toBeDeleted = append(toBeDeleted, oldSorted[j])
				j++
			}
		}
	}

	for ; i < len(newSorted); i++ {
		toBeAdded = append(toBeAdded, newSorted[i])
	}

	for ; j < len(oldSorted); j++ {
		toBeDeleted = append(toBeDeleted, oldSorted[j])
	}
	return
}
<commit_msg>Get rid uf unnecessary check<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 l3plugin

import (
	"bytes"
	"fmt"
	"net"
	"sort"

	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/cn-infra/utils/addrs"
	"github.com/ligato/vpp-agent/plugins/vpp/ifplugin/ifaceidx"
	"github.com/ligato/vpp-agent/plugins/vpp/l3plugin/vppcalls"
	"github.com/ligato/vpp-agent/plugins/vpp/model/l3"
)

// SortedRoutes type is used to implement sort interface for slice of Route.
type SortedRoutes []*vppcalls.Route

// Return length of slice.
// Implements sort.Interface
func (arr SortedRoutes) Len() int {
	return len(arr)
}

// Swap swaps two items in slice identified by indices.
// Implements sort.Interface
func (arr SortedRoutes) Swap(i, j int) {
	arr[i], arr[j] = arr[j], arr[i]
}

// Less returns true if the item at index i in slice
// should be sorted before the element with index j.
// Implements sort.Interface
func (arr SortedRoutes) Less(i, j int) bool {
	return lessRoute(arr[i], arr[j])
}

func eqRoutes(a *vppcalls.Route, b *vppcalls.Route) bool {
	return a.Type == b.Type &&
		a.VrfID == b.VrfID &&
		bytes.Equal(a.DstAddr.IP, b.DstAddr.IP) &&
		bytes.Equal(a.DstAddr.Mask, b.DstAddr.Mask) &&
		bytes.Equal(a.NextHopAddr, b.NextHopAddr) &&
		a.ViaVrfId == b.ViaVrfId &&
		a.OutIface == b.OutIface &&
		a.Weight == b.Weight &&
		a.Preference == b.Preference
}

func lessRoute(a *vppcalls.Route, b *vppcalls.Route) bool {
	if a.Type != b.Type {
		return a.Type < b.Type
	}
	if a.VrfID != b.VrfID {
		return a.VrfID < b.VrfID
	}
	if !bytes.Equal(a.DstAddr.IP, b.DstAddr.IP) {
		return bytes.Compare(a.DstAddr.IP, b.DstAddr.IP) < 0
	}
	if !bytes.Equal(a.DstAddr.Mask, b.DstAddr.Mask) {
		return bytes.Compare(a.DstAddr.Mask, b.DstAddr.Mask) < 0
	}
	if !bytes.Equal(a.NextHopAddr, b.NextHopAddr) {
		return bytes.Compare(a.NextHopAddr, b.NextHopAddr) < 0
	}
	if a.ViaVrfId != b.ViaVrfId {
		return a.ViaVrfId < b.ViaVrfId
	}
	if a.OutIface != b.OutIface {
		return a.OutIface < b.OutIface
	}
	if a.Preference != b.Preference {
		return a.Preference < b.Preference
	}
	return a.Weight < b.Weight

}

// TransformRoute converts raw route data to Route object.
func TransformRoute(routeInput *l3.StaticRoutes_Route, swIndex uint32, log logging.Logger) (*vppcalls.Route, error) {
	if routeInput == nil {
		log.Infof("Route input is empty")
		return nil, nil
	}
	if routeInput.DstIpAddr == "" {
		log.Infof("Route does not contain destination address")
		return nil, nil
	}
	parsedDestIP, isIpv6, err := addrs.ParseIPWithPrefix(routeInput.DstIpAddr)
	if err != nil {
		return nil, err
	}
	vrfID := routeInput.VrfId

	nextHopIP := net.ParseIP(routeInput.NextHopAddr)
	if isIpv6 {
		nextHopIP = nextHopIP.To16()
	} else {
		nextHopIP = nextHopIP.To4()
	}
	route := &vppcalls.Route{
		Type:        vppcalls.RouteType(routeInput.Type),
		VrfID:       vrfID,
		DstAddr:     *parsedDestIP,
		NextHopAddr: nextHopIP,
		ViaVrfId:    routeInput.ViaVrfId,
		OutIface:    swIndex,
		Weight:      routeInput.Weight,
		Preference:  routeInput.Preference,
	}
	return route, nil
}

func resolveInterfaceSwIndex(ifName string, index ifaceidx.SwIfIndex) (uint32, error) {
	ifIndex := vppcalls.NextHopOutgoingIfUnset
	if ifName != "" {
		var exists bool
		ifIndex, _, exists = index.LookupIdx(ifName)
		if !exists {
			return ifIndex, fmt.Errorf("route outgoing interface %v not found", ifName)
		}
	}
	return ifIndex, nil
}

func (plugin *RouteConfigurator) diffRoutes(new []*vppcalls.Route, old []*vppcalls.Route) (toBeDeleted []*vppcalls.Route, toBeAdded []*vppcalls.Route) {
	newSorted := SortedRoutes(new)
	oldSorted := SortedRoutes(old)
	sort.Sort(newSorted)
	sort.Sort(oldSorted)

	// Compare.
	i := 0
	j := 0
	for i < len(newSorted) && j < len(oldSorted) {
		if eqRoutes(newSorted[i], oldSorted[j]) {
			i++
			j++
		} else {
			if lessRoute(newSorted[i], oldSorted[j]) {
				toBeAdded = append(toBeAdded, newSorted[i])
				i++
			} else {
				toBeDeleted = append(toBeDeleted, oldSorted[j])
				j++
			}
		}
	}

	for ; i < len(newSorted); i++ {
		toBeAdded = append(toBeAdded, newSorted[i])
	}

	for ; j < len(oldSorted); j++ {
		toBeDeleted = append(toBeDeleted, oldSorted[j])
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	forecast "github.com/mlbright/darksky/v2"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strings"
)

type gps struct {
	name string
	lat  string
	long string
}

var key string

func icon(str string, moonPhase float64) string {
	var luna string

	switch {
	case moonPhase >= 0 && moonPhase < 0.12:
		luna = "🌑" // new moon
	case moonPhase >= 0.12 && moonPhase < 0.25:
		luna = "🌒" // waxing cresent
	case moonPhase >=  0.25 && moonPhase < 0.37:
		luna = "🌓" // first quarter
	case moonPhase >=  0.37 && moonPhase < 0.5:
		luna = "🌔" // waxing gibbous
	case moonPhase >= 0.5 && moonPhase < 0.62 :
		luna = "🌕" // full moon

	case moonPhase >= 0.62 && moonPhase < 0.75 :
		luna = "🌖" // waning gibbous
	case moonPhase >= 0.75 && moonPhase < 0.87 :
		luna = "🌗" // last quarter
	case moonPhase >= 0.87 && moonPhase < 1 :
		luna = "🌘" // warning crescent
	}

	translation := map[string]string {
		"clear-day": "☀",
			"clear-night": luna ,
			"rain": "🌧",
			"snow": "🌨",
			"sleet": "❄💧" ,
			"wind": "🌬",
			"fog": "🌫",
			"cloudy": "☁",
			"partly-cloudy-day": "⛅",
			"partly-cloudy-night": "☁"+luna,
			"hail": "🌨 grêle",
			"thunderstorm": "⛈",
			"tornado": "🌪",
		}

	if v, ok := translation[str]; ok {
		return v
	}
	return str
}

func temp(temp int) string {
	/*
	- 00 - White.
	- 01 - Black.
	- 02 - Blue.
	- 03 - Green.
	- 04 - Red.
	- 05 - Brown.
	- 06 - Magenta.
	- 07 - Orange.
	- 08 - Yellow.
	- 09 - Light Green.
	- 10 - Cyan.
	- 11 - Light Cyan.
	- 12 - Light Blue.
	- 13 - Pink.
	- 14 - Grey.
	- 15 - Light Grey.
	- 99 - Default Foreground/Background - Not universally supported.
	*/
        switch {
        case temp < -5:
			// bold blue
			return fmt.Sprintf(
				"%c%c02%d%c", 0x02, 0x03, temp, 0x0f,
			)
        case temp >= -5 && temp <= 5:
			// cyan
			return fmt.Sprintf(
				"%c10%d%c", 0x03, temp, 0x0f,
			)
        case temp > 5 && temp < 30:
			// normal
			return fmt.Sprintf("%d", temp)
        case temp >= 30 && temp < 35:
			// orange
			return fmt.Sprintf(
				"%c07%d%c", 0x03, temp, 0x0f,
			)
        case temp > 35:
			// bold red
			return fmt.Sprintf(
				"%c%c04%d%c", 0x02, 0x03, temp, 0x0f,
			)
		}

	return fmt.Sprintf("%d", temp)
}

func Scities(cities []gps) string {
	res := []string{}
	for _, city := range cities {
		f, err := forecast.Get(key, city.lat, city.long, "now", forecast.CA, forecast.French)
		if err != nil {
			log.Fatal(err)
		}
		res = append(
			res,
			fmt.Sprintf(
				"%s %s %sC (%sC) H:%d W:%dkm/h",
				city.name,
				icon(f.Currently.Icon, f.Currently.MoonPhase),
				temp(Round(f.Currently.Temperature)),
				temp(Round(f.Currently.ApparentTemperature)),
				Round(f.Currently.Humidity*100),
				Round(f.Currently.WindSpeed),
			),
		)
	}
	return strings.Join(res, " | ")
}

func Round(value float64) int {
	if value < 0.0 {
		value -= 0.5
	} else {
		value += 0.5
	}
	return int(value)
}

func main() {
	keybytes, err := ioutil.ReadFile("darksky_key.txt")
	if err != nil {
		log.Fatal(err)
	}
	key = string(keybytes)
	key = strings.TrimSpace(key)

	cities := []gps{
		{"Aigaliers", "44.074622", "4.30553"},
		{"Amsterdam", "52.3745", "4.898"},
		{"Budapest", "47.4984", "19.0405"},
		{"Cournonsec", "43.5482", "3.7"},
		{"Dijon", "47.3216", "5.0415"},
		{"Hanoi", "21.0292", "105.8525"},
		{"Marseille", "43.2962", "5.37"},
		{"Montréal", "45.5088", "-73.554"},
		{"Petrozavodsk", "61.79", "34.39"},
	}

	bio := bufio.NewReader(os.Stdin)
	r, _ := regexp.Compile("PRIVMSG (#\\S+) ::meteo")
	for {
		line, err := bio.ReadString('\n')
		if err != nil {
			fmt.Println(err)
		} else {
			par := r.FindStringSubmatch(line)
			if par != nil {
				fmt.Printf("PRIVMSG %s :%s\n", par[1], Scities(cities))
			}
		}
	}
}
<commit_msg>weather: Format wind<commit_after>package main

import (
	"bufio"
	"fmt"
	forecast "github.com/mlbright/darksky/v2"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strings"
)

type gps struct {
	name string
	lat  string
	long string
}

var key string

func icon(str string, moonPhase float64) string {
	var luna string

	switch {
	case moonPhase >= 0 && moonPhase < 0.12:
		luna = "🌑" // new moon
	case moonPhase >= 0.12 && moonPhase < 0.25:
		luna = "🌒" // waxing cresent
	case moonPhase >=  0.25 && moonPhase < 0.37:
		luna = "🌓" // first quarter
	case moonPhase >=  0.37 && moonPhase < 0.5:
		luna = "🌔" // waxing gibbous
	case moonPhase >= 0.5 && moonPhase < 0.62 :
		luna = "🌕" // full moon

	case moonPhase >= 0.62 && moonPhase < 0.75 :
		luna = "🌖" // waning gibbous
	case moonPhase >= 0.75 && moonPhase < 0.87 :
		luna = "🌗" // last quarter
	case moonPhase >= 0.87 && moonPhase < 1 :
		luna = "🌘" // warning crescent
	}

	translation := map[string]string {
		"clear-day": "☀",
			"clear-night": luna ,
			"rain": "🌧",
			"snow": "🌨",
			"sleet": "❄💧" ,
			"wind": "🌬",
			"fog": "🌫",
			"cloudy": "☁",
			"partly-cloudy-day": "⛅",
			"partly-cloudy-night": "☁"+luna,
			"hail": "🌨 grêle",
			"thunderstorm": "⛈",
			"tornado": "🌪",
		}

	if v, ok := translation[str]; ok {
		return v
	}
	return str
}

func temp(temp int) string {
	/*
	- 00 - White.
	- 01 - Black.
	- 02 - Blue.
	- 03 - Green.
	- 04 - Red.
	- 05 - Brown.
	- 06 - Magenta.
	- 07 - Orange.
	- 08 - Yellow.
	- 09 - Light Green.
	- 10 - Cyan.
	- 11 - Light Cyan.
	- 12 - Light Blue.
	- 13 - Pink.
	- 14 - Grey.
	- 15 - Light Grey.
	- 99 - Default Foreground/Background - Not universally supported.
	*/
        switch {
        case temp < -5:
			// bold blue
			return fmt.Sprintf(
				"%c%c02%d%c", 0x02, 0x03, temp, 0x0f,
			)
        case temp >= -5 && temp <= 5:
			// cyan
			return fmt.Sprintf(
				"%c10%d%c", 0x03, temp, 0x0f,
			)
        case temp > 5 && temp < 30:
			// normal
			return fmt.Sprintf("%d", temp)
        case temp >= 30 && temp < 35:
			// orange
			return fmt.Sprintf(
				"%c07%d%c", 0x03, temp, 0x0f,
			)
        case temp > 35:
			// bold red
			return fmt.Sprintf(
				"%c%c04%d%c", 0x02, 0x03, temp, 0x0f,
			)
		}

	return fmt.Sprintf("%d", temp)
}

func wind(w int) string {
	switch {
	case w > 39:
		// vent frais
		// bold
		return fmt.Sprintf("%c%d%c", 0x02, w, 0x0f)
	case w > 50:
		// Grand frais
		// bold + underline
		return fmt.Sprintf("%c%c%d%c", 0x1f, 0x02, w, 0x0f)
	case w > 62:
		// coupe de vent
		// reverse colors
		return fmt.Sprintf("%c%d%c", 0x16, w, 0x0f)
	case w > 75:
		// reverse colors + bold
		return fmt.Sprintf("%c%c%d%c", 0x16, 0x02, w, 0x0f)
	case w > 89:
		// reverse colors + bold + underline
		return fmt.Sprintf("%c%c%c%d%c", 0x16, 0x02, 0x1f, w, 0x0f)
	}

	return fmt.Sprintf("%d", w)
}

func Scities(cities []gps) string {
	res := []string{}
	for _, city := range cities {
		f, err := forecast.Get(key, city.lat, city.long, "now", forecast.CA, forecast.French)
		if err != nil {
			log.Fatal(err)
		}
		res = append(
			res,
			fmt.Sprintf(
				"%s %s %sC (%sC) H:%d W:%skm/h",
				city.name,
				icon(f.Currently.Icon, f.Currently.MoonPhase),
				temp(Round(f.Currently.Temperature)),
				temp(Round(f.Currently.ApparentTemperature)),
				Round(f.Currently.Humidity*100),
				wind(Round(f.Currently.WindSpeed)),
			),
		)
	}
	return strings.Join(res, " | ")
}

func Round(value float64) int {
	if value < 0.0 {
		value -= 0.5
	} else {
		value += 0.5
	}
	return int(value)
}

func main() {
	keybytes, err := ioutil.ReadFile("darksky_key.txt")
	if err != nil {
		log.Fatal(err)
	}
	key = string(keybytes)
	key = strings.TrimSpace(key)

	cities := []gps{
		{"Aigaliers", "44.074622", "4.30553"},
		{"Amsterdam", "52.3745", "4.898"},
		{"Budapest", "47.4984", "19.0405"},
		{"Cournonsec", "43.5482", "3.7"},
		{"Dijon", "47.3216", "5.0415"},
		{"Hanoi", "21.0292", "105.8525"},
		{"Marseille", "43.2962", "5.37"},
		{"Montréal", "45.5088", "-73.554"},
		{"Petrozavodsk", "61.79", "34.39"},
	}

	bio := bufio.NewReader(os.Stdin)
	r, _ := regexp.Compile("PRIVMSG (#\\S+) ::meteo")
	for {
		line, err := bio.ReadString('\n')
		if err != nil {
			fmt.Println(err)
		} else {
			par := r.FindStringSubmatch(line)
			if par != nil {
				fmt.Printf("PRIVMSG %s :%s\n", par[1], Scities(cities))
			}
		}
	}
}
<|endoftext|>


<commit_before>// Package client is the reference client implementation for the watefall service
package client

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/waterfall"
	waterfall_grpc "github.com/waterfall/proto/waterfall_go_grpc"
	"golang.org/x/sync/errgroup"
)

// Echo streams back the contents of the request. Useful for testing the connection.
func Echo(ctx context.Context, client waterfall_grpc.WaterfallClient, r []byte) ([]byte, error) {
	stream, err := client.Echo(ctx)
	if err != nil {
		return nil, err
	}
	eg, ctx := errgroup.WithContext(ctx)
	rec := new(bytes.Buffer)
	eg.Go(func() error {
		for {
			in, err := stream.Recv()
			if err == io.EOF {
				return nil
			}
			if err != nil {
				return err
			}
			rec.Write(in.Payload)
		}
	})
	eg.Go(func() error {
		send := bytes.NewBuffer(r)
		b := make([]byte, 32*1024)
		for {
			n, err := send.Read(b)
			if n > 0 {
				p := &waterfall_grpc.Message{Payload: b[0:n]}
				if err := stream.Send(p); err != nil {
					return err
				}
			}
			if err == io.EOF {
				break
			}
			if err != nil {
				return err
			}
		}
		return stream.CloseSend()
	})
	if err := eg.Wait(); err != nil {
		return nil, err
	}
	return rec.Bytes(), nil
}

// Push pushes a tar stream to the server running in the device.
func Push(ctx context.Context, client waterfall_grpc.WaterfallClient, src, dst string) error {
	rpc, err := client.Push(ctx)
	if err != nil {
		return err
	}

	r, w := io.Pipe()
	defer r.Close()
	eg, ctx := errgroup.WithContext(ctx)
	eg.Go(func() error {
		err := waterfall.Tar(w, src)
		w.Close()
		return err
	})

	buff := make([]byte, 64*1024)
	eg.Go(func() error {
		for {
			n, err := r.Read(buff)
			if err != nil && err != io.EOF {
				return err
			}

			if n > 0 {
				xfer := &waterfall_grpc.Transfer{Path: dst, Payload: buff[0:n]}
				if err := rpc.Send(xfer); err != nil {
					return err
				}
			}

			if err == io.EOF {
				r, err := rpc.CloseAndRecv()
				if err != nil {
					return err
				}

				if !r.Success {
					return fmt.Errorf(string(r.Err))
				}
				return nil
			}
		}
	})
	return eg.Wait()
}

// Pull request a file/directory from the device and unpacks the contents into the desired path.
func Pull(ctx context.Context, client waterfall_grpc.WaterfallClient, src, dst string) error {
	if _, err := os.Stat(filepath.Dir(dst)); err != nil {
		return err
	}

	xstream, err := client.Pull(ctx, &waterfall_grpc.Transfer{Path: src})
	if err != nil {
		return err
	}

	r, w := io.Pipe()
	eg, ctx := errgroup.WithContext(ctx)
	eg.Go(func() error {
		err := waterfall.Untar(r, dst)
		r.Close()
		return err
	})

	eg.Go(func() error {
		defer w.Close()
		for {
			fgmt, err := xstream.Recv()
			if err != nil {
				w.Close()
				if err == io.EOF {
					return nil
				}
				return err
			}
			if _, err := w.Write(fgmt.Payload); err != nil {
				return err
			}
		}
	})
	return eg.Wait()
}

// ExecError contains the status code from the executed command.
type ExecError struct {
	ExitCode uint32
}

// Error returns the string representation for an ExecError.
func (e ExecError) Error() string {
	return fmt.Sprintf("non-zero exit code: %d", e.ExitCode)
}

// Exec executes the requested command on the device. Semantics are the same as execve.
func Exec(ctx context.Context, client waterfall_grpc.WaterfallClient, stdout, stderr io.Writer, cmd string, args ...string) error {
	xstream, err := client.Exec(ctx)
	if err != nil {
		return err
	}

	// send the command at the head of the stream. After that the server will ignore subsequent messages.
	if err := xstream.Send(
		&waterfall_grpc.CmdProgress{Cmd: &waterfall_grpc.Cmd{Path: cmd, Args: args}}); err != nil {
		return err
	}

	var last *waterfall_grpc.CmdProgress
	for {
		pgrs, err := xstream.Recv()
		if err != nil {
			if err == io.EOF {
				break
			}
			return err
		}

		if pgrs.Stdout != nil {
			if _, err := stdout.Write(pgrs.Stdout); err != nil {
				return err
			}
		}
		if pgrs.Stderr != nil {
			if _, err := stdout.Write(pgrs.Stdout); err != nil {
				return err
			}
		}
		last = pgrs
	}
	if last.ExitCode != 0 {
		return ExecError{ExitCode: last.ExitCode}
	}
	return nil
}
<commit_msg>Fixed error stderr -> stdout to stderr -> stderr<commit_after>// Package client is the reference client implementation for the watefall service
package client

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/waterfall"
	waterfall_grpc "github.com/waterfall/proto/waterfall_go_grpc"
	"golang.org/x/sync/errgroup"
)

// Echo streams back the contents of the request. Useful for testing the connection.
func Echo(ctx context.Context, client waterfall_grpc.WaterfallClient, r []byte) ([]byte, error) {
	stream, err := client.Echo(ctx)
	if err != nil {
		return nil, err
	}
	eg, ctx := errgroup.WithContext(ctx)
	rec := new(bytes.Buffer)
	eg.Go(func() error {
		for {
			in, err := stream.Recv()
			if err == io.EOF {
				return nil
			}
			if err != nil {
				return err
			}
			rec.Write(in.Payload)
		}
	})
	eg.Go(func() error {
		send := bytes.NewBuffer(r)
		b := make([]byte, 32*1024)
		for {
			n, err := send.Read(b)
			if n > 0 {
				p := &waterfall_grpc.Message{Payload: b[0:n]}
				if err := stream.Send(p); err != nil {
					return err
				}
			}
			if err == io.EOF {
				break
			}
			if err != nil {
				return err
			}
		}
		return stream.CloseSend()
	})
	if err := eg.Wait(); err != nil {
		return nil, err
	}
	return rec.Bytes(), nil
}

// Push pushes a tar stream to the server running in the device.
func Push(ctx context.Context, client waterfall_grpc.WaterfallClient, src, dst string) error {
	rpc, err := client.Push(ctx)
	if err != nil {
		return err
	}

	r, w := io.Pipe()
	defer r.Close()
	eg, ctx := errgroup.WithContext(ctx)
	eg.Go(func() error {
		err := waterfall.Tar(w, src)
		w.Close()
		return err
	})

	buff := make([]byte, 64*1024)
	eg.Go(func() error {
		for {
			n, err := r.Read(buff)
			if err != nil && err != io.EOF {
				return err
			}

			if n > 0 {
				xfer := &waterfall_grpc.Transfer{Path: dst, Payload: buff[0:n]}
				if err := rpc.Send(xfer); err != nil {
					return err
				}
			}

			if err == io.EOF {
				r, err := rpc.CloseAndRecv()
				if err != nil {
					return err
				}

				if !r.Success {
					return fmt.Errorf(string(r.Err))
				}
				return nil
			}
		}
	})
	return eg.Wait()
}

// Pull request a file/directory from the device and unpacks the contents into the desired path.
func Pull(ctx context.Context, client waterfall_grpc.WaterfallClient, src, dst string) error {
	if _, err := os.Stat(filepath.Dir(dst)); err != nil {
		return err
	}

	xstream, err := client.Pull(ctx, &waterfall_grpc.Transfer{Path: src})
	if err != nil {
		return err
	}

	r, w := io.Pipe()
	eg, ctx := errgroup.WithContext(ctx)
	eg.Go(func() error {
		err := waterfall.Untar(r, dst)
		r.Close()
		return err
	})

	eg.Go(func() error {
		defer w.Close()
		for {
			fgmt, err := xstream.Recv()
			if err != nil {
				w.Close()
				if err == io.EOF {
					return nil
				}
				return err
			}
			if _, err := w.Write(fgmt.Payload); err != nil {
				return err
			}
		}
	})
	return eg.Wait()
}

// ExecError contains the status code from the executed command.
type ExecError struct {
	ExitCode uint32
}

// Error returns the string representation for an ExecError.
func (e ExecError) Error() string {
	return fmt.Sprintf("non-zero exit code: %d", e.ExitCode)
}

// Exec executes the requested command on the device. Semantics are the same as execve.
func Exec(ctx context.Context, client waterfall_grpc.WaterfallClient, stdout, stderr io.Writer, cmd string, args ...string) error {
	xstream, err := client.Exec(ctx)
	if err != nil {
		return err
	}

	// send the command at the head of the stream. After that the server will ignore subsequent messages.
	if err := xstream.Send(
		&waterfall_grpc.CmdProgress{Cmd: &waterfall_grpc.Cmd{Path: cmd, Args: args}}); err != nil {
		return err
	}

	var last *waterfall_grpc.CmdProgress
	for {
		pgrs, err := xstream.Recv()
		if err != nil {
			if err == io.EOF {
				break
			}
			return err
		}

		if pgrs.Stdout != nil {
			if _, err := stdout.Write(pgrs.Stdout); err != nil {
				return err
			}
		}
		if pgrs.Stderr != nil {
			if _, err := stdout.Write(pgrs.Stderr); err != nil {
				return err
			}
		}
		last = pgrs
	}
	if last.ExitCode != 0 {
		return ExecError{ExitCode: last.ExitCode}
	}
	return nil
}
<|endoftext|>