<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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 e2e

import (
	"fmt"
	"strings"
	"testing"
	"time"

	habv1beta1 "github.com/habitat-sh/habitat-operator/pkg/apis/habitat/v1beta1"
	utils "github.com/habitat-sh/habitat-operator/test/e2e/framework"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
)

const (
	serviceStartupWaitTime = 1 * time.Minute
	secretUpdateTimeout    = 2 * time.Minute
	secretUpdateQueryTime  = 10 * time.Second

	configMapName = "peer-watch-file"
)

// TestBind tests that the operator correctly created two Habitat Services and bound them together.
func TestBind(t *testing.T) {
	// Get Habitat object from Habitat go example.
	web, err := utils.ConvertHabitat("resources/bind-config/webapp.yml")
	if err != nil {
		t.Fatal(err)
	}

	if err := framework.CreateHabitat(web); err != nil {
		t.Fatal(err)
	}

	// Get Habitat object from Habitat db example.
	db, err := utils.ConvertHabitat("resources/bind-config/db.yml")
	if err != nil {
		t.Fatal(err)
	}

	if err := framework.CreateHabitat(db); err != nil {
		t.Fatal(err)
	}

	// Get Service object from example file.
	svc, err := utils.ConvertService("resources/bind-config/service.yml")
	if err != nil {
		t.Fatal(err)
	}

	// Create Service.
	_, err = framework.KubeClient.CoreV1().Services(utils.TestNs).Create(svc)
	if err != nil {
		t.Fatal(err)
	}
	// Delete Service so it doesn't interfere with other tests.
	defer (func(name string) {
		if err := framework.DeleteService(name); err != nil {
			t.Fatal(err)
		}
	})(svc.Name)

	// Get Secret object from example file.
	sec, err := utils.ConvertSecret("resources/bind-config/secret.yml")
	if err != nil {
		t.Fatal(err)
	}

	// Create Secret.
	sec, err = framework.KubeClient.CoreV1().Secrets(utils.TestNs).Create(sec)
	if err != nil {
		t.Fatal(err)
	}

	// Wait for resources to be ready.
	if err := framework.WaitForResources(habv1beta1.HabitatNameLabel, web.ObjectMeta.Name, 1); err != nil {
		t.Fatal(err)
	}
	if err := framework.WaitForResources(habv1beta1.HabitatNameLabel, db.ObjectMeta.Name, 1); err != nil {
		t.Fatal(err)
	}

	// Wait until endpoints are ready.
	if err := framework.WaitForEndpoints(svc.ObjectMeta.Name); err != nil {
		t.Fatal(err)
	}

	time.Sleep(serviceStartupWaitTime)

	// Get response from Habitat Service.
	url := fmt.Sprintf("http://%s:30001/", framework.ExternalIP)

	body, err := utils.QueryService(url)
	if err != nil {
		t.Fatal(err)
	}

	// This msg is set in the config of the habitat/bindgo-hab Go Habitat Service.
	expectedMsg := "hello from port: 4444"
	actualMsg := body
	// actualMsg can contain whitespace and newlines or different formatting,
	// the only thing we need to check is it contains the expectedMsg.
	if !strings.Contains(actualMsg, expectedMsg) {
		t.Fatalf("Habitat Service msg does not match one in default.toml. Expected: \"%s\", got: \"%s\"", expectedMsg, actualMsg)
	}

	// Test `user.toml` updates.

	// Update secret.
	newPort := "port = 6333"

	sec.Data["user.toml"] = []byte(newPort)
	if _, err = framework.KubeClient.CoreV1().Secrets(utils.TestNs).Update(sec); err != nil {
		t.Fatalf("Could not update Secret: \"%s\"", err)
	}

	ticker := time.NewTicker(secretUpdateQueryTime)
	defer ticker.Stop()
	timer := time.NewTimer(secretUpdateTimeout)
	defer timer.Stop()
	// Update the message set in the config of the habitat/bindgo-hab Go Habitat Service.
	expectedMsg = fmt.Sprintf("hello from port: %v", 6333)
	for {
		// Check that the port differs after the update.
		actualMsg, err := utils.QueryService(url)
		if err != nil {
			t.Fatal(err)
		}

		// actualMsg can contain whitespace and newlines or different formatting,
		// the only thing we need to check is it contains the expectedMsg.
		if strings.Contains(actualMsg, expectedMsg) {
			break
		}
		select {
		case <-timer.C:
			t.Fatalf("Configuration update did not go through. Expected: \"%s\", got: \"%s\"", expectedMsg, actualMsg)
		case <-ticker.C:
		}
	}
}

// TestHabitatDelete tests Habitat deletion.
func TestHabitatDelete(t *testing.T) {
	// Get Habitat object from Habitat go example.
	habitat, err := utils.ConvertHabitat("resources/standalone/habitat.yml")
	if err != nil {
		t.Fatal(err)
	}

	if err := framework.CreateHabitat(habitat); err != nil {
		t.Fatal(err)
	}

	// Wait for resources to be ready.
	if err := framework.WaitForResources(habv1beta1.HabitatNameLabel, habitat.ObjectMeta.Name, 1); err != nil {
		t.Fatal(err)
	}

	// Delete Habitat.
	if err := framework.DeleteHabitat(habitat.ObjectMeta.Name); err != nil {
		t.Fatal(err)
	}

	// Wait for resources to be deleted.
	if err := framework.WaitForResources(habv1beta1.HabitatNameLabel, habitat.ObjectMeta.Name, 0); err != nil {
		t.Fatal(err)
	}

	// Check if all the resources the operator creates are deleted.
	// We do not care about secrets being deleted, as the user needs to delete those manually.
	d, err := framework.KubeClient.AppsV1beta1().Deployments(utils.TestNs).Get(habitat.ObjectMeta.Name, metav1.GetOptions{})
	if err == nil && d != nil {
		t.Fatal("Deployment was not deleted.")
	}

	// The CM with the peer IP should still be alive, despite the Habitat being deleted as it was created outside of the scope of a Habitat.
	_, err = framework.KubeClient.CoreV1().ConfigMaps(utils.TestNs).Get(configMapName, metav1.GetOptions{})
	if err != nil {
		t.Fatal(err)
	}
}

func TestPersistentStorage(t *testing.T) {
	// We run minikube in a VM on Travis. In that environment, we cannot create PersistentVolumes.
	t.Skip("This test cannot be run successfully in our current testing setup")

	ephemeral, err := utils.ConvertHabitat("resources/standalone/habitat.yml")
	if err != nil {
		t.Fatal(err)
	}

	persisted, err := utils.ConvertHabitat("resources/persisted/habitat.yml")
	if err != nil {
		t.Fatal(err)
	}

	if err := framework.CreateHabitat(ephemeral); err != nil {
		t.Fatal(err)
	}

	if err := framework.CreateHabitat(persisted); err != nil {
		t.Fatal(err)
	}

	// Delete all PVCs at the end of the test.
	// For dynamically provisioned PVs (as is the case on minikube), this will
	// also delete the PVs.
	defer (func(name string) {
		ls := labels.SelectorFromSet(labels.Set(map[string]string{
			habv1beta1.HabitatNameLabel: name,
		}))

		lo := metav1.ListOptions{
			LabelSelector: ls.String(),
		}

		err := framework.KubeClient.CoreV1().PersistentVolumeClaims(utils.TestNs).DeleteCollection(&metav1.DeleteOptions{}, lo)
		if err != nil {
			t.Fatal(err)
		}
	})(persisted.Name)

	if err := framework.WaitForResources(habv1beta1.HabitatNameLabel, persisted.Name, 1); err != nil {
		t.Fatal(err)
	}

	// Test that persistence is only enabled if requested
	ephemeralSTS, err := framework.KubeClient.AppsV1beta1().StatefulSets(utils.TestNs).Get(ephemeral.Name, metav1.GetOptions{})
	if err != nil {
		t.Fatal(err)
	}

	if len(ephemeralSTS.Spec.VolumeClaimTemplates) != 0 {
		t.Fatal("PersistentVolumeClaims created for ephemeral StatefulSet")
	}

	persistedSTS, err := framework.KubeClient.AppsV1beta1().StatefulSets(utils.TestNs).Get(persisted.Name, metav1.GetOptions{})
	if err != nil {
		t.Fatal(err)
	}

	if len(persistedSTS.Spec.VolumeClaimTemplates) == 0 {
		t.Fatal("No PersistentVolumeClaims created for persistent StatefulSet")
	}
}
<commit_msg>Time is hard<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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 e2e

import (
	"fmt"
	"strings"
	"testing"
	"time"

	habv1beta1 "github.com/habitat-sh/habitat-operator/pkg/apis/habitat/v1beta1"
	utils "github.com/habitat-sh/habitat-operator/test/e2e/framework"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
)

const (
	serviceStartupWaitTime = 1 * time.Minute
	secretUpdateTimeout    = 2 * time.Minute
	secretUpdateQueryTime  = 10 * time.Second

	configMapName = "peer-watch-file"
)

// TestBind tests that the operator correctly created two Habitat Services and bound them together.
func TestBind(t *testing.T) {
	// Get Habitat object from Habitat go example.
	web, err := utils.ConvertHabitat("resources/bind-config/webapp.yml")
	if err != nil {
		t.Fatal(err)
	}

	if err := framework.CreateHabitat(web); err != nil {
		t.Fatal(err)
	}

	// Get Habitat object from Habitat db example.
	db, err := utils.ConvertHabitat("resources/bind-config/db.yml")
	if err != nil {
		t.Fatal(err)
	}

	if err := framework.CreateHabitat(db); err != nil {
		t.Fatal(err)
	}

	// Get Service object from example file.
	svc, err := utils.ConvertService("resources/bind-config/service.yml")
	if err != nil {
		t.Fatal(err)
	}

	// Create Service.
	_, err = framework.KubeClient.CoreV1().Services(utils.TestNs).Create(svc)
	if err != nil {
		t.Fatal(err)
	}
	// Delete Service so it doesn't interfere with other tests.
	defer (func(name string) {
		if err := framework.DeleteService(name); err != nil {
			t.Fatal(err)
		}
	})(svc.Name)

	// Get Secret object from example file.
	sec, err := utils.ConvertSecret("resources/bind-config/secret.yml")
	if err != nil {
		t.Fatal(err)
	}

	// Create Secret.
	sec, err = framework.KubeClient.CoreV1().Secrets(utils.TestNs).Create(sec)
	if err != nil {
		t.Fatal(err)
	}

	// Wait for resources to be ready.
	if err := framework.WaitForResources(habv1beta1.HabitatNameLabel, web.ObjectMeta.Name, 1); err != nil {
		t.Fatal(err)
	}
	if err := framework.WaitForResources(habv1beta1.HabitatNameLabel, db.ObjectMeta.Name, 1); err != nil {
		t.Fatal(err)
	}

	// Wait until endpoints are ready.
	if err := framework.WaitForEndpoints(svc.ObjectMeta.Name); err != nil {
		t.Fatal(err)
	}

	time.Sleep(serviceStartupWaitTime)

	// Get response from Habitat Service.
	url := fmt.Sprintf("http://%s:30001/", framework.ExternalIP)

	body, err := utils.QueryService(url)
	if err != nil {
		t.Fatal(err)
	}

	// This msg is set in the config of the habitat/bindgo-hab Go Habitat Service.
	expectedMsg := "hello from port: 4444"
	actualMsg := body
	// actualMsg can contain whitespace and newlines or different formatting,
	// the only thing we need to check is it contains the expectedMsg.
	if !strings.Contains(actualMsg, expectedMsg) {
		t.Fatalf("Habitat Service msg does not match one in default.toml. Expected: \"%s\", got: \"%s\"", expectedMsg, actualMsg)
	}

	// Test `user.toml` updates.

	// Update secret.
	newPort := "port = 6333"

	sec.Data["user.toml"] = []byte(newPort)
	if _, err = framework.KubeClient.CoreV1().Secrets(utils.TestNs).Update(sec); err != nil {
		t.Fatalf("Could not update Secret: \"%s\"", err)
	}

	ticker := time.NewTicker(secretUpdateQueryTime)
	defer ticker.Stop()
	timer := time.NewTimer(secretUpdateTimeout)
	defer timer.Stop()
	// Update the message set in the config of the habitat/bindgo-hab Go Habitat Service.
	expectedMsg = fmt.Sprintf("hello from port: %v", 6333)
	for {
		// Check that the port differs after the update.
		actualMsg, err := utils.QueryService(url)
		if err != nil {
			t.Fatal(err)
		}

		// actualMsg can contain whitespace and newlines or different formatting,
		// the only thing we need to check is it contains the expectedMsg.
		if strings.Contains(actualMsg, expectedMsg) {
			break
		}

		fail := func() {
			t.Fatalf("Configuration update did not go through. Expected: \"%s\", got: \"%s\"", expectedMsg, actualMsg)
		}

		select {
		case <-timer.C:
			fail()
		case <-ticker.C:
			// This is to avoid infinite loops when go
			// decides to always pick the ticker channel,
			// even when timer channel is ready too.
			select {
			case <-timer.C:
				fail()
			default:
			}
		}
	}
}

// TestHabitatDelete tests Habitat deletion.
func TestHabitatDelete(t *testing.T) {
	// Get Habitat object from Habitat go example.
	habitat, err := utils.ConvertHabitat("resources/standalone/habitat.yml")
	if err != nil {
		t.Fatal(err)
	}

	if err := framework.CreateHabitat(habitat); err != nil {
		t.Fatal(err)
	}

	// Wait for resources to be ready.
	if err := framework.WaitForResources(habv1beta1.HabitatNameLabel, habitat.ObjectMeta.Name, 1); err != nil {
		t.Fatal(err)
	}

	// Delete Habitat.
	if err := framework.DeleteHabitat(habitat.ObjectMeta.Name); err != nil {
		t.Fatal(err)
	}

	// Wait for resources to be deleted.
	if err := framework.WaitForResources(habv1beta1.HabitatNameLabel, habitat.ObjectMeta.Name, 0); err != nil {
		t.Fatal(err)
	}

	// Check if all the resources the operator creates are deleted.
	// We do not care about secrets being deleted, as the user needs to delete those manually.
	d, err := framework.KubeClient.AppsV1beta1().Deployments(utils.TestNs).Get(habitat.ObjectMeta.Name, metav1.GetOptions{})
	if err == nil && d != nil {
		t.Fatal("Deployment was not deleted.")
	}

	// The CM with the peer IP should still be alive, despite the Habitat being deleted as it was created outside of the scope of a Habitat.
	_, err = framework.KubeClient.CoreV1().ConfigMaps(utils.TestNs).Get(configMapName, metav1.GetOptions{})
	if err != nil {
		t.Fatal(err)
	}
}

func TestPersistentStorage(t *testing.T) {
	// We run minikube in a VM on Travis. In that environment, we cannot create PersistentVolumes.
	t.Skip("This test cannot be run successfully in our current testing setup")

	ephemeral, err := utils.ConvertHabitat("resources/standalone/habitat.yml")
	if err != nil {
		t.Fatal(err)
	}

	persisted, err := utils.ConvertHabitat("resources/persisted/habitat.yml")
	if err != nil {
		t.Fatal(err)
	}

	if err := framework.CreateHabitat(ephemeral); err != nil {
		t.Fatal(err)
	}

	if err := framework.CreateHabitat(persisted); err != nil {
		t.Fatal(err)
	}

	// Delete all PVCs at the end of the test.
	// For dynamically provisioned PVs (as is the case on minikube), this will
	// also delete the PVs.
	defer (func(name string) {
		ls := labels.SelectorFromSet(labels.Set(map[string]string{
			habv1beta1.HabitatNameLabel: name,
		}))

		lo := metav1.ListOptions{
			LabelSelector: ls.String(),
		}

		err := framework.KubeClient.CoreV1().PersistentVolumeClaims(utils.TestNs).DeleteCollection(&metav1.DeleteOptions{}, lo)
		if err != nil {
			t.Fatal(err)
		}
	})(persisted.Name)

	if err := framework.WaitForResources(habv1beta1.HabitatNameLabel, persisted.Name, 1); err != nil {
		t.Fatal(err)
	}

	// Test that persistence is only enabled if requested
	ephemeralSTS, err := framework.KubeClient.AppsV1beta1().StatefulSets(utils.TestNs).Get(ephemeral.Name, metav1.GetOptions{})
	if err != nil {
		t.Fatal(err)
	}

	if len(ephemeralSTS.Spec.VolumeClaimTemplates) != 0 {
		t.Fatal("PersistentVolumeClaims created for ephemeral StatefulSet")
	}

	persistedSTS, err := framework.KubeClient.AppsV1beta1().StatefulSets(utils.TestNs).Get(persisted.Name, metav1.GetOptions{})
	if err != nil {
		t.Fatal(err)
	}

	if len(persistedSTS.Spec.VolumeClaimTemplates) == 0 {
		t.Fatal("No PersistentVolumeClaims created for persistent StatefulSet")
	}
}
<|endoftext|>


<commit_before><commit_msg>Add test to ensure fragments are compiled correctly.<commit_after><|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccDataSourceAwsTransferServer_basic(t *testing.T) {
	resourceName := "aws_transfer_server.test"
	datasourceName := "data.aws_transfer_server.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAwsTransferServerConfig_basic,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"),
					resource.TestCheckResourceAttrPair(datasourceName, "endpoint", resourceName, "endpoint"),
					resource.TestCheckResourceAttrPair(datasourceName, "identity_provider_type", resourceName, "identity_provider_type"),
					resource.TestCheckResourceAttrPair(datasourceName, "logging_role", resourceName, "logging_role"),
				),
			},
		},
	})
}

func TestAccDataSourceAwsTransferServer_service_managed(t *testing.T) {
	rName := acctest.RandString(5)
	resourceName := "aws_transfer_server.test"
	datasourceName := "data.aws_transfer_server.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAwsTransferServerConfig_service_managed(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"),
					resource.TestCheckResourceAttrPair(datasourceName, "endpoint", resourceName, "endpoint"),
					resource.TestCheckResourceAttrPair(datasourceName, "identity_provider_type", resourceName, "identity_provider_type"),
					resource.TestCheckResourceAttrPair(datasourceName, "logging_role", resourceName, "logging_role"),
				),
			},
		},
	})
}

func TestAccDataSourceAwsTransferServer_apigateway(t *testing.T) {
	rName := acctest.RandString(5)
	resourceName := "aws_transfer_server.test"
	datasourceName := "data.aws_transfer_server.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAwsTransferServerConfig_apigateway(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"),
					resource.TestCheckResourceAttrPair(datasourceName, "endpoint", resourceName, "endpoint"),
					resource.TestCheckResourceAttrPair(datasourceName, "identity_provider_type", resourceName, "identity_provider_type"),
					resource.TestCheckResourceAttrPair(datasourceName, "invocation_role", resourceName, "invocation_role"),
					resource.TestCheckResourceAttrPair(datasourceName, "logging_role", resourceName, "logging_role"),
					resource.TestCheckResourceAttrPair(datasourceName, "url", resourceName, "url"),
				),
			},
		},
	})
}

func testAccDataSourceAwsTransferServerCheck(datasourceName, resourceName string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[datasourceName]
		if !ok {
			return fmt.Errorf("root module has no resource called %s", datasourceName)
		}

		transferServerRs, ok := s.RootModule().Resources[resourceName]
		if !ok {
			return fmt.Errorf("root module has no resource called %s", resourceName)
		}

		attrNames := []string{
			"arn",
			"endpoint",
			"invocation_role",
			"url",
			"identity_provider_type",
			"logging_role",
		}

		for _, attrName := range attrNames {
			if rs.Primary.Attributes[attrName] != transferServerRs.Primary.Attributes[attrName] {
				return fmt.Errorf(
					"%s is %s; want %s",
					attrName,
					rs.Primary.Attributes[attrName],
					transferServerRs.Primary.Attributes[attrName],
				)
			}
		}

		return nil
	}
}

const testAccDataSourceAwsTransferServerConfig_basic = `
resource "aws_transfer_server" "test" {}

data "aws_transfer_server" "test" {
  server_id = "${aws_transfer_server.test.id}"
}
`

func testAccDataSourceAwsTransferServerConfig_service_managed(rName string) string {
	return fmt.Sprintf(`
resource "aws_iam_role" "test" {
	name = "tf-test-transfer-server-iam-role-%s"

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

resource "aws_iam_role_policy" "test" {
	name = "tf-test-transfer-server-iam-policy-%s"
	role = "${aws_iam_role.test.id}"
	policy = <<POLICY
{
	"Version": "2012-10-17",
	"Statement": [
		{
		"Sid": "AllowFullAccesstoCloudWatchLogs",
		"Effect": "Allow",
		"Action": [
			"logs:*"
		],
		"Resource": "*"
		}
	]
}
POLICY
}

resource "aws_transfer_server" "test" {
  identity_provider_type = "SERVICE_MANAGED"
  logging_role = "${aws_iam_role.test.arn}"
}

data "aws_transfer_server" "test" {
  server_id = "${aws_transfer_server.test.id}"
}
`, rName, rName)
}

func testAccDataSourceAwsTransferServerConfig_apigateway(rName string) string {
	return fmt.Sprintf(`
resource "aws_api_gateway_rest_api" "test" {
	name = "test"
}

resource "aws_api_gateway_resource" "test" {
	rest_api_id = "${aws_api_gateway_rest_api.test.id}"
	parent_id = "${aws_api_gateway_rest_api.test.root_resource_id}"
	path_part = "test"
}

resource "aws_api_gateway_method" "test" {
	rest_api_id = "${aws_api_gateway_rest_api.test.id}"
	resource_id = "${aws_api_gateway_resource.test.id}"
	http_method = "GET"
	authorization = "NONE"
}

resource "aws_api_gateway_method_response" "error" {
	rest_api_id = "${aws_api_gateway_rest_api.test.id}"
	resource_id = "${aws_api_gateway_resource.test.id}"
	http_method = "${aws_api_gateway_method.test.http_method}"
	status_code = "400"
}

resource "aws_api_gateway_integration" "test" {
	rest_api_id = "${aws_api_gateway_rest_api.test.id}"
	resource_id = "${aws_api_gateway_resource.test.id}"
	http_method = "${aws_api_gateway_method.test.http_method}"

	type = "HTTP"
	uri = "https://www.google.de"
	integration_http_method = "GET"
}

resource "aws_api_gateway_integration_response" "test" {
	rest_api_id = "${aws_api_gateway_rest_api.test.id}"
	resource_id = "${aws_api_gateway_resource.test.id}"
	http_method = "${aws_api_gateway_integration.test.http_method}"
	status_code = "${aws_api_gateway_method_response.error.status_code}"
}

resource "aws_api_gateway_deployment" "test" {
	depends_on = ["aws_api_gateway_integration.test"]

	rest_api_id = "${aws_api_gateway_rest_api.test.id}"
	stage_name = "test"
	description = "%s"
	stage_description = "%s"

  variables = {
    "a" = "2"
  }
}

resource "aws_iam_role" "test" {
	name = "tf-test-transfer-server-iam-role-for-apigateway-%s"

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

resource "aws_iam_role_policy" "test" {
	name = "tf-test-transfer-server-iam-policy-%s"
	role = "${aws_iam_role.test.id}"
	policy = <<POLICY
{
	"Version": "2012-10-17",
	"Statement": [
		{
		"Sid": "AllowFullAccesstoCloudWatchLogs",
		"Effect": "Allow",
		"Action": [
			"logs:*"
		],
		"Resource": "*"
		}
	]
}
POLICY
}

resource "aws_transfer_server" "test" {
	identity_provider_type	= "API_GATEWAY"
	url 				   	= "https://${aws_api_gateway_rest_api.test.id}.execute-api.us-west-2.amazonaws.com${aws_api_gateway_resource.test.path}"
	invocation_role 	   	= "${aws_iam_role.test.arn}"
	logging_role 		   	= "${aws_iam_role.test.arn}"
}

data "aws_transfer_server" "test" {
  server_id = "${aws_transfer_server.test.id}"
}
`, rName, rName, rName, rName)
}
<commit_msg>remove unused function<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
)

func TestAccDataSourceAwsTransferServer_basic(t *testing.T) {
	resourceName := "aws_transfer_server.test"
	datasourceName := "data.aws_transfer_server.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAwsTransferServerConfig_basic,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"),
					resource.TestCheckResourceAttrPair(datasourceName, "endpoint", resourceName, "endpoint"),
					resource.TestCheckResourceAttrPair(datasourceName, "identity_provider_type", resourceName, "identity_provider_type"),
					resource.TestCheckResourceAttrPair(datasourceName, "logging_role", resourceName, "logging_role"),
				),
			},
		},
	})
}

func TestAccDataSourceAwsTransferServer_service_managed(t *testing.T) {
	rName := acctest.RandString(5)
	resourceName := "aws_transfer_server.test"
	datasourceName := "data.aws_transfer_server.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAwsTransferServerConfig_service_managed(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"),
					resource.TestCheckResourceAttrPair(datasourceName, "endpoint", resourceName, "endpoint"),
					resource.TestCheckResourceAttrPair(datasourceName, "identity_provider_type", resourceName, "identity_provider_type"),
					resource.TestCheckResourceAttrPair(datasourceName, "logging_role", resourceName, "logging_role"),
				),
			},
		},
	})
}

func TestAccDataSourceAwsTransferServer_apigateway(t *testing.T) {
	rName := acctest.RandString(5)
	resourceName := "aws_transfer_server.test"
	datasourceName := "data.aws_transfer_server.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAwsTransferServerConfig_apigateway(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"),
					resource.TestCheckResourceAttrPair(datasourceName, "endpoint", resourceName, "endpoint"),
					resource.TestCheckResourceAttrPair(datasourceName, "identity_provider_type", resourceName, "identity_provider_type"),
					resource.TestCheckResourceAttrPair(datasourceName, "invocation_role", resourceName, "invocation_role"),
					resource.TestCheckResourceAttrPair(datasourceName, "logging_role", resourceName, "logging_role"),
					resource.TestCheckResourceAttrPair(datasourceName, "url", resourceName, "url"),
				),
			},
		},
	})
}

const testAccDataSourceAwsTransferServerConfig_basic = `
resource "aws_transfer_server" "test" {}

data "aws_transfer_server" "test" {
  server_id = "${aws_transfer_server.test.id}"
}
`

func testAccDataSourceAwsTransferServerConfig_service_managed(rName string) string {
	return fmt.Sprintf(`
resource "aws_iam_role" "test" {
	name = "tf-test-transfer-server-iam-role-%s"

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

resource "aws_iam_role_policy" "test" {
	name = "tf-test-transfer-server-iam-policy-%s"
	role = "${aws_iam_role.test.id}"
	policy = <<POLICY
{
	"Version": "2012-10-17",
	"Statement": [
		{
		"Sid": "AllowFullAccesstoCloudWatchLogs",
		"Effect": "Allow",
		"Action": [
			"logs:*"
		],
		"Resource": "*"
		}
	]
}
POLICY
}

resource "aws_transfer_server" "test" {
  identity_provider_type = "SERVICE_MANAGED"
  logging_role = "${aws_iam_role.test.arn}"
}

data "aws_transfer_server" "test" {
  server_id = "${aws_transfer_server.test.id}"
}
`, rName, rName)
}

func testAccDataSourceAwsTransferServerConfig_apigateway(rName string) string {
	return fmt.Sprintf(`
resource "aws_api_gateway_rest_api" "test" {
	name = "test"
}

resource "aws_api_gateway_resource" "test" {
	rest_api_id = "${aws_api_gateway_rest_api.test.id}"
	parent_id = "${aws_api_gateway_rest_api.test.root_resource_id}"
	path_part = "test"
}

resource "aws_api_gateway_method" "test" {
	rest_api_id = "${aws_api_gateway_rest_api.test.id}"
	resource_id = "${aws_api_gateway_resource.test.id}"
	http_method = "GET"
	authorization = "NONE"
}

resource "aws_api_gateway_method_response" "error" {
	rest_api_id = "${aws_api_gateway_rest_api.test.id}"
	resource_id = "${aws_api_gateway_resource.test.id}"
	http_method = "${aws_api_gateway_method.test.http_method}"
	status_code = "400"
}

resource "aws_api_gateway_integration" "test" {
	rest_api_id = "${aws_api_gateway_rest_api.test.id}"
	resource_id = "${aws_api_gateway_resource.test.id}"
	http_method = "${aws_api_gateway_method.test.http_method}"

	type = "HTTP"
	uri = "https://www.google.de"
	integration_http_method = "GET"
}

resource "aws_api_gateway_integration_response" "test" {
	rest_api_id = "${aws_api_gateway_rest_api.test.id}"
	resource_id = "${aws_api_gateway_resource.test.id}"
	http_method = "${aws_api_gateway_integration.test.http_method}"
	status_code = "${aws_api_gateway_method_response.error.status_code}"
}

resource "aws_api_gateway_deployment" "test" {
	depends_on = ["aws_api_gateway_integration.test"]

	rest_api_id = "${aws_api_gateway_rest_api.test.id}"
	stage_name = "test"
	description = "%s"
	stage_description = "%s"

  variables = {
    "a" = "2"
  }
}

resource "aws_iam_role" "test" {
	name = "tf-test-transfer-server-iam-role-for-apigateway-%s"

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

resource "aws_iam_role_policy" "test" {
	name = "tf-test-transfer-server-iam-policy-%s"
	role = "${aws_iam_role.test.id}"
	policy = <<POLICY
{
	"Version": "2012-10-17",
	"Statement": [
		{
		"Sid": "AllowFullAccesstoCloudWatchLogs",
		"Effect": "Allow",
		"Action": [
			"logs:*"
		],
		"Resource": "*"
		}
	]
}
POLICY
}

resource "aws_transfer_server" "test" {
	identity_provider_type	= "API_GATEWAY"
	url 				   	= "https://${aws_api_gateway_rest_api.test.id}.execute-api.us-west-2.amazonaws.com${aws_api_gateway_resource.test.path}"
	invocation_role 	   	= "${aws_iam_role.test.arn}"
	logging_role 		   	= "${aws_iam_role.test.arn}"
}

data "aws_transfer_server" "test" {
  server_id = "${aws_transfer_server.test.id}"
}
`, rName, rName, rName, rName)
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/sagemaker"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/sagemaker/finder"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/sagemaker/waiter"
)

func resourceAwsSagemakerImageVersion() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSagemakerImageVersionCreate,
		Read:   resourceAwsSagemakerImageVersionRead,
		Update: resourceAwsSagemakerImageVersionCreate,
		Delete: resourceAwsSagemakerImageVersionDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"base_image": {
				Type:     schema.TypeString,
				Required: true,
			},
			"container_image": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"image_arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"image_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"version": {
				Type:     schema.TypeInt,
				Computed: true,
			},
		},
	}
}

func resourceAwsSagemakerImageVersionCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).sagemakerconn

	name := d.Get("image_name").(string)
	input := &sagemaker.CreateImageVersionInput{
		ImageName: aws.String(name),
		BaseImage: aws.String(d.Get("base_image").(string)),
	}

	_, err := conn.CreateImageVersion(input)
	if err != nil {
		return fmt.Errorf("error creating Sagemaker Image Version %s: %w", name, err)
	}

	d.SetId(name)

	if _, err := waiter.ImageVersionCreated(conn, d.Id()); err != nil {
		return fmt.Errorf("error waiting for SageMaker Image Version (%s) to be created: %w", d.Id(), err)
	}

	return resourceAwsSagemakerImageVersionRead(d, meta)
}

func resourceAwsSagemakerImageVersionRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).sagemakerconn

	image, err := finder.ImageVersionByName(conn, d.Id())
	if err != nil {
		if isAWSErr(err, sagemaker.ErrCodeResourceNotFound, "does not exist") {
			d.SetId("")
			log.Printf("[WARN] Unable to find Sagemaker Image Version (%s); removing from state", d.Id())
			return nil
		}
		return fmt.Errorf("error reading Sagemaker Image Version (%s): %w", d.Id(), err)

	}

	d.Set("arn", image.ImageVersionArn)
	d.Set("base_image", image.BaseImage)
	d.Set("image_arn", image.ImageArn)
	d.Set("container_image", image.ContainerImage)
	d.Set("version", image.Version)
	d.Set("image_name", d.Id())

	return nil
}

func resourceAwsSagemakerImageVersionDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).sagemakerconn

	input := &sagemaker.DeleteImageVersionInput{
		ImageName: aws.String(d.Id()),
		Version:   aws.Int64(int64(d.Get("version").(int))),
	}

	if _, err := conn.DeleteImageVersion(input); err != nil {
		if isAWSErr(err, sagemaker.ErrCodeResourceNotFound, "does not exist") {
			return nil
		}
		return fmt.Errorf("error deleting Sagemaker Image Version (%s): %w", d.Id(), err)
	}

	if _, err := waiter.ImageVersionDeleted(conn, d.Id()); err != nil {
		if isAWSErr(err, sagemaker.ErrCodeResourceNotFound, "does not exist") {
			return nil
		}
		return fmt.Errorf("error waiting for SageMaker Image Version (%s) to delete: %w", d.Id(), err)
	}

	return nil
}
<commit_msg>remove update<commit_after>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/sagemaker"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/sagemaker/finder"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/sagemaker/waiter"
)

func resourceAwsSagemakerImageVersion() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSagemakerImageVersionCreate,
		Read:   resourceAwsSagemakerImageVersionRead,
		Delete: resourceAwsSagemakerImageVersionDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"base_image": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"container_image": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"image_arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"image_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"version": {
				Type:     schema.TypeInt,
				Computed: true,
			},
		},
	}
}

func resourceAwsSagemakerImageVersionCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).sagemakerconn

	name := d.Get("image_name").(string)
	input := &sagemaker.CreateImageVersionInput{
		ImageName: aws.String(name),
		BaseImage: aws.String(d.Get("base_image").(string)),
	}

	_, err := conn.CreateImageVersion(input)
	if err != nil {
		return fmt.Errorf("error creating Sagemaker Image Version %s: %w", name, err)
	}

	d.SetId(name)

	if _, err := waiter.ImageVersionCreated(conn, d.Id()); err != nil {
		return fmt.Errorf("error waiting for SageMaker Image Version (%s) to be created: %w", d.Id(), err)
	}

	return resourceAwsSagemakerImageVersionRead(d, meta)
}

func resourceAwsSagemakerImageVersionRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).sagemakerconn

	image, err := finder.ImageVersionByName(conn, d.Id())
	if err != nil {
		if isAWSErr(err, sagemaker.ErrCodeResourceNotFound, "does not exist") {
			d.SetId("")
			log.Printf("[WARN] Unable to find Sagemaker Image Version (%s); removing from state", d.Id())
			return nil
		}
		return fmt.Errorf("error reading Sagemaker Image Version (%s): %w", d.Id(), err)

	}

	d.Set("arn", image.ImageVersionArn)
	d.Set("base_image", image.BaseImage)
	d.Set("image_arn", image.ImageArn)
	d.Set("container_image", image.ContainerImage)
	d.Set("version", image.Version)
	d.Set("image_name", d.Id())

	return nil
}

func resourceAwsSagemakerImageVersionDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).sagemakerconn

	input := &sagemaker.DeleteImageVersionInput{
		ImageName: aws.String(d.Id()),
		Version:   aws.Int64(int64(d.Get("version").(int))),
	}

	if _, err := conn.DeleteImageVersion(input); err != nil {
		if isAWSErr(err, sagemaker.ErrCodeResourceNotFound, "does not exist") {
			return nil
		}
		return fmt.Errorf("error deleting Sagemaker Image Version (%s): %w", d.Id(), err)
	}

	if _, err := waiter.ImageVersionDeleted(conn, d.Id()); err != nil {
		if isAWSErr(err, sagemaker.ErrCodeResourceNotFound, "does not exist") {
			return nil
		}
		return fmt.Errorf("error waiting for SageMaker Image Version (%s) to delete: %w", d.Id(), err)
	}

	return nil
}
<|endoftext|>


<commit_before>package announcer

import (
	"context"
	"math"
	"net"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/tracker"
	"github.com/cenkalti/rain/internal/tracker/httptracker"
)

type Status int

const (
	NotContactedYet Status = iota
	Contacting
	Working
	NotWorking
)

type PeriodicalAnnouncer struct {
	Tracker       tracker.Tracker
	status        Status
	statsCommandC chan statsRequest
	numWant       int
	interval      time.Duration
	minInterval   time.Duration
	seeders       int
	leechers      int
	lastError     *AnnounceError
	log           logger.Logger
	completedC    chan struct{}
	newPeers      chan []*net.TCPAddr
	backoff       backoff.BackOff
	getTorrent    func() tracker.Torrent
	lastAnnounce  time.Time
	HasAnnounced  bool
	responseC     chan *tracker.AnnounceResponse
	errC          chan error
	closeC        chan struct{}
	doneC         chan struct{}

	needMorePeers  bool
	mNeedMorePeers sync.RWMutex
	needMorePeersC chan struct{}
}

func NewPeriodicalAnnouncer(trk tracker.Tracker, numWant int, minInterval time.Duration, getTorrent func() tracker.Torrent, completedC chan struct{}, newPeers chan []*net.TCPAddr, l logger.Logger) *PeriodicalAnnouncer {
	return &PeriodicalAnnouncer{
		Tracker:        trk,
		status:         NotContactedYet,
		statsCommandC:  make(chan statsRequest),
		numWant:        numWant,
		minInterval:    minInterval,
		log:            l,
		completedC:     completedC,
		newPeers:       newPeers,
		getTorrent:     getTorrent,
		needMorePeersC: make(chan struct{}, 1),
		responseC:      make(chan *tracker.AnnounceResponse),
		errC:           make(chan error),
		closeC:         make(chan struct{}),
		doneC:          make(chan struct{}),
		backoff: &backoff.ExponentialBackOff{
			InitialInterval:     5 * time.Second,
			RandomizationFactor: 0.5,
			Multiplier:          2,
			MaxInterval:         30 * time.Minute,
			MaxElapsedTime:      0, // never stop
			Clock:               backoff.SystemClock,
		},
	}
}

func (a *PeriodicalAnnouncer) Close() {
	close(a.closeC)
	<-a.doneC
}

type statsRequest struct {
	Response chan Stats
}

func (a *PeriodicalAnnouncer) Stats() Stats {
	var stats Stats
	req := statsRequest{Response: make(chan Stats, 1)}
	select {
	case a.statsCommandC <- req:
	case <-a.closeC:
	}
	select {
	case stats = <-req.Response:
	case <-a.closeC:
	}
	return stats
}

func (a *PeriodicalAnnouncer) NeedMorePeers(val bool) {
	a.mNeedMorePeers.Lock()
	a.needMorePeers = val
	a.mNeedMorePeers.Unlock()
	select {
	case a.needMorePeersC <- struct{}{}:
	case <-a.doneC:
	default:
	}
}

func (a *PeriodicalAnnouncer) Run() {
	defer close(a.doneC)
	a.backoff.Reset()

	timer := time.NewTimer(math.MaxInt64)
	defer timer.Stop()

	// BEP 0003: No completed is sent if the file was complete when started.
	select {
	case <-a.completedC:
		a.completedC = nil
	default:
	}

	ctx, cancel := context.WithCancel(context.Background())
	go a.announce(ctx, tracker.EventStarted, a.numWant)
	a.status = Contacting
	for {
		select {
		case <-timer.C:
			if a.status == Contacting {
				break
			}
			go a.announce(ctx, tracker.EventNone, a.numWant)
			a.status = Contacting
		case resp := <-a.responseC:
			a.status = Working
			a.lastAnnounce = time.Now()
			a.seeders = int(resp.Seeders)
			a.leechers = int(resp.Leechers)
			a.interval = resp.Interval
			if resp.MinInterval > 0 {
				a.minInterval = resp.MinInterval
			}
			a.HasAnnounced = true
			a.lastError = nil
			a.backoff.Reset()
			a.mNeedMorePeers.RLock()
			needMorePeers := a.needMorePeers
			a.mNeedMorePeers.RUnlock()
			if needMorePeers {
				timer.Reset(a.minInterval)
			} else {
				timer.Reset(a.interval)
			}
		case err := <-a.errC:
			a.status = NotWorking
			a.lastAnnounce = time.Now()
			// Give more friendly error to the user
			a.lastError = newAnnounceError(err)
			if a.lastError.Unknown {
				a.log.Errorln("announce error:", a.lastError.Err.Error())
			} else {
				a.log.Debugln("announce error:", a.lastError.Err.Error())
			}
			if terr, ok := a.lastError.Err.(*tracker.Error); ok && terr.RetryIn > 0 {
				timer.Reset(terr.RetryIn)
			} else {
				timer.Reset(a.backoff.NextBackOff())
			}
		case <-a.needMorePeersC:
			a.mNeedMorePeers.RLock()
			needMorePeers := a.needMorePeers
			a.mNeedMorePeers.RUnlock()
			if a.status == Contacting {
				break
			}
			if needMorePeers {
				timer.Reset(time.Until(a.lastAnnounce.Add(a.minInterval)))
			} else {
				timer.Reset(time.Until(a.lastAnnounce.Add(a.interval)))
			}
		case <-a.completedC:
			if a.status == Contacting {
				cancel()
				ctx, cancel = context.WithCancel(context.Background())
			}
			go a.announce(ctx, tracker.EventCompleted, 0)
			a.status = Contacting
			a.completedC = nil // do not send more than one "completed" event
		case req := <-a.statsCommandC:
			req.Response <- a.stats()
		case <-a.closeC:
			cancel()
			return
		}
	}
}

func (a *PeriodicalAnnouncer) announce(ctx context.Context, event tracker.Event, numWant int) {
	announce(ctx, a.Tracker, event, numWant, a.getTorrent(), a.responseC, a.errC)
}

type Stats struct {
	Status   Status
	Error    *AnnounceError
	Seeders  int
	Leechers int
}

func (a *PeriodicalAnnouncer) stats() Stats {
	return Stats{
		Status:   a.status,
		Error:    a.lastError,
		Seeders:  a.seeders,
		Leechers: a.leechers,
	}
}

type AnnounceError struct {
	Err     error
	Message string
	Unknown bool
}

func newAnnounceError(err error) *AnnounceError {
	e := &AnnounceError{Err: err}
	switch err := err.(type) {
	case *net.OpError:
		if err.Timeout() || err.Error() == "operation was canceled" {
			e.Message = "timeout contacting tracker"
			return e
		}
		// comes from UDP trackers
		if strings.HasSuffix(err.Error(), "no such host") {
			e.Message = "tracker host not found"
			return e
		}
	case *url.Error:
		if err.Timeout() {
			e.Message = "timeout contacting tracker"
			return e
		}
		// comes from HTTP trackers
		if strings.HasSuffix(err.Error(), "no such host") {
			e.Message = "tracker host not found"
			return e
		}
	case *httptracker.StatusError:
		if err.Code == 403 || err.Code == 404 {
			e.Message = "tracker returned http status: " + strconv.Itoa(err.Code)
			return e
		}
	case *tracker.Error:
		e.Message = "announce error: " + err.FailureReason
		return e
	}
	e.Message = "unknown error in announce"
	e.Unknown = true
	return e
}
<commit_msg>handle http connection refused error<commit_after>package announcer

import (
	"context"
	"math"
	"net"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/tracker"
	"github.com/cenkalti/rain/internal/tracker/httptracker"
)

type Status int

const (
	NotContactedYet Status = iota
	Contacting
	Working
	NotWorking
)

type PeriodicalAnnouncer struct {
	Tracker       tracker.Tracker
	status        Status
	statsCommandC chan statsRequest
	numWant       int
	interval      time.Duration
	minInterval   time.Duration
	seeders       int
	leechers      int
	lastError     *AnnounceError
	log           logger.Logger
	completedC    chan struct{}
	newPeers      chan []*net.TCPAddr
	backoff       backoff.BackOff
	getTorrent    func() tracker.Torrent
	lastAnnounce  time.Time
	HasAnnounced  bool
	responseC     chan *tracker.AnnounceResponse
	errC          chan error
	closeC        chan struct{}
	doneC         chan struct{}

	needMorePeers  bool
	mNeedMorePeers sync.RWMutex
	needMorePeersC chan struct{}
}

func NewPeriodicalAnnouncer(trk tracker.Tracker, numWant int, minInterval time.Duration, getTorrent func() tracker.Torrent, completedC chan struct{}, newPeers chan []*net.TCPAddr, l logger.Logger) *PeriodicalAnnouncer {
	return &PeriodicalAnnouncer{
		Tracker:        trk,
		status:         NotContactedYet,
		statsCommandC:  make(chan statsRequest),
		numWant:        numWant,
		minInterval:    minInterval,
		log:            l,
		completedC:     completedC,
		newPeers:       newPeers,
		getTorrent:     getTorrent,
		needMorePeersC: make(chan struct{}, 1),
		responseC:      make(chan *tracker.AnnounceResponse),
		errC:           make(chan error),
		closeC:         make(chan struct{}),
		doneC:          make(chan struct{}),
		backoff: &backoff.ExponentialBackOff{
			InitialInterval:     5 * time.Second,
			RandomizationFactor: 0.5,
			Multiplier:          2,
			MaxInterval:         30 * time.Minute,
			MaxElapsedTime:      0, // never stop
			Clock:               backoff.SystemClock,
		},
	}
}

func (a *PeriodicalAnnouncer) Close() {
	close(a.closeC)
	<-a.doneC
}

type statsRequest struct {
	Response chan Stats
}

func (a *PeriodicalAnnouncer) Stats() Stats {
	var stats Stats
	req := statsRequest{Response: make(chan Stats, 1)}
	select {
	case a.statsCommandC <- req:
	case <-a.closeC:
	}
	select {
	case stats = <-req.Response:
	case <-a.closeC:
	}
	return stats
}

func (a *PeriodicalAnnouncer) NeedMorePeers(val bool) {
	a.mNeedMorePeers.Lock()
	a.needMorePeers = val
	a.mNeedMorePeers.Unlock()
	select {
	case a.needMorePeersC <- struct{}{}:
	case <-a.doneC:
	default:
	}
}

func (a *PeriodicalAnnouncer) Run() {
	defer close(a.doneC)
	a.backoff.Reset()

	timer := time.NewTimer(math.MaxInt64)
	defer timer.Stop()

	// BEP 0003: No completed is sent if the file was complete when started.
	select {
	case <-a.completedC:
		a.completedC = nil
	default:
	}

	ctx, cancel := context.WithCancel(context.Background())
	go a.announce(ctx, tracker.EventStarted, a.numWant)
	a.status = Contacting
	for {
		select {
		case <-timer.C:
			if a.status == Contacting {
				break
			}
			go a.announce(ctx, tracker.EventNone, a.numWant)
			a.status = Contacting
		case resp := <-a.responseC:
			a.status = Working
			a.lastAnnounce = time.Now()
			a.seeders = int(resp.Seeders)
			a.leechers = int(resp.Leechers)
			a.interval = resp.Interval
			if resp.MinInterval > 0 {
				a.minInterval = resp.MinInterval
			}
			a.HasAnnounced = true
			a.lastError = nil
			a.backoff.Reset()
			a.mNeedMorePeers.RLock()
			needMorePeers := a.needMorePeers
			a.mNeedMorePeers.RUnlock()
			if needMorePeers {
				timer.Reset(a.minInterval)
			} else {
				timer.Reset(a.interval)
			}
		case err := <-a.errC:
			a.status = NotWorking
			a.lastAnnounce = time.Now()
			// Give more friendly error to the user
			a.lastError = newAnnounceError(err)
			if a.lastError.Unknown {
				a.log.Errorln("announce error:", a.lastError.Err.Error())
			} else {
				a.log.Debugln("announce error:", a.lastError.Err.Error())
			}
			if terr, ok := a.lastError.Err.(*tracker.Error); ok && terr.RetryIn > 0 {
				timer.Reset(terr.RetryIn)
			} else {
				timer.Reset(a.backoff.NextBackOff())
			}
		case <-a.needMorePeersC:
			a.mNeedMorePeers.RLock()
			needMorePeers := a.needMorePeers
			a.mNeedMorePeers.RUnlock()
			if a.status == Contacting {
				break
			}
			if needMorePeers {
				timer.Reset(time.Until(a.lastAnnounce.Add(a.minInterval)))
			} else {
				timer.Reset(time.Until(a.lastAnnounce.Add(a.interval)))
			}
		case <-a.completedC:
			if a.status == Contacting {
				cancel()
				ctx, cancel = context.WithCancel(context.Background())
			}
			go a.announce(ctx, tracker.EventCompleted, 0)
			a.status = Contacting
			a.completedC = nil // do not send more than one "completed" event
		case req := <-a.statsCommandC:
			req.Response <- a.stats()
		case <-a.closeC:
			cancel()
			return
		}
	}
}

func (a *PeriodicalAnnouncer) announce(ctx context.Context, event tracker.Event, numWant int) {
	announce(ctx, a.Tracker, event, numWant, a.getTorrent(), a.responseC, a.errC)
}

type Stats struct {
	Status   Status
	Error    *AnnounceError
	Seeders  int
	Leechers int
}

func (a *PeriodicalAnnouncer) stats() Stats {
	return Stats{
		Status:   a.status,
		Error:    a.lastError,
		Seeders:  a.seeders,
		Leechers: a.leechers,
	}
}

type AnnounceError struct {
	Err     error
	Message string
	Unknown bool
}

func newAnnounceError(err error) *AnnounceError {
	e := &AnnounceError{Err: err}
	switch err := err.(type) {
	case *net.OpError:
		if err.Timeout() || err.Error() == "operation was canceled" {
			e.Message = "timeout contacting tracker"
			return e
		}
		// comes from UDP trackers
		if strings.HasSuffix(err.Error(), "no such host") {
			e.Message = "tracker host not found"
			return e
		}
	case *url.Error:
		if err.Timeout() {
			e.Message = "timeout contacting tracker"
			return e
		}
		// comes from HTTP trackers
		errStr := err.Error()
		if strings.HasSuffix(errStr, "no such host") {
			e.Message = "tracker host not found"
			return e
		} else if strings.HasSuffix(errStr, "connection refused") {
			e.Message = "tracker refused the connection"
			return e
		}
	case *httptracker.StatusError:
		if err.Code == 403 || err.Code == 404 {
			e.Message = "tracker returned http status: " + strconv.Itoa(err.Code)
			return e
		}
	case *tracker.Error:
		e.Message = "announce error: " + err.FailureReason
		return e
	}
	e.Message = "unknown error in announce"
	e.Unknown = true
	return e
}
<|endoftext|>


<commit_before>package exec

import (
	"fmt"
	"reflect"
	"sort"
	"strings"

	"github.com/neelance/graphql-go/errors"
	"github.com/neelance/graphql-go/internal/query"
	"github.com/neelance/graphql-go/internal/schema"
)

var metaSchema *schema.Schema
var schemaExec iExec
var typeExec iExec

func init() {
	{
		var err *errors.GraphQLError
		metaSchema, err = schema.Parse(metaSchemaSrc)
		if err != nil {
			panic(err)
		}
	}

	{
		var err error
		schemaExec, err = makeWithType(metaSchema, metaSchema.Types["__Schema"], &schemaResolver{})
		if err != nil {
			panic(err)
		}
	}

	{
		var err error
		typeExec, err = makeWithType(metaSchema, metaSchema.Types["__Type"], &typeResolver{})
		if err != nil {
			panic(err)
		}
	}
}

func introspectSchema(r *request, selSet *query.SelectionSet) interface{} {
	return schemaExec.exec(r, selSet, reflect.ValueOf(&schemaResolver{r.schema}))
}

func introspectType(r *request, name string, selSet *query.SelectionSet) interface{} {
	t, ok := r.schema.Types[name]
	if !ok {
		return nil
	}
	return typeExec.exec(r, selSet, reflect.ValueOf(&typeResolver{typ: t}))
}

var metaSchemaSrc = `
	type __Schema {
		types: [__Type!]!
		queryType: __Type!
		mutationType: __Type
		directives: [__Directive!]!
	}

	type __Type {
		kind: __TypeKind!
		name: String
		description: String

		# OBJECT and INTERFACE only
		fields(includeDeprecated: Boolean = false): [__Field!]

		# OBJECT only
		interfaces: [__Type!]

		# INTERFACE and UNION only
		possibleTypes: [__Type!]

		# ENUM only
		enumValues(includeDeprecated: Boolean = false): [__EnumValue!]

		# INPUT_OBJECT only
		inputFields: [__InputValue!]

		# NON_NULL and LIST only
		ofType: __Type
	}

	type __Field {
		name: String!
		description: String
		args: [__InputValue!]!
		type: __Type!
		isDeprecated: Boolean!
		deprecationReason: String
	}

	type __InputValue {
		name: String!
		description: String
		type: __Type!
		defaultValue: String
	}

	type __EnumValue {
		name: String!
		description: String
		isDeprecated: Boolean!
		deprecationReason: String
	}

	enum __TypeKind {
		SCALAR
		OBJECT
		INTERFACE
		UNION
		ENUM
		INPUT_OBJECT
		LIST
		NON_NULL
	}

	type __Directive {
		name: String!
		description: String
		locations: [__DirectiveLocation!]!
		args: [__InputValue!]!
	}

	enum __DirectiveLocation {
		QUERY
		MUTATION
		FIELD
		FRAGMENT_DEFINITION
		FRAGMENT_SPREAD
		INLINE_FRAGMENT
	}
`

type schemaResolver struct {
	schema *schema.Schema
}

func (r *schemaResolver) Types() []*typeResolver {
	var l []*typeResolver
	addTypes := func(s *schema.Schema, metaOnly bool) {
		var names []string
		for name := range s.Types {
			if !metaOnly || strings.HasPrefix(name, "__") {
				names = append(names, name)
			}
		}
		sort.Strings(names)
		for _, name := range names {
			l = append(l, &typeResolver{s.Types[name]})
		}
	}
	addTypes(r.schema, false)
	addTypes(metaSchema, true)
	return l
}

func (r *schemaResolver) QueryType() *typeResolver {
	return &typeResolver{typ: r.schema.Types[r.schema.EntryPoints["query"]]}
}

func (r *schemaResolver) MutationType() *typeResolver {
	return &typeResolver{typ: r.schema.Types[r.schema.EntryPoints["mutation"]]}
}

func (r *schemaResolver) Directives() []*directiveResolver {
	return nil
}

type typeResolver struct {
	typ schema.Type
}

func (r *typeResolver) Kind() string {
	switch r.typ.(type) {
	case *schema.Scalar:
		return "SCALAR"
	case *schema.Object:
		return "OBJECT"
	case *schema.Interface:
		return "INTERFACE"
	case *schema.Union:
		return "UNION"
	case *schema.Enum:
		return "ENUM"
	case *schema.InputObject:
		return "INPUT_OBJECT"
	case *schema.List:
		return "LIST"
	case *schema.NonNull:
		return "NON_NULL"
	default:
		panic("unreachable")
	}
}

func (r *typeResolver) Name() *string {
	switch t := r.typ.(type) {
	case *schema.Scalar:
		return &t.Name
	case *schema.Object:
		return &t.Name
	case *schema.Interface:
		return &t.Name
	case *schema.Union:
		return &t.Name
	case *schema.Enum:
		return &t.Name
	case *schema.InputObject:
		return &t.Name
	default:
		return nil
	}
}

func (r *typeResolver) Description() string {
	return ""
}

func (r *typeResolver) Fields(args struct{ IncludeDeprecated bool }) *[]*fieldResolver {
	var fields map[string]*schema.Field
	var fieldOrder []string
	switch t := r.typ.(type) {
	case *schema.Object:
		fields = t.Fields
		fieldOrder = t.FieldOrder
	case *schema.Interface:
		fields = t.Fields
		fieldOrder = t.FieldOrder
	default:
		return nil
	}

	l := make([]*fieldResolver, len(fieldOrder))
	for i, name := range fieldOrder {
		l[i] = &fieldResolver{fields[name]}
	}
	return &l
}

func (r *typeResolver) Interfaces() *[]*typeResolver {
	t, ok := r.typ.(*schema.Object)
	if !ok {
		return nil
	}

	l := make([]*typeResolver, len(t.Interfaces))
	for i, intf := range t.Interfaces {
		l[i] = &typeResolver{intf}
	}
	return &l
}

func (r *typeResolver) PossibleTypes() *[]*typeResolver {
	var possibleTypes []*schema.Object
	switch t := r.typ.(type) {
	case *schema.Interface:
		possibleTypes = t.PossibleTypes
	case *schema.Union:
		possibleTypes = t.PossibleTypes
	default:
		return nil
	}

	l := make([]*typeResolver, len(possibleTypes))
	for i, intf := range possibleTypes {
		l[i] = &typeResolver{intf}
	}
	return &l
}

func (r *typeResolver) EnumValues(args struct{ IncludeDeprecated bool }) *[]*enumValueResolver {
	t, ok := r.typ.(*schema.Enum)
	if !ok {
		return nil
	}

	l := make([]*enumValueResolver, len(t.Values))
	for i, v := range t.Values {
		l[i] = &enumValueResolver{v}
	}
	return &l
}

func (r *typeResolver) InputFields() *[]*inputValueResolver {
	panic("TODO")
}

func (r *typeResolver) OfType() *typeResolver {
	switch t := r.typ.(type) {
	case *schema.List:
		return &typeResolver{t.OfType}
	case *schema.NonNull:
		return &typeResolver{t.OfType}
	default:
		return nil
	}
}

type fieldResolver struct {
	field *schema.Field
}

func (r *fieldResolver) Name() string {
	return r.field.Name
}

func (r *fieldResolver) Description() string {
	return ""
}

func (r *fieldResolver) Args() []*inputValueResolver {
	l := make([]*inputValueResolver, len(r.field.ArgOrder))
	for i, name := range r.field.ArgOrder {
		l[i] = &inputValueResolver{r.field.Args[name]}
	}
	return l
}

func (r *fieldResolver) Type() *typeResolver {
	return &typeResolver{typ: r.field.Type}
}

func (r *fieldResolver) IsDeprecated() bool {
	return false
}

func (r *fieldResolver) DeprecationReason() *string {
	return nil
}

type inputValueResolver struct {
	value *schema.InputValue
}

func (r *inputValueResolver) Name() string {
	return r.value.Name
}

func (r *inputValueResolver) Description() string {
	return ""
}

func (r *inputValueResolver) Type() *typeResolver {
	return &typeResolver{r.value.Type}
}

func (r *inputValueResolver) DefaultValue() *string {
	if r.value.Default == nil {
		return nil
	}
	s := fmt.Sprint(r.value.Default)
	return &s
}

type enumValueResolver struct {
	value string
}

func (r *enumValueResolver) Name() string {
	return r.value
}

func (r *enumValueResolver) Description() string {
	return ""
}

func (r *enumValueResolver) IsDeprecated() bool {
	return false
}

func (r *enumValueResolver) DeprecationReason() *string {
	return nil
}

type directiveResolver struct {
}

func (r *directiveResolver) Name() string {
	panic("TODO")
}

func (r *directiveResolver) Description() string {
	panic("TODO")
}

func (r *directiveResolver) Locations() []string {
	panic("TODO")
}

func (r *directiveResolver) Args() []*inputValueResolver {
	panic("TODO")
}
<commit_msg>introspection: inputFields<commit_after>package exec

import (
	"fmt"
	"reflect"
	"sort"
	"strings"

	"github.com/neelance/graphql-go/errors"
	"github.com/neelance/graphql-go/internal/query"
	"github.com/neelance/graphql-go/internal/schema"
)

var metaSchema *schema.Schema
var schemaExec iExec
var typeExec iExec

func init() {
	{
		var err *errors.GraphQLError
		metaSchema, err = schema.Parse(metaSchemaSrc)
		if err != nil {
			panic(err)
		}
	}

	{
		var err error
		schemaExec, err = makeWithType(metaSchema, metaSchema.Types["__Schema"], &schemaResolver{})
		if err != nil {
			panic(err)
		}
	}

	{
		var err error
		typeExec, err = makeWithType(metaSchema, metaSchema.Types["__Type"], &typeResolver{})
		if err != nil {
			panic(err)
		}
	}
}

func introspectSchema(r *request, selSet *query.SelectionSet) interface{} {
	return schemaExec.exec(r, selSet, reflect.ValueOf(&schemaResolver{r.schema}))
}

func introspectType(r *request, name string, selSet *query.SelectionSet) interface{} {
	t, ok := r.schema.Types[name]
	if !ok {
		return nil
	}
	return typeExec.exec(r, selSet, reflect.ValueOf(&typeResolver{typ: t}))
}

var metaSchemaSrc = `
	type __Schema {
		types: [__Type!]!
		queryType: __Type!
		mutationType: __Type
		directives: [__Directive!]!
	}

	type __Type {
		kind: __TypeKind!
		name: String
		description: String

		# OBJECT and INTERFACE only
		fields(includeDeprecated: Boolean = false): [__Field!]

		# OBJECT only
		interfaces: [__Type!]

		# INTERFACE and UNION only
		possibleTypes: [__Type!]

		# ENUM only
		enumValues(includeDeprecated: Boolean = false): [__EnumValue!]

		# INPUT_OBJECT only
		inputFields: [__InputValue!]

		# NON_NULL and LIST only
		ofType: __Type
	}

	type __Field {
		name: String!
		description: String
		args: [__InputValue!]!
		type: __Type!
		isDeprecated: Boolean!
		deprecationReason: String
	}

	type __InputValue {
		name: String!
		description: String
		type: __Type!
		defaultValue: String
	}

	type __EnumValue {
		name: String!
		description: String
		isDeprecated: Boolean!
		deprecationReason: String
	}

	enum __TypeKind {
		SCALAR
		OBJECT
		INTERFACE
		UNION
		ENUM
		INPUT_OBJECT
		LIST
		NON_NULL
	}

	type __Directive {
		name: String!
		description: String
		locations: [__DirectiveLocation!]!
		args: [__InputValue!]!
	}

	enum __DirectiveLocation {
		QUERY
		MUTATION
		FIELD
		FRAGMENT_DEFINITION
		FRAGMENT_SPREAD
		INLINE_FRAGMENT
	}
`

type schemaResolver struct {
	schema *schema.Schema
}

func (r *schemaResolver) Types() []*typeResolver {
	var l []*typeResolver
	addTypes := func(s *schema.Schema, metaOnly bool) {
		var names []string
		for name := range s.Types {
			if !metaOnly || strings.HasPrefix(name, "__") {
				names = append(names, name)
			}
		}
		sort.Strings(names)
		for _, name := range names {
			l = append(l, &typeResolver{s.Types[name]})
		}
	}
	addTypes(r.schema, false)
	addTypes(metaSchema, true)
	return l
}

func (r *schemaResolver) QueryType() *typeResolver {
	return &typeResolver{typ: r.schema.Types[r.schema.EntryPoints["query"]]}
}

func (r *schemaResolver) MutationType() *typeResolver {
	return &typeResolver{typ: r.schema.Types[r.schema.EntryPoints["mutation"]]}
}

func (r *schemaResolver) Directives() []*directiveResolver {
	return nil
}

type typeResolver struct {
	typ schema.Type
}

func (r *typeResolver) Kind() string {
	switch r.typ.(type) {
	case *schema.Scalar:
		return "SCALAR"
	case *schema.Object:
		return "OBJECT"
	case *schema.Interface:
		return "INTERFACE"
	case *schema.Union:
		return "UNION"
	case *schema.Enum:
		return "ENUM"
	case *schema.InputObject:
		return "INPUT_OBJECT"
	case *schema.List:
		return "LIST"
	case *schema.NonNull:
		return "NON_NULL"
	default:
		panic("unreachable")
	}
}

func (r *typeResolver) Name() *string {
	switch t := r.typ.(type) {
	case *schema.Scalar:
		return &t.Name
	case *schema.Object:
		return &t.Name
	case *schema.Interface:
		return &t.Name
	case *schema.Union:
		return &t.Name
	case *schema.Enum:
		return &t.Name
	case *schema.InputObject:
		return &t.Name
	default:
		return nil
	}
}

func (r *typeResolver) Description() string {
	return ""
}

func (r *typeResolver) Fields(args struct{ IncludeDeprecated bool }) *[]*fieldResolver {
	var fields map[string]*schema.Field
	var fieldOrder []string
	switch t := r.typ.(type) {
	case *schema.Object:
		fields = t.Fields
		fieldOrder = t.FieldOrder
	case *schema.Interface:
		fields = t.Fields
		fieldOrder = t.FieldOrder
	default:
		return nil
	}

	l := make([]*fieldResolver, len(fieldOrder))
	for i, name := range fieldOrder {
		l[i] = &fieldResolver{fields[name]}
	}
	return &l
}

func (r *typeResolver) Interfaces() *[]*typeResolver {
	t, ok := r.typ.(*schema.Object)
	if !ok {
		return nil
	}

	l := make([]*typeResolver, len(t.Interfaces))
	for i, intf := range t.Interfaces {
		l[i] = &typeResolver{intf}
	}
	return &l
}

func (r *typeResolver) PossibleTypes() *[]*typeResolver {
	var possibleTypes []*schema.Object
	switch t := r.typ.(type) {
	case *schema.Interface:
		possibleTypes = t.PossibleTypes
	case *schema.Union:
		possibleTypes = t.PossibleTypes
	default:
		return nil
	}

	l := make([]*typeResolver, len(possibleTypes))
	for i, intf := range possibleTypes {
		l[i] = &typeResolver{intf}
	}
	return &l
}

func (r *typeResolver) EnumValues(args struct{ IncludeDeprecated bool }) *[]*enumValueResolver {
	t, ok := r.typ.(*schema.Enum)
	if !ok {
		return nil
	}

	l := make([]*enumValueResolver, len(t.Values))
	for i, v := range t.Values {
		l[i] = &enumValueResolver{v}
	}
	return &l
}

func (r *typeResolver) InputFields() *[]*inputValueResolver {
	t, ok := r.typ.(*schema.InputObject)
	if !ok {
		return nil
	}

	l := make([]*inputValueResolver, len(t.InputFieldOrder))
	for i, name := range t.InputFieldOrder {
		l[i] = &inputValueResolver{t.InputFields[name]}
	}
	return &l
}

func (r *typeResolver) OfType() *typeResolver {
	switch t := r.typ.(type) {
	case *schema.List:
		return &typeResolver{t.OfType}
	case *schema.NonNull:
		return &typeResolver{t.OfType}
	default:
		return nil
	}
}

type fieldResolver struct {
	field *schema.Field
}

func (r *fieldResolver) Name() string {
	return r.field.Name
}

func (r *fieldResolver) Description() string {
	return ""
}

func (r *fieldResolver) Args() []*inputValueResolver {
	l := make([]*inputValueResolver, len(r.field.ArgOrder))
	for i, name := range r.field.ArgOrder {
		l[i] = &inputValueResolver{r.field.Args[name]}
	}
	return l
}

func (r *fieldResolver) Type() *typeResolver {
	return &typeResolver{typ: r.field.Type}
}

func (r *fieldResolver) IsDeprecated() bool {
	return false
}

func (r *fieldResolver) DeprecationReason() *string {
	return nil
}

type inputValueResolver struct {
	value *schema.InputValue
}

func (r *inputValueResolver) Name() string {
	return r.value.Name
}

func (r *inputValueResolver) Description() string {
	return ""
}

func (r *inputValueResolver) Type() *typeResolver {
	return &typeResolver{r.value.Type}
}

func (r *inputValueResolver) DefaultValue() *string {
	if r.value.Default == nil {
		return nil
	}
	s := fmt.Sprint(r.value.Default)
	return &s
}

type enumValueResolver struct {
	value string
}

func (r *enumValueResolver) Name() string {
	return r.value
}

func (r *enumValueResolver) Description() string {
	return ""
}

func (r *enumValueResolver) IsDeprecated() bool {
	return false
}

func (r *enumValueResolver) DeprecationReason() *string {
	return nil
}

type directiveResolver struct {
}

func (r *directiveResolver) Name() string {
	panic("TODO")
}

func (r *directiveResolver) Description() string {
	panic("TODO")
}

func (r *directiveResolver) Locations() []string {
	panic("TODO")
}

func (r *directiveResolver) Args() []*inputValueResolver {
	panic("TODO")
}
<|endoftext|>


<commit_before>package runtime

import (
	"net/http"
	"strings"

	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
)

const metadataHeaderPrefix = "Grpc-Metadata-"
const metadataTrailerPrefix = "Grpc-Trailer-"

/*
AnnotateContext adds context information such as metadata from the request.

If there are no metadata headers in the request, then the context returned
will be the same context.
*/
func AnnotateContext(ctx context.Context, req *http.Request) context.Context {
	var pairs []string
	for key, vals := range req.Header {
		for _, val := range vals {
			if strings.ToLower(key) == "authorization" {
				pairs = append(pairs, key, val)
				continue
			}
			if strings.HasPrefix(key, metadataHeaderPrefix) {
				pairs = append(pairs, key[len(metadataHeaderPrefix):], val)
			}
		}
	}

	if len(pairs) == 0 {
		return ctx
	}
	return metadata.NewContext(ctx, metadata.Pairs(pairs...))
}

// ServerMetadata consists of metadata sent from gRPC server.
type ServerMetadata struct {
	HeaderMD  metadata.MD
	TrailerMD metadata.MD
}

type serverMetadataKey struct{}

// NewServerMetadataContext creates a new context with ServerMetadata
func NewServerMetadataContext(ctx context.Context, md ServerMetadata) context.Context {
	return context.WithValue(ctx, serverMetadataKey{}, md)
}

// ServerMetadataFromContext returns the ServerMetadata in ctx
func ServerMetadataFromContext(ctx context.Context) (md ServerMetadata, ok bool) {
	md, ok = ctx.Value(serverMetadataKey{}).(ServerMetadata)
	return
}
<commit_msg>Include HTTP req.remoteAddr in gRPC ctx<commit_after>package runtime

import (
	"net/http"
	"strings"

	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
)

const metadataHeaderPrefix = "Grpc-Metadata-"
const metadataTrailerPrefix = "Grpc-Trailer-"

/*
AnnotateContext adds context information such as metadata from the request.

At a minimum, the RemoteAddr is included in the fashion of "X-Forwarded-For",
except that the forwarded destination is not another HTTP service but rather
a gRPC service.
*/
func AnnotateContext(ctx context.Context, req *http.Request) context.Context {
	var pairs []string

	// Include the original RemoteAddr, something the gRPC service may need in the ctx
	pairs = append(pairs, "RemoteAddr", req.RemoteAddr)

	for key, vals := range req.Header {
		for _, val := range vals {
			if key == "Authorization" {
				pairs = append(pairs, key, val)
				continue
			}
			if strings.HasPrefix(key, metadataHeaderPrefix) {
				pairs = append(pairs, key[len(metadataHeaderPrefix):], val)
			}
		}
	}

	if len(pairs) == 0 {
		return ctx
	}
	return metadata.NewContext(ctx, metadata.Pairs(pairs...))
}

// ServerMetadata consists of metadata sent from gRPC server.
type ServerMetadata struct {
	HeaderMD  metadata.MD
	TrailerMD metadata.MD
}

type serverMetadataKey struct{}

// NewServerMetadataContext creates a new context with ServerMetadata
func NewServerMetadataContext(ctx context.Context, md ServerMetadata) context.Context {
	return context.WithValue(ctx, serverMetadataKey{}, md)
}

// ServerMetadataFromContext returns the ServerMetadata in ctx
func ServerMetadataFromContext(ctx context.Context) (md ServerMetadata, ok bool) {
	md, ok = ctx.Value(serverMetadataKey{}).(ServerMetadata)
	return
}
<|endoftext|>


<commit_before>package interpolation

type Func func(float64) float64
<commit_msg>add 2d func<commit_after>package interpolation

type (
	Func  func(float64) float64
	Func2 func(x, y float64) float64
)
<|endoftext|>


<commit_before>package godirwalk

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

var rootDir string

func TestMain(m *testing.M) {
	flag.Parse()

	// All tests use the same directory test scaffolding.  Create the directory
	// hierarchy, run the tests, then remove the root directory of the test
	// scaffolding.
	if err := setup(); err != nil {
		fmt.Fprintf(os.Stderr, "setup: %s\n", err)
		os.Exit(1)
	}

	code := m.Run()

	if code != 0 {
		// When any test was a failure, then use standard library to walk test
		// scaffolding directory and print its contents.
		trim := len(rootDir) // trim rootDir from prefix of strings
		err := filepath.Walk(rootDir, func(osPathname string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			osPathname = osPathname[trim:]
			_, err = fmt.Fprintf(os.Stderr, "%s %s\n", info.Mode(), osPathname)
			return err
		})
		if err != nil {
			fmt.Fprintf(os.Stderr, "cannot walk test directory: %s\n", err)
		}
	}

	if err := teardown(); err != nil {
		fmt.Fprintf(os.Stderr, "teardown: %s\n", err)
		os.Exit(1)
	}

	os.Exit(code)
}

func setup() error {
	var err error

	rootDir, err = ioutil.TempDir(os.TempDir(), "godirwalk-")
	if err != nil {
		return err
	}

	// Create files, creating parent directories along the way.
	files := []string{
		"dir1/dir1a/file1a1",
		"dir1/dir1a/skip",
		"dir1/dir1a/z1a2",
		"dir1/file1b",
		"dir2/file2a",
		"dir2/skip/file2b1",
		"dir2/z2c/file2c1",
		"dir3/aaa.txt",
		"dir3/zzz/aaa.txt",
		"dir4/aaa.txt",
		"dir4/zzz/aaa.txt",
		"dir5/a1.txt",
		"dir5/a2/a2a/a2a1.txt",
		"dir5/a2/a2b.txt",
		"dir6/bravo.txt",
		"dir6/code/123.txt",
		"dir7/z",
		"file3",
	}

	for _, pathname := range files {
		pathname = filepath.Join(rootDir, filepath.FromSlash(pathname))

		if err := os.MkdirAll(filepath.Dir(pathname), os.ModePerm); err != nil {
			return fmt.Errorf("cannot create directory for test scaffolding: %s", err)
		}

		if err = ioutil.WriteFile(pathname, []byte("some test data\n"), os.ModePerm); err != nil {
			return fmt.Errorf("cannot create file for test scaffolding: %s", err)
		}
	}

	symlinks := []struct {
		newname, oldname string
	}{
		{"dir3/skip", "zzz"},
		{"dir4/symlinkToDirectory", "zzz"},
		{"dir4/symlinkToFile", "aaa.txt"},
		{"dir7/b/y", "../z"},
		{"dir7/a/x", "../b"},
		{"symlinks/dir-symlink", "../symlinks"}, // infinite loop of symlinks
		{"symlinks/file-symlink", "../file3"},
		{"symlinks/invalid-symlink", "/non/existing/file"},
	}

	for _, entry := range symlinks {
		newname := filepath.Join(rootDir, filepath.FromSlash(entry.newname))

		if err := os.MkdirAll(filepath.Dir(newname), os.ModePerm); err != nil {
			return fmt.Errorf("cannot create directory for test scaffolding: %s", err)
		}

		oldname := filepath.FromSlash(entry.oldname)

		if err := os.Symlink(oldname, newname); err != nil {
			return fmt.Errorf("cannot create symbolic link for test scaffolding: %s", err)
		}
	}

	extraDirs := []string{
		"dir6/abc",
		"dir6/def",
	}

	for _, pathname := range extraDirs {
		pathname = filepath.Join(rootDir, filepath.FromSlash(pathname))

		if err := os.MkdirAll(pathname, os.ModePerm); err != nil {
			return fmt.Errorf("cannot create directory for test scaffolding: %s", err)
		}
	}

	if err := os.MkdirAll(filepath.Join(rootDir, filepath.FromSlash("dir6/noaccess")), os.FileMode(0)); err != nil {
		return fmt.Errorf("cannot create directory for test scaffolding: %s", err)
	}

	return nil
}

func teardown() error {
	if err := os.Chmod(filepath.Join(rootDir, filepath.FromSlash("dir6/noaccess")), os.ModePerm); err != nil {
		return fmt.Errorf("cannot change permission to delete dir6/noaccess for test scaffolding: %s", err)
	}
	if err := os.RemoveAll(rootDir); err != nil {
		return err
	}
	return nil
}
<commit_msg>better print out of file system<commit_after>package godirwalk

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

var rootDir string

func TestMain(m *testing.M) {
	flag.Parse()

	// All tests use the same directory test scaffolding.  Create the directory
	// hierarchy, run the tests, then remove the root directory of the test
	// scaffolding.
	if err := setup(); err != nil {
		fmt.Fprintf(os.Stderr, "setup: %s\n", err)
		os.Exit(1)
	}

	code := m.Run()

	if true || code != 0 {
		// When any test was a failure, then use standard library to walk test
		// scaffolding directory and print its contents.
		trim := len(rootDir) // trim rootDir from prefix of strings
		err := filepath.Walk(rootDir, func(osPathname string, info os.FileInfo, err error) error {
			osPathname = osPathname[trim:]
			if err != nil {
				fmt.Fprintf(os.Stderr, "---------- %s: %s\n", osPathname, err)
			} else {
				fmt.Fprintf(os.Stderr, "%s %s\n", info.Mode(), osPathname)
			}
			return nil
		})
		if err != nil {
			fmt.Fprintf(os.Stderr, "cannot walk test directory: %s\n", err)
		}
	}

	if err := teardown(); err != nil {
		fmt.Fprintf(os.Stderr, "teardown: %s\n", err)
		os.Exit(1)
	}

	os.Exit(code)
}

func setup() error {
	var err error

	rootDir, err = ioutil.TempDir(os.TempDir(), "godirwalk-")
	if err != nil {
		return err
	}

	// Create files, creating parent directories along the way.
	files := []string{
		"dir1/dir1a/file1a1",
		"dir1/dir1a/skip",
		"dir1/dir1a/z1a2",
		"dir1/file1b",
		"dir2/file2a",
		"dir2/skip/file2b1",
		"dir2/z2c/file2c1",
		"dir3/aaa.txt",
		"dir3/zzz/aaa.txt",
		"dir4/aaa.txt",
		"dir4/zzz/aaa.txt",
		"dir5/a1.txt",
		"dir5/a2/a2a/a2a1.txt",
		"dir5/a2/a2b.txt",
		"dir6/bravo.txt",
		"dir6/code/123.txt",
		"dir7/z",
		"file3",
	}

	for _, pathname := range files {
		pathname = filepath.Join(rootDir, filepath.FromSlash(pathname))

		if err := os.MkdirAll(filepath.Dir(pathname), os.ModePerm); err != nil {
			return fmt.Errorf("cannot create directory for test scaffolding: %s", err)
		}

		if err = ioutil.WriteFile(pathname, []byte("some test data\n"), os.ModePerm); err != nil {
			return fmt.Errorf("cannot create file for test scaffolding: %s", err)
		}
	}

	symlinks := []struct {
		newname, oldname string
	}{
		{"dir3/skip", "zzz"},
		{"dir4/symlinkToDirectory", "zzz"},
		{"dir4/symlinkToFile", "aaa.txt"},
		{"dir7/b/y", "../z"},
		{"dir7/a/x", "../b"},
		{"symlinks/dir-symlink", "../symlinks"}, // infinite loop of symlinks
		{"symlinks/file-symlink", "../file3"},
		{"symlinks/invalid-symlink", "/non/existing/file"},
	}

	for _, entry := range symlinks {
		newname := filepath.Join(rootDir, filepath.FromSlash(entry.newname))

		if err := os.MkdirAll(filepath.Dir(newname), os.ModePerm); err != nil {
			return fmt.Errorf("cannot create directory for test scaffolding: %s", err)
		}

		oldname := filepath.FromSlash(entry.oldname)

		if err := os.Symlink(oldname, newname); err != nil {
			return fmt.Errorf("cannot create symbolic link for test scaffolding: %s", err)
		}
	}

	extraDirs := []string{
		"dir6/abc",
		"dir6/def",
	}

	for _, pathname := range extraDirs {
		pathname = filepath.Join(rootDir, filepath.FromSlash(pathname))

		if err := os.MkdirAll(pathname, os.ModePerm); err != nil {
			return fmt.Errorf("cannot create directory for test scaffolding: %s", err)
		}
	}

	if err := os.MkdirAll(filepath.Join(rootDir, filepath.FromSlash("dir6/noaccess")), os.FileMode(0)); err != nil {
		return fmt.Errorf("cannot create directory for test scaffolding: %s", err)
	}

	return nil
}

func teardown() error {
	if err := os.Chmod(filepath.Join(rootDir, filepath.FromSlash("dir6/noaccess")), os.ModePerm); err != nil {
		return fmt.Errorf("cannot change permission to delete dir6/noaccess for test scaffolding: %s", err)
	}
	if err := os.RemoveAll(rootDir); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Riviera is a feed aggregator.
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"time"

	"gopkg.in/fsnotify.v1"
	"hawx.me/code/riviera/river"
	"hawx.me/code/riviera/river/data"
	"hawx.me/code/riviera/river/data/boltdata"
	"hawx.me/code/riviera/river/data/memdata"
	"hawx.me/code/riviera/subscriptions"
	"hawx.me/code/riviera/subscriptions/opml"
	"hawx.me/code/serve"
)

func printHelp() {
	fmt.Println(`Usage: riviera [options] FILE

  Riviera is a feed aggregator. It reads a list of feeds in OPML
  subscription list format (http://dev.opml.org/spec2.html) given
  as FILE, polls these feeds at a customisable interval, and serves
  a riverjs (http://riverjs.org) format document at '/river'.

  A json list of fetch events is served at '/river/log'

  Changes to FILE are watched and will modify the feeds watched, if it
  can be successfully parsed.

 DISPLAY
   --cutoff DUR='-24h'
      Time to ignore items after, given in standard go duration format
      (see http://golang.org/pkg/time/#ParseDuration).

   --refresh DUR='15m'
      Time to refresh feeds after. This is the default used, but if
      advice is given in the feed itself it may be ignored.

 DATA
   By default riviera runs with an in memory database.

   --boltdb PATH
      Use the boltdb file at the given path.

 SERVE
   --port PORT='8080'
      Serve on given port.

   --socket SOCK
      Serve at given socket, instead.
`)
}

var (
	cutOff  = flag.String("cutoff", "-24h", "")
	refresh = flag.String("refresh", "15m", "")

	boltdbPath = flag.String("boltdb", "", "")

	port   = flag.String("port", "8080", "")
	socket = flag.String("socket", "", "")

	help = flag.Bool("help", false, "")
)

// DefaultCallback is the name of the callback to use in the jsonp response.
const DefaultCallback = "onGetRiverStream"

func loadDatastore() (data.Database, error) {
	if *boltdbPath != "" {
		return boltdata.Open(*boltdbPath)
	}

	return memdata.Open(), nil
}

func watchFile(path string, f func()) (io.Closer, error) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return watcher, err
	}

	go func() {
		for {
			select {
			case event := <-watcher.Events:
				if event.Op&fsnotify.Write == fsnotify.Write {
					f()
				}
			case err := <-watcher.Errors:
				log.Println("error:", err)
			}
		}
	}()

	return watcher, watcher.Add(path)
}

type riverHandler struct {
	river.River
}

func newRiverHandler(feeds river.River) http.Handler {
	return &riverHandler{feeds}
}

func (h *riverHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if strings.ToUpper(r.Method) != "GET" {
		w.Header().Set("Accept", "GET")
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	switch r.URL.Path {
	case "/":
		w.Header().Set("Content-Type", "application/javascript")
		fmt.Fprintf(w, "%s(", DefaultCallback)
		if err := h.WriteTo(w); err != nil {
			log.Println("/:", err)
		}
		fmt.Fprintf(w, ")")

	case "/log":
		w.Header().Set("Content-Type", "application/json")
		if err := json.NewEncoder(w).Encode(h.Log()); err != nil {
			log.Println("/log:", err)
		}

	default:
		http.NotFound(w, r)
	}
}

func main() {
	flag.Parse()

	if *help || flag.NArg() == 0 {
		printHelp()
		return
	}

	opmlPath := flag.Arg(0)

	duration, err := time.ParseDuration(*cutOff)
	if err != nil {
		log.Fatal(err)
	}

	cacheTimeout, err := time.ParseDuration(*refresh)
	if err != nil {
		log.Fatal(err)
	}

	store, err := loadDatastore()
	if err != nil {
		log.Fatal(err)
	}
	defer store.Close()

	outline, err := opml.Load(opmlPath)
	if err != nil {
		log.Fatal(err)
	}

	subs := subscriptions.FromOpml(outline)

	feeds := river.New(store, river.Options{
		Mapping:   river.DefaultMapping,
		CutOff:    duration,
		Refresh:   cacheTimeout,
		LogLength: 500,
	})

	for _, sub := range subs.List() {
		feeds.Add(sub.Uri)
	}

	updateSubs := func() {
		log.Printf("reading %s\n", opmlPath)
		outline, err := opml.Load(opmlPath)
		if err != nil {
			log.Printf("could not read %s: %s\n", opmlPath, err)
			return
		}

		newsubs := subscriptions.FromOpml(outline)

		for _, change := range subscriptions.Diff(subs, newsubs) {
			switch change.Type {
			case subscriptions.Removed:
				feeds.Remove(change.Uri)
				subs.Remove(change.Uri)
			case subscriptions.Added:
				feeds.Add(change.Uri)
				subs.Add(change.Uri)
			}
		}
	}

	http.Handle("/river/", http.StripPrefix("/river", newRiverHandler(feeds)))

	watcher, err := watchFile(opmlPath, updateSubs)
	if err != nil {
		log.Printf("could not start watching %s: %v\n", opmlPath, err)
	}
	defer watcher.Close()

	serve.Serve(*port, *socket, http.DefaultServeMux)
}
<commit_msg>Replace log.Fatal usages with Println then return<commit_after>// Riviera is a feed aggregator.
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"time"

	"gopkg.in/fsnotify.v1"
	"hawx.me/code/riviera/river"
	"hawx.me/code/riviera/river/data"
	"hawx.me/code/riviera/river/data/boltdata"
	"hawx.me/code/riviera/river/data/memdata"
	"hawx.me/code/riviera/subscriptions"
	"hawx.me/code/riviera/subscriptions/opml"
	"hawx.me/code/serve"
)

func printHelp() {
	fmt.Println(`Usage: riviera [options] FILE

  Riviera is a feed aggregator. It reads a list of feeds in OPML
  subscription list format (http://dev.opml.org/spec2.html) given
  as FILE, polls these feeds at a customisable interval, and serves
  a riverjs (http://riverjs.org) format document at '/river'.

  A json list of fetch events is served at '/river/log'

  Changes to FILE are watched and will modify the feeds watched, if it
  can be successfully parsed.

 DISPLAY
   --cutoff DUR='-24h'
      Time to ignore items after, given in standard go duration format
      (see http://golang.org/pkg/time/#ParseDuration).

   --refresh DUR='15m'
      Time to refresh feeds after. This is the default used, but if
      advice is given in the feed itself it may be ignored.

 DATA
   By default riviera runs with an in memory database.

   --boltdb PATH
      Use the boltdb file at the given path.

 SERVE
   --port PORT='8080'
      Serve on given port.

   --socket SOCK
      Serve at given socket, instead.
`)
}

var (
	cutOff  = flag.String("cutoff", "-24h", "")
	refresh = flag.String("refresh", "15m", "")

	boltdbPath = flag.String("boltdb", "", "")

	port   = flag.String("port", "8080", "")
	socket = flag.String("socket", "", "")

	help = flag.Bool("help", false, "")
)

// DefaultCallback is the name of the callback to use in the jsonp response.
const DefaultCallback = "onGetRiverStream"

func loadDatastore() (data.Database, error) {
	if *boltdbPath != "" {
		return boltdata.Open(*boltdbPath)
	}

	return memdata.Open(), nil
}

func watchFile(path string, f func()) (io.Closer, error) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return watcher, err
	}

	go func() {
		for {
			select {
			case event := <-watcher.Events:
				if event.Op&fsnotify.Write == fsnotify.Write {
					f()
				}
			case err := <-watcher.Errors:
				log.Println("error:", err)
			}
		}
	}()

	return watcher, watcher.Add(path)
}

type riverHandler struct {
	river.River
}

func newRiverHandler(feeds river.River) http.Handler {
	return &riverHandler{feeds}
}

func (h *riverHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if strings.ToUpper(r.Method) != "GET" {
		w.Header().Set("Accept", "GET")
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	switch r.URL.Path {
	case "/":
		w.Header().Set("Content-Type", "application/javascript")
		fmt.Fprintf(w, "%s(", DefaultCallback)
		if err := h.WriteTo(w); err != nil {
			log.Println("/:", err)
		}
		fmt.Fprintf(w, ")")

	case "/log":
		w.Header().Set("Content-Type", "application/json")
		if err := json.NewEncoder(w).Encode(h.Log()); err != nil {
			log.Println("/log:", err)
		}

	default:
		http.NotFound(w, r)
	}
}

func main() {
	flag.Parse()

	if *help || flag.NArg() == 0 {
		printHelp()
		return
	}

	opmlPath := flag.Arg(0)

	duration, err := time.ParseDuration(*cutOff)
	if err != nil {
		log.Println(err)
		return
	}

	cacheTimeout, err := time.ParseDuration(*refresh)
	if err != nil {
		log.Println(err)
		return
	}

	store, err := loadDatastore()
	if err != nil {
		log.Println(err)
		return
	}
	defer store.Close()

	outline, err := opml.Load(opmlPath)
	if err != nil {
		log.Println(err)
		return
	}

	subs := subscriptions.FromOpml(outline)

	feeds := river.New(store, river.Options{
		Mapping:   river.DefaultMapping,
		CutOff:    duration,
		Refresh:   cacheTimeout,
		LogLength: 500,
	})

	for _, sub := range subs.List() {
		feeds.Add(sub.Uri)
	}

	updateSubs := func() {
		log.Printf("reading %s\n", opmlPath)
		outline, err := opml.Load(opmlPath)
		if err != nil {
			log.Printf("could not read %s: %s\n", opmlPath, err)
			return
		}

		newsubs := subscriptions.FromOpml(outline)

		for _, change := range subscriptions.Diff(subs, newsubs) {
			switch change.Type {
			case subscriptions.Removed:
				feeds.Remove(change.Uri)
				subs.Remove(change.Uri)
			case subscriptions.Added:
				feeds.Add(change.Uri)
				subs.Add(change.Uri)
			}
		}
	}

	http.Handle("/river/", http.StripPrefix("/river", newRiverHandler(feeds)))

	watcher, err := watchFile(opmlPath, updateSubs)
	if err != nil {
		log.Printf("could not start watching %s: %v\n", opmlPath, err)
	}
	defer watcher.Close()

	serve.Serve(*port, *socket, http.DefaultServeMux)
}
<|endoftext|>