<commit_before>package azurerm

import (
	"fmt"
	"net/http"
	"testing"

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

func TestAccAzureRMNetworkSecurityRule_basic(t *testing.T) {
	rInt := acctest.RandInt()
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMNetworkSecurityRuleDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAzureRMNetworkSecurityRule_basic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test"),
				),
			},
		},
	})
}

func TestAccAzureRMNetworkSecurityRule_disappears(t *testing.T) {
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMNetworkSecurityRuleDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAzureRMNetworkSecurityRule_basic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test"),
					testCheckAzureRMNetworkSecurityRuleDisappears("azurerm_network_security_rule.test"),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func TestAccAzureRMNetworkSecurityRule_addingRules(t *testing.T) {
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMNetworkSecurityRuleDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAzureRMNetworkSecurityRule_updateBasic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test1"),
				),
			},

			{
				Config: testAccAzureRMNetworkSecurityRule_updateExtraRule(rInt),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test2"),
				),
			},
		},
	})
}

func testCheckAzureRMNetworkSecurityRuleExists(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {

		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("Not found: %s", name)
		}

		sgName := rs.Primary.Attributes["network_security_group_name"]
		sgrName := rs.Primary.Attributes["name"]
		resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
		if !hasResourceGroup {
			return fmt.Errorf("Bad: no resource group found in state for network security rule: %s", sgName)
		}

		conn := testAccProvider.Meta().(*ArmClient).secRuleClient

		resp, err := conn.Get(resourceGroup, sgName, sgrName)
		if err != nil {
			return fmt.Errorf("Bad: Get on secRuleClient: %s", err)
		}

		if resp.StatusCode == http.StatusNotFound {
			return fmt.Errorf("Bad: Network Security Rule %q (resource group: %q) (network security group: %q) does not exist", sgrName, sgName, resourceGroup)
		}

		return nil
	}
}

func testCheckAzureRMNetworkSecurityRuleDisappears(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {

		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("Not found: %s", name)
		}

		sgName := rs.Primary.Attributes["network_security_group_name"]
		sgrName := rs.Primary.Attributes["name"]
		resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
		if !hasResourceGroup {
			return fmt.Errorf("Bad: no resource group found in state for network security rule: %s", sgName)
		}

		conn := testAccProvider.Meta().(*ArmClient).secRuleClient

		_, err := conn.Delete(resourceGroup, sgName, sgrName, make(chan struct{}))
		if err != nil {
			return fmt.Errorf("Bad: Delete on secRuleClient: %s", err)
		}

		return nil
	}
}

func testCheckAzureRMNetworkSecurityRuleDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*ArmClient).secRuleClient

	for _, rs := range s.RootModule().Resources {

		if rs.Type != "azurerm_network_security_rule" {
			continue
		}

		sgName := rs.Primary.Attributes["network_security_group_name"]
		sgrName := rs.Primary.Attributes["name"]
		resourceGroup := rs.Primary.Attributes["resource_group_name"]

		resp, err := conn.Get(resourceGroup, sgName, sgrName)

		if err != nil {
			return nil
		}

		if resp.StatusCode != http.StatusNotFound {
			return fmt.Errorf("Network Security Rule still exists:\n%#v", resp.Properties)
		}
	}

	return nil
}

func testAccAzureRMNetworkSecurityRule_basic(rInt int) string {
	return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
    name = "acceptanceTestResourceGroup1"
    location = "West US"
}

resource "azurerm_network_security_group" "test" {
    name = "acceptanceTestSecurityGroup1"
    location = "West US"
    resource_group_name = "${azurerm_resource_group.test.name}"
}

resource "azurerm_network_security_rule" "test" {
	name = "test123"
    	priority = 100
    	direction = "Outbound"
    	access = "Allow"
    	protocol = "Tcp"
    	source_port_range = "*"
    	destination_port_range = "*"
    	source_address_prefix = "*"
    	destination_address_prefix = "*"
    	resource_group_name = "${azurerm_resource_group.test.name}"
    	network_security_group_name = "${azurerm_network_security_group.test.name}"
}
`, rInt)
}

func testAccAzureRMNetworkSecurityRule_updateBasic(rInt int) string {
	return fmt.Sprintf(`
resource "azurerm_resource_group" "test1" {
    name = "acceptanceTestResourceGroup2"
    location = "West US"
}

resource "azurerm_network_security_group" "test1" {
    name = "acceptanceTestSecurityGroup2"
    location = "West US"
    resource_group_name = "${azurerm_resource_group.test1.name}"
}

resource "azurerm_network_security_rule" "test1" {
	name = "test123"
    	priority = 100
    	direction = "Outbound"
    	access = "Allow"
    	protocol = "Tcp"
    	source_port_range = "*"
    	destination_port_range = "*"
    	source_address_prefix = "*"
    	destination_address_prefix = "*"
    	resource_group_name = "${azurerm_resource_group.test1.name}"
    	network_security_group_name = "${azurerm_network_security_group.test1.name}"
}
`, rInt)
}

func testAccAzureRMNetworkSecurityRule_updateExtraRule(rInt int) string {
	return fmt.Sprintf(`
resource "azurerm_resource_group" "test1" {
    name = "acceptanceTestResourceGroup2"
    location = "West US"
}

resource "azurerm_network_security_group" "test1" {
    name = "acceptanceTestSecurityGroup2"
    location = "West US"
    resource_group_name = "${azurerm_resource_group.test1.name}"
}

resource "azurerm_network_security_rule" "test1" {
	name = "test123"
    	priority = 100
    	direction = "Outbound"
    	access = "Allow"
    	protocol = "Tcp"
    	source_port_range = "*"
    	destination_port_range = "*"
    	source_address_prefix = "*"
    	destination_address_prefix = "*"
    	resource_group_name = "${azurerm_resource_group.test1.name}"
    	network_security_group_name = "${azurerm_network_security_group.test1.name}"
}

resource "azurerm_network_security_rule" "test2" {
	name = "testing456"
    	priority = 101
    	direction = "Inbound"
    	access = "Deny"
    	protocol = "Tcp"
    	source_port_range = "*"
    	destination_port_range = "*"
    	source_address_prefix = "*"
    	destination_address_prefix = "*"
    	resource_group_name = "${azurerm_resource_group.test1.name}"
    	network_security_group_name = "${azurerm_network_security_group.test1.name}"
}
`, rInt)
}
<commit_msg>provider/azurerm: Randomizing the ARM security rule acceptance tests<commit_after>package azurerm

import (
	"fmt"
	"net/http"
	"testing"

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

func TestAccAzureRMNetworkSecurityRule_basic(t *testing.T) {
	rInt := acctest.RandInt()
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMNetworkSecurityRuleDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAzureRMNetworkSecurityRule_basic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test"),
				),
			},
		},
	})
}

func TestAccAzureRMNetworkSecurityRule_disappears(t *testing.T) {
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMNetworkSecurityRuleDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAzureRMNetworkSecurityRule_basic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test"),
					testCheckAzureRMNetworkSecurityRuleDisappears("azurerm_network_security_rule.test"),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func TestAccAzureRMNetworkSecurityRule_addingRules(t *testing.T) {
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testCheckAzureRMNetworkSecurityRuleDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAzureRMNetworkSecurityRule_updateBasic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test1"),
				),
			},

			{
				Config: testAccAzureRMNetworkSecurityRule_updateExtraRule(rInt),
				Check: resource.ComposeTestCheckFunc(
					testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test2"),
				),
			},
		},
	})
}

func testCheckAzureRMNetworkSecurityRuleExists(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {

		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("Not found: %s", name)
		}

		sgName := rs.Primary.Attributes["network_security_group_name"]
		sgrName := rs.Primary.Attributes["name"]
		resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
		if !hasResourceGroup {
			return fmt.Errorf("Bad: no resource group found in state for network security rule: %s", sgName)
		}

		conn := testAccProvider.Meta().(*ArmClient).secRuleClient

		resp, err := conn.Get(resourceGroup, sgName, sgrName)
		if err != nil {
			return fmt.Errorf("Bad: Get on secRuleClient: %s", err)
		}

		if resp.StatusCode == http.StatusNotFound {
			return fmt.Errorf("Bad: Network Security Rule %q (resource group: %q) (network security group: %q) does not exist", sgrName, sgName, resourceGroup)
		}

		return nil
	}
}

func testCheckAzureRMNetworkSecurityRuleDisappears(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {

		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("Not found: %s", name)
		}

		sgName := rs.Primary.Attributes["network_security_group_name"]
		sgrName := rs.Primary.Attributes["name"]
		resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
		if !hasResourceGroup {
			return fmt.Errorf("Bad: no resource group found in state for network security rule: %s", sgName)
		}

		conn := testAccProvider.Meta().(*ArmClient).secRuleClient

		_, err := conn.Delete(resourceGroup, sgName, sgrName, make(chan struct{}))
		if err != nil {
			return fmt.Errorf("Bad: Delete on secRuleClient: %s", err)
		}

		return nil
	}
}

func testCheckAzureRMNetworkSecurityRuleDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*ArmClient).secRuleClient

	for _, rs := range s.RootModule().Resources {

		if rs.Type != "azurerm_network_security_rule" {
			continue
		}

		sgName := rs.Primary.Attributes["network_security_group_name"]
		sgrName := rs.Primary.Attributes["name"]
		resourceGroup := rs.Primary.Attributes["resource_group_name"]

		resp, err := conn.Get(resourceGroup, sgName, sgrName)

		if err != nil {
			return nil
		}

		if resp.StatusCode != http.StatusNotFound {
			return fmt.Errorf("Network Security Rule still exists:\n%#v", resp.Properties)
		}
	}

	return nil
}

func testAccAzureRMNetworkSecurityRule_basic(rInt int) string {
	return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
    name = "acctestRG-%d"
    location = "West US"
}

resource "azurerm_network_security_group" "test" {
    name = "acceptanceTestSecurityGroup1"
    location = "West US"
    resource_group_name = "${azurerm_resource_group.test.name}"
}

resource "azurerm_network_security_rule" "test" {
	name = "test123"
    	priority = 100
    	direction = "Outbound"
    	access = "Allow"
    	protocol = "Tcp"
    	source_port_range = "*"
    	destination_port_range = "*"
    	source_address_prefix = "*"
    	destination_address_prefix = "*"
    	resource_group_name = "${azurerm_resource_group.test.name}"
    	network_security_group_name = "${azurerm_network_security_group.test.name}"
}
`, rInt)
}

func testAccAzureRMNetworkSecurityRule_updateBasic(rInt int) string {
	return fmt.Sprintf(`
resource "azurerm_resource_group" "test1" {
    name = "acctestRG-%d"
    location = "West US"
}

resource "azurerm_network_security_group" "test1" {
    name = "acceptanceTestSecurityGroup2"
    location = "West US"
    resource_group_name = "${azurerm_resource_group.test1.name}"
}

resource "azurerm_network_security_rule" "test1" {
	name = "test123"
    	priority = 100
    	direction = "Outbound"
    	access = "Allow"
    	protocol = "Tcp"
    	source_port_range = "*"
    	destination_port_range = "*"
    	source_address_prefix = "*"
    	destination_address_prefix = "*"
    	resource_group_name = "${azurerm_resource_group.test1.name}"
    	network_security_group_name = "${azurerm_network_security_group.test1.name}"
}
`, rInt)
}

func testAccAzureRMNetworkSecurityRule_updateExtraRule(rInt int) string {
	return fmt.Sprintf(`
resource "azurerm_resource_group" "test1" {
    name = "acctestRG-%d"
    location = "West US"
}

resource "azurerm_network_security_group" "test1" {
    name = "acceptanceTestSecurityGroup2"
    location = "West US"
    resource_group_name = "${azurerm_resource_group.test1.name}"
}

resource "azurerm_network_security_rule" "test1" {
	name = "test123"
    	priority = 100
    	direction = "Outbound"
    	access = "Allow"
    	protocol = "Tcp"
    	source_port_range = "*"
    	destination_port_range = "*"
    	source_address_prefix = "*"
    	destination_address_prefix = "*"
    	resource_group_name = "${azurerm_resource_group.test1.name}"
    	network_security_group_name = "${azurerm_network_security_group.test1.name}"
}

resource "azurerm_network_security_rule" "test2" {
	name = "testing456"
    	priority = 101
    	direction = "Inbound"
    	access = "Deny"
    	protocol = "Tcp"
    	source_port_range = "*"
    	destination_port_range = "*"
    	source_address_prefix = "*"
    	destination_address_prefix = "*"
    	resource_group_name = "${azurerm_resource_group.test1.name}"
    	network_security_group_name = "${azurerm_network_security_group.test1.name}"
}
`, rInt)
}
<|endoftext|>


<commit_before>package quotes

import (
	"net/http"
	"strconv"

	"github.com/aerogo/aero"
	"github.com/animenotifier/arn"
	"github.com/animenotifier/notify.moe/components"
	"github.com/animenotifier/notify.moe/utils"
)

const maxQuotes = 6

// Latest renders the quotes page.
func Latest(ctx *aero.Context) string {
	user := utils.GetUser(ctx)

	quotes := arn.FilterQuotes(func(track *arn.Quote) bool {
		return !track.IsDraft
	})

	arn.SortQuotesLatestFirst(quotes)

	// Limit the number of displayed quotes
	loadMoreIndex := 0

	if len(quotes) > maxQuotes {
		quotes = quotes[:maxQuotes]
		loadMoreIndex = maxQuotes
	}

	return ctx.HTML(components.Quotes(quotes, loadMoreIndex, user))
}

// LatestFrom renders the quotes from the given index.
func LatestFrom(ctx *aero.Context) string {
	user := utils.GetUser(ctx)
	index, err := ctx.GetInt("index")

	if err != nil {
		return ctx.Error(http.StatusBadRequest, "Invalid start index", err)
	}

	allQuotes := arn.FilterQuotes(func(track *arn.Quote) bool {
		return !track.IsDraft
	})

	if index < 0 || index >= len(allQuotes) {
		return ctx.Error(http.StatusBadRequest, "Invalid start index (maximum is "+strconv.Itoa(len(allQuotes))+")", nil)
	}

	arn.SortQuotesLatestFirst(allQuotes)

	quotes := allQuotes[index:]

	if len(quotes) > maxQuotes {
		quotes = quotes[:maxQuotes]
	}

	nextIndex := index + maxQuotes

	if nextIndex >= len(allQuotes) {
		// End of data - no more scrolling
		ctx.Response().Header().Set("X-LoadMore-Index", "-1")
	} else {
		// Send the index for the next request
		ctx.Response().Header().Set("X-LoadMore-Index", strconv.Itoa(nextIndex))
	}

	return ctx.HTML(components.QuotesScrollable(quotes, user))
}
<commit_msg>Set max quotes displayed to 9<commit_after>package quotes

import (
	"net/http"
	"strconv"

	"github.com/aerogo/aero"
	"github.com/animenotifier/arn"
	"github.com/animenotifier/notify.moe/components"
	"github.com/animenotifier/notify.moe/utils"
)

const maxQuotes = 9

// Latest renders the quotes page.
func Latest(ctx *aero.Context) string {
	user := utils.GetUser(ctx)

	quotes := arn.FilterQuotes(func(track *arn.Quote) bool {
		return !track.IsDraft
	})

	arn.SortQuotesLatestFirst(quotes)

	// Limit the number of displayed quotes
	loadMoreIndex := 0

	if len(quotes) > maxQuotes {
		quotes = quotes[:maxQuotes]
		loadMoreIndex = maxQuotes
	}

	return ctx.HTML(components.Quotes(quotes, loadMoreIndex, user))
}

// LatestFrom renders the quotes from the given index.
func LatestFrom(ctx *aero.Context) string {
	user := utils.GetUser(ctx)
	index, err := ctx.GetInt("index")

	if err != nil {
		return ctx.Error(http.StatusBadRequest, "Invalid start index", err)
	}

	allQuotes := arn.FilterQuotes(func(track *arn.Quote) bool {
		return !track.IsDraft
	})

	if index < 0 || index >= len(allQuotes) {
		return ctx.Error(http.StatusBadRequest, "Invalid start index (maximum is "+strconv.Itoa(len(allQuotes))+")", nil)
	}

	arn.SortQuotesLatestFirst(allQuotes)

	quotes := allQuotes[index:]

	if len(quotes) > maxQuotes {
		quotes = quotes[:maxQuotes]
	}

	nextIndex := index + maxQuotes

	if nextIndex >= len(allQuotes) {
		// End of data - no more scrolling
		ctx.Response().Header().Set("X-LoadMore-Index", "-1")
	} else {
		// Send the index for the next request
		ctx.Response().Header().Set("X-LoadMore-Index", strconv.Itoa(nextIndex))
	}

	return ctx.HTML(components.QuotesScrollable(quotes, user))
}
<|endoftext|>


<commit_before>package slogger

import (
	"fmt"
	"os"

	"github.com/tychoish/grip/level"
	"github.com/tychoish/grip/message"
	"github.com/tychoish/grip/send"
)

///////////////////////////////////////////////////////////////////////////
//
// A shim between slogger.Append and send.Sender
//
///////////////////////////////////////////////////////////////////////////

type appenderSender struct {
	appender Appender
	name     string
	level    send.LevelInfo
}

// NewAppenderSender implements the send.Sender interface, which
// allows it to be used as a grip backend, but the it's mode of action
// is to use a slogger.Appender. This allows using the grip package,
// either via the slogger interface or the normal grip Jouernaler
// interface, while continuing to use existing slogger code.
func NewAppenderSender(name string, a Appender) send.Sender {
	return &appenderSender{
		appender: a,
		name:     name,
		level:    send.LevelInfo{Default: level.Debug, Threshold: level.Debug},
	}
}

// WrapAppender takes an Appender instance and returns a send.Sender
// instance that wraps it. The name defaults to the name of the
// process (argc).
func WrapAppender(a Appender) send.Sender {
	return &appenderSender{
		appender: a,
		name:     os.Args[0],
		level:    send.LevelInfo{Default: level.Debug, Threshold: level.Debug},
	}
}

// TODO: we may want to add a mutex here
func (a *appenderSender) Close() error          { return nil }
func (a *appenderSender) Name() string          { return a.name }
func (a *appenderSender) SetName(n string)      { a.name = n }
func (a *appenderSender) Type() send.SenderType { return send.Custom }
func (a *appenderSender) Level() send.LevelInfo { return a.level }
func (a *appenderSender) SetLevel(l send.LevelInfo) error {
	if !l.Valid() {
		return fmt.Errorf("level settings are not valid: %+v", l)
	}

	a.level = l
	return nil
}

func (a *appenderSender) Send(m message.Composer) {
	if a.level.ShouldLog(m) {
		_ = a.appender.Append(NewLog(m))
	}
}
<commit_msg>avoid double wrapping slogger messages<commit_after>package slogger

import (
	"fmt"
	"os"

	"github.com/tychoish/grip/level"
	"github.com/tychoish/grip/message"
	"github.com/tychoish/grip/send"
)

///////////////////////////////////////////////////////////////////////////
//
// A shim between slogger.Append and send.Sender
//
///////////////////////////////////////////////////////////////////////////

type appenderSender struct {
	appender Appender
	name     string
	level    send.LevelInfo
}

// NewAppenderSender implements the send.Sender interface, which
// allows it to be used as a grip backend, but the it's mode of action
// is to use a slogger.Appender. This allows using the grip package,
// either via the slogger interface or the normal grip Jouernaler
// interface, while continuing to use existing slogger code.
func NewAppenderSender(name string, a Appender) send.Sender {
	return &appenderSender{
		appender: a,
		name:     name,
		level:    send.LevelInfo{Default: level.Debug, Threshold: level.Debug},
	}
}

// WrapAppender takes an Appender instance and returns a send.Sender
// instance that wraps it. The name defaults to the name of the
// process (argc).
func WrapAppender(a Appender) send.Sender {
	return &appenderSender{
		appender: a,
		name:     os.Args[0],
		level:    send.LevelInfo{Default: level.Debug, Threshold: level.Debug},
	}
}

// TODO: we may want to add a mutex here
func (a *appenderSender) Close() error          { return nil }
func (a *appenderSender) Name() string          { return a.name }
func (a *appenderSender) SetName(n string)      { a.name = n }
func (a *appenderSender) Type() send.SenderType { return send.Custom }
func (a *appenderSender) Level() send.LevelInfo { return a.level }
func (a *appenderSender) SetLevel(l send.LevelInfo) error {
	if !l.Valid() {
		return fmt.Errorf("level settings are not valid: %+v", l)
	}

	a.level = l
	return nil
}

func (a *appenderSender) Send(m message.Composer) {
	if a.level.ShouldLog(m) {
		log, ok := m.(*Log)
		if ok {
			_ = a.appender.Append(log)
		} else {
			_ = a.appender.Append(NewLog(m))
		}
	}
}
<|endoftext|>


<commit_before>package pgxpool_test

import (
	"context"
	"testing"
	"time"

	"github.com/jackc/pgx/v4/pgxpool"

	"github.com/jackc/pgconn"
	"github.com/jackc/pgx/v4"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// Conn.Release is an asynchronous process that returns immediately. There is no signal when the actual work is
// completed. To test something that relies on the actual work for Conn.Release being completed we must simply wait.
// This function wraps the sleep so there is more meaning for the callers.
func waitForReleaseToComplete() {
	time.Sleep(5 * time.Millisecond)
}

type execer interface {
	Exec(ctx context.Context, sql string, arguments ...interface{}) (pgconn.CommandTag, error)
}

func testExec(t *testing.T, db execer) {
	results, err := db.Exec(context.Background(), "set time zone 'America/Chicago'")
	require.NoError(t, err)
	assert.EqualValues(t, "SET", results)
}

type queryer interface {
	Query(ctx context.Context, sql string, args ...interface{}) (pgx.Rows, error)
}

func testQuery(t *testing.T, db queryer) {
	var sum, rowCount int32

	rows, err := db.Query(context.Background(), "select generate_series(1,$1)", 10)
	require.NoError(t, err)

	for rows.Next() {
		var n int32
		rows.Scan(&n)
		sum += n
		rowCount++
	}

	assert.NoError(t, rows.Err())
	assert.Equal(t, int32(10), rowCount)
	assert.Equal(t, int32(55), sum)
}

type queryRower interface {
	QueryRow(ctx context.Context, sql string, args ...interface{}) pgx.Row
}

func testQueryRow(t *testing.T, db queryRower) {
	var what, who string
	err := db.QueryRow(context.Background(), "select 'hello', $1::text", "world").Scan(&what, &who)
	assert.NoError(t, err)
	assert.Equal(t, "hello", what)
	assert.Equal(t, "world", who)
}

type sendBatcher interface {
	SendBatch(context.Context, *pgx.Batch) pgx.BatchResults
}

func testSendBatch(t *testing.T, db sendBatcher) {
	batch := &pgx.Batch{}
	batch.Queue("select 1")
	batch.Queue("select 2")

	br := db.SendBatch(context.Background(), batch)

	var err error
	var n int32
	err = br.QueryRow().Scan(&n)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, n)

	err = br.QueryRow().Scan(&n)
	assert.NoError(t, err)
	assert.EqualValues(t, 2, n)

	err = br.Close()
	assert.NoError(t, err)
}

type copyFromer interface {
	CopyFrom(context.Context, pgx.Identifier, []string, pgx.CopyFromSource) (int64, error)
}

func testCopyFrom(t *testing.T, db interface {
	execer
	queryer
	copyFromer
}) {
	_, err := db.Exec(context.Background(), `create temporary table foo(a int2, b int4, c int8, d varchar, e text, f date, g timestamptz)`)
	require.NoError(t, err)

	tzedTime := time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local)

	inputRows := [][]interface{}{
		{int16(0), int32(1), int64(2), "abc", "efg", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), tzedTime},
		{nil, nil, nil, nil, nil, nil, nil},
	}

	copyCount, err := db.CopyFrom(context.Background(), pgx.Identifier{"foo"}, []string{"a", "b", "c", "d", "e", "f", "g"}, pgx.CopyFromRows(inputRows))
	assert.NoError(t, err)
	assert.EqualValues(t, len(inputRows), copyCount)

	rows, err := db.Query(context.Background(), "select * from foo")
	assert.NoError(t, err)

	var outputRows [][]interface{}
	for rows.Next() {
		row, err := rows.Values()
		if err != nil {
			t.Errorf("Unexpected error for rows.Values(): %v", err)
		}
		outputRows = append(outputRows, row)
	}

	assert.NoError(t, rows.Err())
	assert.Equal(t, inputRows, outputRows)
}

func assertConfigsEqual(t *testing.T, expected, actual *pgxpool.Config, testName string) {
	if !assert.NotNil(t, expected) {
		return
	}
	if !assert.NotNil(t, actual) {
		return
	}

	assert.Equalf(t, expected.ConnString(), actual.ConnString(), "%s - ConnString", testName)

	// Can't test function equality, so just test that they are set or not.
	assert.Equalf(t, expected.AfterConnect == nil, actual.AfterConnect == nil, "%s - AfterConnect", testName)
	assert.Equalf(t, expected.BeforeAcquire == nil, actual.BeforeAcquire == nil, "%s - BeforeAcquire", testName)
	assert.Equalf(t, expected.AfterRelease == nil, actual.AfterRelease == nil, "%s - AfterRelease", testName)

	assert.Equalf(t, expected.MaxConnLifetime, actual.MaxConnLifetime, "%s - MaxConnLifetime", testName)
	assert.Equalf(t, expected.MaxConnIdleTime, actual.MaxConnIdleTime, "%s - MaxConnIdleTime", testName)
	assert.Equalf(t, expected.MaxConns, actual.MaxConns, "%s - MaxConns", testName)
	assert.Equalf(t, expected.MinConns, actual.MinConns, "%s - MinConns", testName)
	assert.Equalf(t, expected.HealthCheckPeriod, actual.HealthCheckPeriod, "%s - HealthCheckPeriod", testName)
	assert.Equalf(t, expected.LazyConnect, actual.LazyConnect, "%s - LazyConnect", testName)

	assertConnConfigsEqual(t, expected.ConnConfig, actual.ConnConfig, testName)
}

func assertConnConfigsEqual(t *testing.T, expected, actual *pgx.ConnConfig, testName string) {
	if !assert.NotNil(t, expected) {
		return
	}
	if !assert.NotNil(t, actual) {
		return
	}

	assert.Equalf(t, expected.Logger, actual.Logger, "%s - Logger", testName)
	assert.Equalf(t, expected.LogLevel, actual.LogLevel, "%s - LogLevel", testName)
	assert.Equalf(t, expected.ConnString(), actual.ConnString(), "%s - ConnString", testName)

	// Can't test function equality, so just test that they are set or not.
	assert.Equalf(t, expected.BuildStatementCache == nil, actual.BuildStatementCache == nil, "%s - BuildStatementCache", testName)

	assert.Equalf(t, expected.PreferSimpleProtocol, actual.PreferSimpleProtocol, "%s - PreferSimpleProtocol", testName)

	assert.Equalf(t, expected.Host, actual.Host, "%s - Host", testName)
	assert.Equalf(t, expected.Database, actual.Database, "%s - Database", testName)
	assert.Equalf(t, expected.Port, actual.Port, "%s - Port", testName)
	assert.Equalf(t, expected.User, actual.User, "%s - User", testName)
	assert.Equalf(t, expected.Password, actual.Password, "%s - Password", testName)
	assert.Equalf(t, expected.ConnectTimeout, actual.ConnectTimeout, "%s - ConnectTimeout", testName)
	assert.Equalf(t, expected.RuntimeParams, actual.RuntimeParams, "%s - RuntimeParams", testName)

	// Can't test function equality, so just test that they are set or not.
	assert.Equalf(t, expected.ValidateConnect == nil, actual.ValidateConnect == nil, "%s - ValidateConnect", testName)
	assert.Equalf(t, expected.AfterConnect == nil, actual.AfterConnect == nil, "%s - AfterConnect", testName)

	if assert.Equalf(t, expected.TLSConfig == nil, actual.TLSConfig == nil, "%s - TLSConfig", testName) {
		if expected.TLSConfig != nil {
			assert.Equalf(t, expected.TLSConfig.InsecureSkipVerify, actual.TLSConfig.InsecureSkipVerify, "%s - TLSConfig InsecureSkipVerify", testName)
			assert.Equalf(t, expected.TLSConfig.ServerName, actual.TLSConfig.ServerName, "%s - TLSConfig ServerName", testName)
		}
	}

	if assert.Equalf(t, len(expected.Fallbacks), len(actual.Fallbacks), "%s - Fallbacks", testName) {
		for i := range expected.Fallbacks {
			assert.Equalf(t, expected.Fallbacks[i].Host, actual.Fallbacks[i].Host, "%s - Fallback %d - Host", testName, i)
			assert.Equalf(t, expected.Fallbacks[i].Port, actual.Fallbacks[i].Port, "%s - Fallback %d - Port", testName, i)

			if assert.Equalf(t, expected.Fallbacks[i].TLSConfig == nil, actual.Fallbacks[i].TLSConfig == nil, "%s - Fallback %d - TLSConfig", testName, i) {
				if expected.Fallbacks[i].TLSConfig != nil {
					assert.Equalf(t, expected.Fallbacks[i].TLSConfig.InsecureSkipVerify, actual.Fallbacks[i].TLSConfig.InsecureSkipVerify, "%s - Fallback %d - TLSConfig InsecureSkipVerify", testName)
					assert.Equalf(t, expected.Fallbacks[i].TLSConfig.ServerName, actual.Fallbacks[i].TLSConfig.ServerName, "%s - Fallback %d - TLSConfig ServerName", testName)
				}
			}
		}
	}
}
<commit_msg>Increase pool release conn wait time for CI<commit_after>package pgxpool_test

import (
	"context"
	"testing"
	"time"

	"github.com/jackc/pgx/v4/pgxpool"

	"github.com/jackc/pgconn"
	"github.com/jackc/pgx/v4"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// Conn.Release is an asynchronous process that returns immediately. There is no signal when the actual work is
// completed. To test something that relies on the actual work for Conn.Release being completed we must simply wait.
// This function wraps the sleep so there is more meaning for the callers.
func waitForReleaseToComplete() {
	time.Sleep(25 * time.Millisecond)
}

type execer interface {
	Exec(ctx context.Context, sql string, arguments ...interface{}) (pgconn.CommandTag, error)
}

func testExec(t *testing.T, db execer) {
	results, err := db.Exec(context.Background(), "set time zone 'America/Chicago'")
	require.NoError(t, err)
	assert.EqualValues(t, "SET", results)
}

type queryer interface {
	Query(ctx context.Context, sql string, args ...interface{}) (pgx.Rows, error)
}

func testQuery(t *testing.T, db queryer) {
	var sum, rowCount int32

	rows, err := db.Query(context.Background(), "select generate_series(1,$1)", 10)
	require.NoError(t, err)

	for rows.Next() {
		var n int32
		rows.Scan(&n)
		sum += n
		rowCount++
	}

	assert.NoError(t, rows.Err())
	assert.Equal(t, int32(10), rowCount)
	assert.Equal(t, int32(55), sum)
}

type queryRower interface {
	QueryRow(ctx context.Context, sql string, args ...interface{}) pgx.Row
}

func testQueryRow(t *testing.T, db queryRower) {
	var what, who string
	err := db.QueryRow(context.Background(), "select 'hello', $1::text", "world").Scan(&what, &who)
	assert.NoError(t, err)
	assert.Equal(t, "hello", what)
	assert.Equal(t, "world", who)
}

type sendBatcher interface {
	SendBatch(context.Context, *pgx.Batch) pgx.BatchResults
}

func testSendBatch(t *testing.T, db sendBatcher) {
	batch := &pgx.Batch{}
	batch.Queue("select 1")
	batch.Queue("select 2")

	br := db.SendBatch(context.Background(), batch)

	var err error
	var n int32
	err = br.QueryRow().Scan(&n)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, n)

	err = br.QueryRow().Scan(&n)
	assert.NoError(t, err)
	assert.EqualValues(t, 2, n)

	err = br.Close()
	assert.NoError(t, err)
}

type copyFromer interface {
	CopyFrom(context.Context, pgx.Identifier, []string, pgx.CopyFromSource) (int64, error)
}

func testCopyFrom(t *testing.T, db interface {
	execer
	queryer
	copyFromer
}) {
	_, err := db.Exec(context.Background(), `create temporary table foo(a int2, b int4, c int8, d varchar, e text, f date, g timestamptz)`)
	require.NoError(t, err)

	tzedTime := time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local)

	inputRows := [][]interface{}{
		{int16(0), int32(1), int64(2), "abc", "efg", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), tzedTime},
		{nil, nil, nil, nil, nil, nil, nil},
	}

	copyCount, err := db.CopyFrom(context.Background(), pgx.Identifier{"foo"}, []string{"a", "b", "c", "d", "e", "f", "g"}, pgx.CopyFromRows(inputRows))
	assert.NoError(t, err)
	assert.EqualValues(t, len(inputRows), copyCount)

	rows, err := db.Query(context.Background(), "select * from foo")
	assert.NoError(t, err)

	var outputRows [][]interface{}
	for rows.Next() {
		row, err := rows.Values()
		if err != nil {
			t.Errorf("Unexpected error for rows.Values(): %v", err)
		}
		outputRows = append(outputRows, row)
	}

	assert.NoError(t, rows.Err())
	assert.Equal(t, inputRows, outputRows)
}

func assertConfigsEqual(t *testing.T, expected, actual *pgxpool.Config, testName string) {
	if !assert.NotNil(t, expected) {
		return
	}
	if !assert.NotNil(t, actual) {
		return
	}

	assert.Equalf(t, expected.ConnString(), actual.ConnString(), "%s - ConnString", testName)

	// Can't test function equality, so just test that they are set or not.
	assert.Equalf(t, expected.AfterConnect == nil, actual.AfterConnect == nil, "%s - AfterConnect", testName)
	assert.Equalf(t, expected.BeforeAcquire == nil, actual.BeforeAcquire == nil, "%s - BeforeAcquire", testName)
	assert.Equalf(t, expected.AfterRelease == nil, actual.AfterRelease == nil, "%s - AfterRelease", testName)

	assert.Equalf(t, expected.MaxConnLifetime, actual.MaxConnLifetime, "%s - MaxConnLifetime", testName)
	assert.Equalf(t, expected.MaxConnIdleTime, actual.MaxConnIdleTime, "%s - MaxConnIdleTime", testName)
	assert.Equalf(t, expected.MaxConns, actual.MaxConns, "%s - MaxConns", testName)
	assert.Equalf(t, expected.MinConns, actual.MinConns, "%s - MinConns", testName)
	assert.Equalf(t, expected.HealthCheckPeriod, actual.HealthCheckPeriod, "%s - HealthCheckPeriod", testName)
	assert.Equalf(t, expected.LazyConnect, actual.LazyConnect, "%s - LazyConnect", testName)

	assertConnConfigsEqual(t, expected.ConnConfig, actual.ConnConfig, testName)
}

func assertConnConfigsEqual(t *testing.T, expected, actual *pgx.ConnConfig, testName string) {
	if !assert.NotNil(t, expected) {
		return
	}
	if !assert.NotNil(t, actual) {
		return
	}

	assert.Equalf(t, expected.Logger, actual.Logger, "%s - Logger", testName)
	assert.Equalf(t, expected.LogLevel, actual.LogLevel, "%s - LogLevel", testName)
	assert.Equalf(t, expected.ConnString(), actual.ConnString(), "%s - ConnString", testName)

	// Can't test function equality, so just test that they are set or not.
	assert.Equalf(t, expected.BuildStatementCache == nil, actual.BuildStatementCache == nil, "%s - BuildStatementCache", testName)

	assert.Equalf(t, expected.PreferSimpleProtocol, actual.PreferSimpleProtocol, "%s - PreferSimpleProtocol", testName)

	assert.Equalf(t, expected.Host, actual.Host, "%s - Host", testName)
	assert.Equalf(t, expected.Database, actual.Database, "%s - Database", testName)
	assert.Equalf(t, expected.Port, actual.Port, "%s - Port", testName)
	assert.Equalf(t, expected.User, actual.User, "%s - User", testName)
	assert.Equalf(t, expected.Password, actual.Password, "%s - Password", testName)
	assert.Equalf(t, expected.ConnectTimeout, actual.ConnectTimeout, "%s - ConnectTimeout", testName)
	assert.Equalf(t, expected.RuntimeParams, actual.RuntimeParams, "%s - RuntimeParams", testName)

	// Can't test function equality, so just test that they are set or not.
	assert.Equalf(t, expected.ValidateConnect == nil, actual.ValidateConnect == nil, "%s - ValidateConnect", testName)
	assert.Equalf(t, expected.AfterConnect == nil, actual.AfterConnect == nil, "%s - AfterConnect", testName)

	if assert.Equalf(t, expected.TLSConfig == nil, actual.TLSConfig == nil, "%s - TLSConfig", testName) {
		if expected.TLSConfig != nil {
			assert.Equalf(t, expected.TLSConfig.InsecureSkipVerify, actual.TLSConfig.InsecureSkipVerify, "%s - TLSConfig InsecureSkipVerify", testName)
			assert.Equalf(t, expected.TLSConfig.ServerName, actual.TLSConfig.ServerName, "%s - TLSConfig ServerName", testName)
		}
	}

	if assert.Equalf(t, len(expected.Fallbacks), len(actual.Fallbacks), "%s - Fallbacks", testName) {
		for i := range expected.Fallbacks {
			assert.Equalf(t, expected.Fallbacks[i].Host, actual.Fallbacks[i].Host, "%s - Fallback %d - Host", testName, i)
			assert.Equalf(t, expected.Fallbacks[i].Port, actual.Fallbacks[i].Port, "%s - Fallback %d - Port", testName, i)

			if assert.Equalf(t, expected.Fallbacks[i].TLSConfig == nil, actual.Fallbacks[i].TLSConfig == nil, "%s - Fallback %d - TLSConfig", testName, i) {
				if expected.Fallbacks[i].TLSConfig != nil {
					assert.Equalf(t, expected.Fallbacks[i].TLSConfig.InsecureSkipVerify, actual.Fallbacks[i].TLSConfig.InsecureSkipVerify, "%s - Fallback %d - TLSConfig InsecureSkipVerify", testName)
					assert.Equalf(t, expected.Fallbacks[i].TLSConfig.ServerName, actual.Fallbacks[i].TLSConfig.ServerName, "%s - Fallback %d - TLSConfig ServerName", testName)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package openapi3

import (
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"github.com/grokify/gotilla/net/urlutil"
	"github.com/grokify/gotilla/type/stringsutil"

	oas3 "github.com/getkin/kin-openapi/openapi3"
	"github.com/grokify/gocharts/data/table"
	"github.com/grokify/gotilla/encoding/jsonutil"
	"github.com/grokify/gotilla/text"
)

type SpecMore struct {
	Spec *oas3.Swagger
}

func ReadSpecMore(path string, validate bool) (*SpecMore, error) {
	spec, err := ReadFile(path, validate)
	if err != nil {
		return nil, err
	}
	return &SpecMore{Spec: spec}, nil
}

func (sm *SpecMore) SchemaCount() int {
	if sm.Spec == nil {
		return -1
	} else if sm.Spec.Components.Schemas == nil {
		return 0
	}
	return len(sm.Spec.Components.Schemas)
}

func (sm *SpecMore) OperationsTable(columns *text.TextSet) (*table.Table, error) {
	return operationsTable(sm.Spec, columns)
}

func operationsTable(spec *oas3.Swagger, columns *text.TextSet) (*table.Table, error) {
	if columns == nil {
		columns = &text.TextSet{Texts: OpTableColumnsDefault()}
	}
	tbl := table.NewTable()
	tbl.Name = spec.Info.Title
	tbl.Columns = columns.DisplayTexts()

	specMore := SpecMore{Spec: spec}

	//tgs, err := SpecTagGroups(spec)
	tgs, err := specMore.TagGroups()
	if err != nil {
		return nil, err
	}

	VisitOperations(spec, func(path, method string, op *oas3.Operation) {
		row := []string{}

		for _, text := range columns.Texts {
			switch text.Slug {
			case "method":
				row = append(row, method)
			case "path":
				row = append(row, path)
			case "operationId":
				row = append(row, op.OperationID)
			case "summary":
				row = append(row, op.Summary)
			case "tags":
				row = append(row, strings.Join(op.Tags, ", "))
			case "x-tag-groups":
				row = append(row, strings.Join(
					tgs.GetTagGroupNamesForTagNames(op.Tags...), ", "))
			default:
				row = append(row, GetExtensionPropStringOrEmpty(op.ExtensionProps, text.Slug))
			}
		}

		tbl.Records = append(tbl.Records, row)
	})
	return &tbl, nil
}

func OpTableColumnsDefault() []text.Text {
	texts := []text.Text{
		{
			Display: "Method",
			Slug:    "method"},
		{
			Display: "Path",
			Slug:    "path"},
		{
			Display: "OperationID",
			Slug:    "operationId"},
		{
			Display: "Summary",
			Slug:    "summary"},
		{
			Display: "Tags",
			Slug:    "tags"},
	}
	return texts
}

func OpTableColumnsRingCentral() *text.TextSet {
	texts := []text.Text{
		{
			Display: "Method",
			Slug:    "method"},
		{
			Display: "Path",
			Slug:    "path"},
		{
			Display: "OperationID",
			Slug:    "operationId"},
		{
			Display: "Summary",
			Slug:    "summary"},
		{
			Display: "Tags",
			Slug:    "tags"},
		{
			Display: "API Group",
			Slug:    "x-api-group"},
		{
			Display: "Throttling",
			Slug:    "x-throttling-group"},
		{
			Display: "App Permission",
			Slug:    "x-app-permission"},
		{
			Display: "User Permissions",
			Slug:    "x-user-permission"},
	}
	return &text.TextSet{Texts: texts}
}

func (sm *SpecMore) OperationMetas() []OperationMeta {
	ometas := []OperationMeta{}
	if sm.Spec == nil {
		return ometas
	}
	for url, path := range sm.Spec.Paths {
		if path.Connect != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodConnect, path.Connect))
		}
		if path.Delete != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodDelete, path.Delete))
		}
		if path.Get != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodGet, path.Get))
		}
		if path.Head != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodHead, path.Head))
		}
		if path.Options != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodOptions, path.Options))
		}
		if path.Patch != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodPatch, path.Patch))
		}
		if path.Post != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodPost, path.Post))
		}
		if path.Put != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodPut, path.Put))
		}
		if path.Trace != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodTrace, path.Trace))
		}
	}

	return ometas
}

func (sm *SpecMore) OperationsCount() uint {
	return uint(len(sm.OperationMetas()))
}

func (sm *SpecMore) SchemaNames() []string {
	schemaNames := []string{}
	for schemaName := range sm.Spec.Components.Schemas {
		schemaNames = append(schemaNames, schemaName)
	}
	return stringsutil.SliceCondenseSpace(schemaNames, true, true)
}

func (sm *SpecMore) SchemaNameExists(schemaName string, includeNil bool) bool {
	for schemaNameTry, schemaRef := range sm.Spec.Components.Schemas {
		if schemaNameTry == schemaName {
			if includeNil {
				return true
			} else if schemaRef == nil {
				return false
			}
			schemaRef.Ref = strings.TrimSpace(schemaRef.Ref)
			if len(schemaRef.Ref) > 0 {
				return true
			}
			if schemaRef.Value == nil {
				return false
			} else {
				return true
			}
		}
	}
	return false
}

// ServerURL returns the OAS3 Spec URL for the index
// specified.
func (sm *SpecMore) ServerURL(index uint) string {
	if int(index)+1 > len(sm.Spec.Servers) {
		return ""
	}
	server := sm.Spec.Servers[index]
	return strings.TrimSpace(server.URL)
}

// ServerURLBasePath extracts the base path from a OAS URL
// which can include variables.
func (sm *SpecMore) ServerURLBasePath(index uint) (string, error) {
	serverURL := sm.ServerURL(index)
	if len(serverURL) == 0 {
		return "", nil
	}
	serverURLParsed, err := urlutil.ParseURLTemplate(serverURL)
	if err != nil {
		return "", err
	}
	return serverURLParsed.Path, nil
}

func (sm *SpecMore) Tags(inclTop, inclOps bool) []string {
	tags := []string{}
	tagsMap := sm.TagsMap(inclTop, inclOps)
	for tag := range tagsMap {
		tags = append(tags, tag)
	}
	return stringsutil.SliceCondenseSpace(tags, true, true)
}

// TagsMap returns a set of tags present in the current
// spec.
func (sm *SpecMore) TagsMap(inclTop, inclOps bool) map[string]int {
	tagsMap := map[string]int{}
	if inclTop {
		for _, tag := range sm.Spec.Tags {
			tagName := strings.TrimSpace(tag.Name)
			if len(tagName) > 0 {
				if _, ok := tagsMap[tagName]; !ok {
					tagsMap[tagName] = 0
				}
				tagsMap[tagName]++
			}
		}
	}
	if inclOps {
		VisitOperations(sm.Spec, func(skipPath, skipMethod string, op *oas3.Operation) {
			for _, tagName := range op.Tags {
				tagName = strings.TrimSpace(tagName)
				if len(tagName) > 0 {
					if _, ok := tagsMap[tagName]; !ok {
						tagsMap[tagName] = 0
					}
					tagsMap[tagName]++
				}
			}
		})
	}
	return tagsMap
}

func (sm *SpecMore) WriteFileJSON(filename string, perm os.FileMode, prefix, indent string) error {
	jsonData, err := sm.Spec.MarshalJSON()
	if err != nil {
		return err
	}
	pretty := false
	if len(prefix) > 0 || len(indent) > 0 {
		pretty = true
	}
	if pretty {
		jsonData = jsonutil.PrettyPrint(jsonData, "", "  ")
	}
	return ioutil.WriteFile(filename, jsonData, perm)
}

func (sm *SpecMore) WriteFileXLSX(filename string) error {
	tbl, err := sm.OperationsTable(nil)
	if err != nil {
		return err
	}
	return table.WriteXLSX(filename, tbl)
}

type TagsMore struct {
	Tags oas3.Tags
}

func (tg *TagsMore) Get(tagName string) *oas3.Tag {
	for _, tag := range tg.Tags {
		if tagName == tag.Name {
			return tag
		}
	}
	return nil
}
<commit_msg>enhance: openapi3.SpecMore: add Stats()<commit_after>package openapi3

import (
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"github.com/grokify/gotilla/net/urlutil"
	"github.com/grokify/gotilla/type/stringsutil"

	oas3 "github.com/getkin/kin-openapi/openapi3"
	"github.com/grokify/gocharts/data/table"
	"github.com/grokify/gotilla/encoding/jsonutil"
	"github.com/grokify/gotilla/text"
)

type SpecMore struct {
	Spec *oas3.Swagger
}

func ReadSpecMore(path string, validate bool) (*SpecMore, error) {
	spec, err := ReadFile(path, validate)
	if err != nil {
		return nil, err
	}
	return &SpecMore{Spec: spec}, nil
}

func (sm *SpecMore) SchemasCount() int {
	if sm.Spec == nil {
		return -1
	} else if sm.Spec.Components.Schemas == nil {
		return 0
	}
	return len(sm.Spec.Components.Schemas)
}

func (sm *SpecMore) OperationsTable(columns *text.TextSet) (*table.Table, error) {
	return operationsTable(sm.Spec, columns)
}

func operationsTable(spec *oas3.Swagger, columns *text.TextSet) (*table.Table, error) {
	if columns == nil {
		columns = &text.TextSet{Texts: OpTableColumnsDefault()}
	}
	tbl := table.NewTable()
	tbl.Name = spec.Info.Title
	tbl.Columns = columns.DisplayTexts()

	specMore := SpecMore{Spec: spec}

	//tgs, err := SpecTagGroups(spec)
	tgs, err := specMore.TagGroups()
	if err != nil {
		return nil, err
	}

	VisitOperations(spec, func(path, method string, op *oas3.Operation) {
		row := []string{}

		for _, text := range columns.Texts {
			switch text.Slug {
			case "method":
				row = append(row, method)
			case "path":
				row = append(row, path)
			case "operationId":
				row = append(row, op.OperationID)
			case "summary":
				row = append(row, op.Summary)
			case "tags":
				row = append(row, strings.Join(op.Tags, ", "))
			case "x-tag-groups":
				row = append(row, strings.Join(
					tgs.GetTagGroupNamesForTagNames(op.Tags...), ", "))
			default:
				row = append(row, GetExtensionPropStringOrEmpty(op.ExtensionProps, text.Slug))
			}
		}

		tbl.Records = append(tbl.Records, row)
	})
	return &tbl, nil
}

func OpTableColumnsDefault() []text.Text {
	texts := []text.Text{
		{
			Display: "Method",
			Slug:    "method"},
		{
			Display: "Path",
			Slug:    "path"},
		{
			Display: "OperationID",
			Slug:    "operationId"},
		{
			Display: "Summary",
			Slug:    "summary"},
		{
			Display: "Tags",
			Slug:    "tags"},
	}
	return texts
}

func OpTableColumnsRingCentral() *text.TextSet {
	texts := []text.Text{
		{
			Display: "Method",
			Slug:    "method"},
		{
			Display: "Path",
			Slug:    "path"},
		{
			Display: "OperationID",
			Slug:    "operationId"},
		{
			Display: "Summary",
			Slug:    "summary"},
		{
			Display: "Tags",
			Slug:    "tags"},
		{
			Display: "API Group",
			Slug:    "x-api-group"},
		{
			Display: "Throttling",
			Slug:    "x-throttling-group"},
		{
			Display: "App Permission",
			Slug:    "x-app-permission"},
		{
			Display: "User Permissions",
			Slug:    "x-user-permission"},
	}
	return &text.TextSet{Texts: texts}
}

func (sm *SpecMore) OperationMetas() []OperationMeta {
	ometas := []OperationMeta{}
	if sm.Spec == nil {
		return ometas
	}
	for url, path := range sm.Spec.Paths {
		if path.Connect != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodConnect, path.Connect))
		}
		if path.Delete != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodDelete, path.Delete))
		}
		if path.Get != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodGet, path.Get))
		}
		if path.Head != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodHead, path.Head))
		}
		if path.Options != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodOptions, path.Options))
		}
		if path.Patch != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodPatch, path.Patch))
		}
		if path.Post != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodPost, path.Post))
		}
		if path.Put != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodPut, path.Put))
		}
		if path.Trace != nil {
			ometas = append(ometas, OperationToMeta(url, http.MethodTrace, path.Trace))
		}
	}

	return ometas
}

func (sm *SpecMore) OperationsCount() int {
	if sm.Spec == nil {
		return -1
	}
	return len(sm.OperationMetas())
}

func (sm *SpecMore) SchemaNames() []string {
	schemaNames := []string{}
	for schemaName := range sm.Spec.Components.Schemas {
		schemaNames = append(schemaNames, schemaName)
	}
	return stringsutil.SliceCondenseSpace(schemaNames, true, true)
}

func (sm *SpecMore) SchemaNameExists(schemaName string, includeNil bool) bool {
	for schemaNameTry, schemaRef := range sm.Spec.Components.Schemas {
		if schemaNameTry == schemaName {
			if includeNil {
				return true
			} else if schemaRef == nil {
				return false
			}
			schemaRef.Ref = strings.TrimSpace(schemaRef.Ref)
			if len(schemaRef.Ref) > 0 {
				return true
			}
			if schemaRef.Value == nil {
				return false
			} else {
				return true
			}
		}
	}
	return false
}

// ServerURL returns the OAS3 Spec URL for the index
// specified.
func (sm *SpecMore) ServerURL(index uint) string {
	if int(index)+1 > len(sm.Spec.Servers) {
		return ""
	}
	server := sm.Spec.Servers[index]
	return strings.TrimSpace(server.URL)
}

// ServerURLBasePath extracts the base path from a OAS URL
// which can include variables.
func (sm *SpecMore) ServerURLBasePath(index uint) (string, error) {
	serverURL := sm.ServerURL(index)
	if len(serverURL) == 0 {
		return "", nil
	}
	serverURLParsed, err := urlutil.ParseURLTemplate(serverURL)
	if err != nil {
		return "", err
	}
	return serverURLParsed.Path, nil
}

func (sm *SpecMore) Tags(inclTop, inclOps bool) []string {
	tags := []string{}
	tagsMap := sm.TagsMap(inclTop, inclOps)
	for tag := range tagsMap {
		tags = append(tags, tag)
	}
	return stringsutil.SliceCondenseSpace(tags, true, true)
}

// TagsMap returns a set of tags present in the current
// spec.
func (sm *SpecMore) TagsMap(inclTop, inclOps bool) map[string]int {
	tagsMap := map[string]int{}
	if inclTop {
		for _, tag := range sm.Spec.Tags {
			tagName := strings.TrimSpace(tag.Name)
			if len(tagName) > 0 {
				if _, ok := tagsMap[tagName]; !ok {
					tagsMap[tagName] = 0
				}
				tagsMap[tagName]++
			}
		}
	}
	if inclOps {
		VisitOperations(sm.Spec, func(skipPath, skipMethod string, op *oas3.Operation) {
			for _, tagName := range op.Tags {
				tagName = strings.TrimSpace(tagName)
				if len(tagName) > 0 {
					if _, ok := tagsMap[tagName]; !ok {
						tagsMap[tagName] = 0
					}
					tagsMap[tagName]++
				}
			}
		})
	}
	return tagsMap
}

type SpecStats struct {
	OperationsCount int
	SchemasCount    int
}

func (sm *SpecMore) Stats() SpecStats {
	return SpecStats{
		OperationsCount: sm.OperationsCount(),
		SchemasCount:    sm.SchemasCount(),
	}
}

func (sm *SpecMore) WriteFileJSON(filename string, perm os.FileMode, prefix, indent string) error {
	jsonData, err := sm.Spec.MarshalJSON()
	if err != nil {
		return err
	}
	pretty := false
	if len(prefix) > 0 || len(indent) > 0 {
		pretty = true
	}
	if pretty {
		jsonData = jsonutil.PrettyPrint(jsonData, "", "  ")
	}
	return ioutil.WriteFile(filename, jsonData, perm)
}

func (sm *SpecMore) WriteFileXLSX(filename string) error {
	tbl, err := sm.OperationsTable(nil)
	if err != nil {
		return err
	}
	return table.WriteXLSX(filename, tbl)
}

type TagsMore struct {
	Tags oas3.Tags
}

func (tg *TagsMore) Get(tagName string) *oas3.Tag {
	for _, tag := range tg.Tags {
		if tagName == tag.Name {
			return tag
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Package catalog implements the service catalog handling immutable data: origins, sources and metrics.
package catalog

import (
	"log"
	"sync"
	"time"

	"github.com/facette/facette/pkg/config"
)

// Catalog represents the main structure of a catalog instance.
type Catalog struct {
	Config     *config.Config
	Origins    map[string]*Origin
	Updated    time.Time
	debugLevel int
}

const (
	_ = iota
	// OriginCmdRefresh represents an origin refresh command
	OriginCmdRefresh
	// OriginCmdShutdown represents an origin shutdown command
	OriginCmdShutdown
)

// NewCatalog creates a new instance of catalog.
func NewCatalog(config *config.Config, debugLevel int) *Catalog {
	return &Catalog{
		Config:     config,
		Origins:    make(map[string]*Origin),
		debugLevel: debugLevel,
	}
}

// GetMetric returns an existing metric entry based on its origin, source and name.
func (catalog *Catalog) GetMetric(origin, source, name string) *Metric {
	if _, ok := catalog.Origins[origin]; !ok {
		return nil
	} else if _, ok := catalog.Origins[origin].Sources[source]; !ok {
		return nil
	} else if _, ok := catalog.Origins[origin].Sources[source].Metrics[name]; !ok {
		return nil
	}

	return catalog.Origins[origin].Sources[source].Metrics[name]
}

// Refresh updates the current catalog by refreshing its origins.
func (catalog *Catalog) Refresh() error {
	var (
		origin *Origin
		err    error
	)

	log.Println("INFO: catalog refresh started")

	// Get origins from configuration
	catalog.Origins = make(map[string]*Origin)

	for originName, originConfig := range catalog.Config.Origins {
		origin, err = NewOrigin(originName, originConfig)
		if err != nil {
			log.Printf("ERROR: %s\n", err.Error())
		}

		origin.Catalog = catalog

		catalog.Origins[originName] = origin
	}

	wait := &sync.WaitGroup{}

	// Update catalog origins concurrently
	for _, origin = range catalog.Origins {
		wait.Add(1)

		go func(wg *sync.WaitGroup, origin *Origin) {
			defer wg.Done()

			if err = SendOriginWorkerCmd(origin, OriginCmdRefresh); err != nil {
				log.Println("ERROR: " + err.Error())
			}
		}(wait, origin)
	}

	// Wait for all origins to be refreshed
	wait.Wait()

	catalog.Updated = time.Now()

	log.Println("INFO: catalog refresh completed")

	return nil
}

// Close terminates all origin workers and performs catalog clean-up
func (catalog *Catalog) Close() error {
	var err error

	// Shutdown catalog origin workers
	for _, origin := range catalog.Origins {
		if err = SendOriginWorkerCmd(origin, OriginCmdShutdown); err != nil {
			log.Printf("ERROR: unable to shut down origin `%s' worker: %s", origin.Name, err)
		}
	}

	log.Println("INFO: catalog closed")

	return nil
}
<commit_msg>Discard origins when error occurs<commit_after>// Package catalog implements the service catalog handling immutable data: origins, sources and metrics.
package catalog

import (
	"log"
	"sync"
	"time"

	"github.com/facette/facette/pkg/config"
)

// Catalog represents the main structure of a catalog instance.
type Catalog struct {
	Config     *config.Config
	Origins    map[string]*Origin
	Updated    time.Time
	debugLevel int
}

const (
	_ = iota
	// OriginCmdRefresh represents an origin refresh command
	OriginCmdRefresh
	// OriginCmdShutdown represents an origin shutdown command
	OriginCmdShutdown
)

// NewCatalog creates a new instance of catalog.
func NewCatalog(config *config.Config, debugLevel int) *Catalog {
	return &Catalog{
		Config:     config,
		Origins:    make(map[string]*Origin),
		debugLevel: debugLevel,
	}
}

// GetMetric returns an existing metric entry based on its origin, source and name.
func (catalog *Catalog) GetMetric(origin, source, name string) *Metric {
	if _, ok := catalog.Origins[origin]; !ok {
		return nil
	} else if _, ok := catalog.Origins[origin].Sources[source]; !ok {
		return nil
	} else if _, ok := catalog.Origins[origin].Sources[source].Metrics[name]; !ok {
		return nil
	}

	return catalog.Origins[origin].Sources[source].Metrics[name]
}

// Refresh updates the current catalog by refreshing its origins.
func (catalog *Catalog) Refresh() error {
	var (
		origin *Origin
		err    error
	)

	log.Println("INFO: catalog refresh started")

	// Get origins from configuration
	catalog.Origins = make(map[string]*Origin)

	for originName, originConfig := range catalog.Config.Origins {
		origin, err = NewOrigin(originName, originConfig)
		if err != nil {
			log.Printf("ERROR: %s, discarding origin\n", err.Error())
			continue
		}

		origin.Catalog = catalog

		catalog.Origins[originName] = origin
	}

	wait := &sync.WaitGroup{}

	// Update catalog origins concurrently
	for _, origin = range catalog.Origins {
		wait.Add(1)

		go func(wg *sync.WaitGroup, origin *Origin) {
			defer wg.Done()

			if err = SendOriginWorkerCmd(origin, OriginCmdRefresh); err != nil {
				log.Println("ERROR: " + err.Error())
			}
		}(wait, origin)
	}

	// Wait for all origins to be refreshed
	wait.Wait()

	catalog.Updated = time.Now()

	log.Println("INFO: catalog refresh completed")

	return nil
}

// Close terminates all origin workers and performs catalog clean-up
func (catalog *Catalog) Close() error {
	var err error

	// Shutdown catalog origin workers
	for _, origin := range catalog.Origins {
		if err = SendOriginWorkerCmd(origin, OriginCmdShutdown); err != nil {
			log.Printf("ERROR: unable to shut down origin `%s' worker: %s", origin.Name, err)
		}
	}

	log.Println("INFO: catalog closed")

	return nil
}
<|endoftext|>


<commit_before>package cli

import (
	"github.com/spf13/cobra"

	"github.com/telepresenceio/telepresence/v2/pkg/client/cli/cliutil"
)

// Command returns the telepresence sub-command "auth"
func LoginCommand() *cobra.Command {
	return &cobra.Command{
		Use:  "login",
		Args: cobra.NoArgs,

		Short: "Authenticate to Ambassador Cloud",
		Long:  "Authenticate to Ambassador Cloud",
		RunE: func(cmd *cobra.Command, _ []string) error {
			_, err := cliutil.EnsureLoggedIn(cmd.Context())
			return err
		},
	}
}

func LogoutCommand() *cobra.Command {
	return &cobra.Command{
		Use:  "logout",
		Args: cobra.NoArgs,

		Short: "Logout from Ambassador Cloud",
		Long:  "Logout from Ambassador Cloud",
		RunE: func(cmd *cobra.Command, _ []string) error {
			return cliutil.Logout(cmd.Context())
		},
	}
}
<commit_msg>Delete inaccurate comment<commit_after>package cli

import (
	"github.com/spf13/cobra"

	"github.com/telepresenceio/telepresence/v2/pkg/client/cli/cliutil"
)

func LoginCommand() *cobra.Command {
	return &cobra.Command{
		Use:  "login",
		Args: cobra.NoArgs,

		Short: "Authenticate to Ambassador Cloud",
		Long:  "Authenticate to Ambassador Cloud",
		RunE: func(cmd *cobra.Command, _ []string) error {
			_, err := cliutil.EnsureLoggedIn(cmd.Context())
			return err
		},
	}
}

func LogoutCommand() *cobra.Command {
	return &cobra.Command{
		Use:  "logout",
		Args: cobra.NoArgs,

		Short: "Logout from Ambassador Cloud",
		Long:  "Logout from Ambassador Cloud",
		RunE: func(cmd *cobra.Command, _ []string) error {
			return cliutil.Logout(cmd.Context())
		},
	}
}
<|endoftext|>


<commit_before>package dataset

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/gonum/matrix/mat64"
	"github.com/gonum/stat"
)

// LRN data format constants
const (
	// LrnHeaderSize holds the size of LRN file header
	LrnHeaderSize = iota
	// LrnHeaderCols holds number of LRN header columns
	LrnHeaderCols
	// LrnHeaderTypes holds number of LRN types
	LrnHeaderTypes
	// LrnHeaderNames not used
	LrnHeaderNames
	// LrnHeaderRows holds number of LRN header rows
	LrnHeaderRows
)

// load data funcs
var loadFuncs = map[string]func(io.Reader) (*mat64.Dense, error){
	".csv": LoadCSV,
	".lrn": LoadLRN,
}

// DataSet represents training data set
type DataSet struct {
	data    *mat64.Dense
	classes map[int]int
}

// New returns new data set or fails with error if either the path to data set
// supplied as a parameter does not exist or if the file is encoded
// in an unsupported format. File format is inferred from the file extension.
// Currently only csv files are supported.
func New(path string, classifications bool) (*DataSet, error) {
	// Check if the supplied file type is supported
	fileType := filepath.Ext(path)
	loadData, ok := loadFuncs[fileType]
	if !ok {
		return nil, fmt.Errorf("Unsupported file type: %s\n", fileType)
	}
	// Check if the training data file exists
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return nil, err
	}
	// Open training data file
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	// Load file
	data, err := loadData(file)
	if err != nil {
		return nil, err
	}
	// Load classes
	classes := make(map[int]int) // default empty classification information
	if classifications {
		// get cls file path by replacing the extension
		regex, _ := regexp.Compile("\\..+$") // no error expected here
		clsPath := regex.ReplaceAllString(path, ".cls")
		// Check if the classification file exists
		if _, err := os.Stat(clsPath); os.IsNotExist(err) {
			return nil, err
		}
		clsFile, err := os.Open(clsPath)
		if err != nil {
			return nil, err
		}
		defer clsFile.Close()
		classes, err = LoadCLS(clsFile)

		if err != nil {
			return nil, err
		}
	}
	// Return Data
	return &DataSet{
		data:    data,
		classes: classes,
	}, nil
}

// Data returns the data stored in a matrix
func (ds DataSet) Data() *mat64.Dense {
	return ds.data
}

// Classes returns classification information stored in a map
func (ds DataSet) Classes() map[int]int {
	return ds.classes
}

// Scale normalizes data in each column based on its mean and standard deviation and returns it.
// It modifies the underlying daata. If this is not desirable use the standalone Scale function.
func (ds *DataSet) Scale() *mat64.Dense {
	return scale(ds.data, true)
}

// LoadCSV loads data set from the path supplied as a parameter.
// It returns data matrix that contains particular CSV fields in columns.
// It returns error if the supplied data set contains corrrupted data or
// if the data can not be converted to float numbers
func LoadCSV(r io.Reader) (*mat64.Dense, error) {
	// data matrix dimensions: rows x cols
	var rows, cols int
	// mxData contains ALL data read field by field
	var mxData []float64
	// create new CSV reader
	csvReader := csv.NewReader(r)
	// read all data record by record
	for {
		record, err := csvReader.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		// allocate the dataRow during the first iteration
		if rows == 0 {
			// initialize cols on first iteration
			cols = len(record)
		}
		// convert strings to floats
		for _, field := range record {
			f, err := strconv.ParseFloat(field, 64)
			if err != nil {
				return nil, err
			}
			// append the read data into mxData
			mxData = append(mxData, f)
		}
		rows++
	}
	// return data matrix
	return mat64.NewDense(rows, cols, mxData), nil
}

// LoadLRN reads data from a .lrn file.
// See the specification here: http://databionic-esom.sourceforge.net/user.html#Data_files____lrn_
func LoadLRN(reader io.Reader) (*mat64.Dense, error) {
	const DataCol = 1
	var rows, cols int
	var mxData []float64
	headerRow := 0
	columnTypes := []int{}
	valueRow := 0

	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "#") { // comment
			continue
		} else if strings.HasPrefix(line, "%") { // header
			headerLine := strings.TrimPrefix(line, "% ")
			if headerRow == LrnHeaderSize { // rows
				rows64, err := strconv.ParseInt(headerLine, 10, 64)
				if err != nil {
					return nil, fmt.Errorf("Dataset size information missing")
				}
				rows = int(rows64)
			} else if headerRow == LrnHeaderCols { // cols
				// discard
			} else if headerRow == LrnHeaderTypes { // col types
				colTypes := strings.Split(headerLine, "\t")
				for _, colType := range colTypes {
					// this seems to happen in real .lrn files
					if len(colType) == 0 {
						continue
					}
					ct, err := strconv.ParseInt(colType, 10, 64)
					if err != nil {
						return nil, err
					}
					columnTypes = append(columnTypes, int(ct))
					// we're interested in data columns only
					if ct == DataCol {
						cols++
					}
				}
				// allocate data matrix because we know rows and cols now
				mxData = make([]float64, rows*cols)
			} else if headerRow == LrnHeaderNames { // col names
				// discard
			}
			headerRow++
		} else { // data
			if headerRow < LrnHeaderRows {
				return nil, fmt.Errorf("Invalid header")
			}
			if valueRow >= rows {
				return nil, fmt.Errorf("Too many data rows")
			}
			vals := strings.Split(line, "\t")
			valueIndex := 0
			for i, val := range vals {
				if i > len(columnTypes) {
					return nil, fmt.Errorf("Too many columns")
				}
				if columnTypes[i] == DataCol {
					if valueIndex < cols {
						f, err := strconv.ParseFloat(val, 64)
						if err != nil {
							return nil, fmt.Errorf("Problem parsing value at line %d, col %d", valueRow, i)
						}
						mxData[valueRow*cols+valueIndex] = f
						valueIndex++
						continue
					}
					return nil, fmt.Errorf("Too many data columns")
				}
			}
			valueRow++
		}
	}
	if valueRow != rows {
		return nil, fmt.Errorf("Wrong number of data rows.  Expecting %d, but was %d", rows, valueRow)
	}

	return mat64.NewDense(rows, cols, mxData), nil
}

// LoadCLS reads classification information from a .cls file.
// See the specification here: http://databionic-esom.sourceforge.net/user.html#Classification_files____cls_
// The only supported header is the Number of datasets (n)
func LoadCLS(reader io.Reader) (map[int]int, error) {
	var rows *int
	valueRow := 0
	classifications := make(map[int]int)

	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "#") { // comment
			continue
		} else if strings.HasPrefix(line, "%") { // header
			if rows != nil {
				return nil, fmt.Errorf("Unsupported header")
			}
			headerLine := strings.TrimPrefix(line, "% ")
			rows64, err := strconv.ParseInt(headerLine, 10, 64)
			if err != nil {
				fmt.Println(err)
				return nil, fmt.Errorf("Classification data size information missing")
			}
			rowsTmp := int(rows64)
			rows = &rowsTmp
		} else { // classes
			if rows == nil {
				return nil, fmt.Errorf("Invalid header")
			}
			if valueRow >= *rows {
				return nil, fmt.Errorf("Too many classification rows")
			}
			vals := strings.Split(line, "\t")
			// classes come in pairs: index -> class
			var index, class *int
			for i, val := range vals {
				if i > 2 {
					return nil, fmt.Errorf("Too many classification columns")
				}
				num64, err := strconv.ParseInt(val, 10, 64)
				if err != nil {
					return nil, fmt.Errorf("Problem parsing value at line %d, col %d", valueRow, i)
				}
				num := int(num64)

				if i == 0 {
					index = &num
				} else if i == 1 {
					class = &num
				}
			}
			if index == nil || class == nil {
				return nil, fmt.Errorf("Incomplete classification row")
			}

			// CLS indexes are 1-based, but we're using 0-based
			classifications[*index-1] = *class
			valueRow++
		}
	}
	return classifications, nil
}

// Scale centers the data set to zero mean values in each column and then normalizes them.
// It does not modify the data stored in the matrix supplied as a parameter.
func Scale(mx mat64.Matrix) *mat64.Dense {
	return scale(mx, false)
}

// scale centers the supplied data set to zero mean in each column and then normalizes them.
// You can specify whether you want to scale data in place or return new data set
func scale(mx mat64.Matrix, inPlace bool) *mat64.Dense {
	rows, cols := mx.Dims()
	// mean/stdev store each column mean/stdev values
	col := make([]float64, rows)
	mean := make([]float64, cols)
	stdev := make([]float64, cols)
	// calculate mean and standard deviation for each column
	for i := 0; i < cols; i++ {
		// copy i-th column to col
		mat64.Col(col, i, mx)
		mean[i], stdev[i] = stat.MeanStdDev(col, nil)
	}
	// initialize scale function
	scale := func(i, j int, x float64) float64 {
		return (x - mean[j]) / stdev[j]
	}
	// if in place data should be modified
	if inPlace {
		mxDense := mx.(*mat64.Dense)
		mxDense.Apply(scale, mxDense)
		return mxDense
	}
	// otherwise allocate new data matrix
	dataMx := new(mat64.Dense)
	dataMx.Clone(mx)
	dataMx.Apply(scale, dataMx)
	return dataMx
}
<commit_msg>Fixed a problem with trailing whitespace in data files<commit_after>package dataset

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/gonum/matrix/mat64"
	"github.com/gonum/stat"
)

// LRN data format constants
const (
	// LrnHeaderSize holds the size of LRN file header
	LrnHeaderSize = iota
	// LrnHeaderCols holds number of LRN header columns
	LrnHeaderCols
	// LrnHeaderTypes holds number of LRN types
	LrnHeaderTypes
	// LrnHeaderNames not used
	LrnHeaderNames
	// LrnHeaderRows holds number of LRN header rows
	LrnHeaderRows
)

// load data funcs
var loadFuncs = map[string]func(io.Reader) (*mat64.Dense, error){
	".csv": LoadCSV,
	".lrn": LoadLRN,
}

// DataSet represents training data set
type DataSet struct {
	data    *mat64.Dense
	classes map[int]int
}

// New returns new data set or fails with error if either the path to data set
// supplied as a parameter does not exist or if the file is encoded
// in an unsupported format. File format is inferred from the file extension.
// Currently only csv files are supported.
func New(path string, classifications bool) (*DataSet, error) {
	// Check if the supplied file type is supported
	fileType := filepath.Ext(path)
	loadData, ok := loadFuncs[fileType]
	if !ok {
		return nil, fmt.Errorf("Unsupported file type: %s\n", fileType)
	}
	// Check if the training data file exists
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return nil, err
	}
	// Open training data file
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	// Load file
	data, err := loadData(file)
	if err != nil {
		return nil, err
	}
	// Load classes
	classes := make(map[int]int) // default empty classification information
	if classifications {
		// get cls file path by replacing the extension
		regex, _ := regexp.Compile("\\..+$") // no error expected here
		clsPath := regex.ReplaceAllString(path, ".cls")
		// Check if the classification file exists
		if _, err := os.Stat(clsPath); os.IsNotExist(err) {
			return nil, err
		}
		clsFile, err := os.Open(clsPath)
		if err != nil {
			return nil, err
		}
		defer clsFile.Close()
		classes, err = LoadCLS(clsFile)

		if err != nil {
			return nil, err
		}
	}
	// Return Data
	return &DataSet{
		data:    data,
		classes: classes,
	}, nil
}

// Data returns the data stored in a matrix
func (ds DataSet) Data() *mat64.Dense {
	return ds.data
}

// Classes returns classification information stored in a map
func (ds DataSet) Classes() map[int]int {
	return ds.classes
}

// Scale normalizes data in each column based on its mean and standard deviation and returns it.
// It modifies the underlying daata. If this is not desirable use the standalone Scale function.
func (ds *DataSet) Scale() *mat64.Dense {
	return scale(ds.data, true)
}

// LoadCSV loads data set from the path supplied as a parameter.
// It returns data matrix that contains particular CSV fields in columns.
// It returns error if the supplied data set contains corrrupted data or
// if the data can not be converted to float numbers
func LoadCSV(r io.Reader) (*mat64.Dense, error) {
	// data matrix dimensions: rows x cols
	var rows, cols int
	// mxData contains ALL data read field by field
	var mxData []float64
	// create new CSV reader
	csvReader := csv.NewReader(r)
	// read all data record by record
	for {
		record, err := csvReader.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		// allocate the dataRow during the first iteration
		if rows == 0 {
			// initialize cols on first iteration
			cols = len(record)
		}
		// convert strings to floats
		for _, field := range record {
			f, err := strconv.ParseFloat(field, 64)
			if err != nil {
				return nil, err
			}
			// append the read data into mxData
			mxData = append(mxData, f)
		}
		rows++
	}
	// return data matrix
	return mat64.NewDense(rows, cols, mxData), nil
}

// LoadLRN reads data from a .lrn file.
// See the specification here: http://databionic-esom.sourceforge.net/user.html#Data_files____lrn_
func LoadLRN(reader io.Reader) (*mat64.Dense, error) {
	const DataCol = 1
	var rows, cols int
	var mxData []float64
	headerRow := 0
	columnTypes := []int{}
	valueRow := 0

	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		line := strings.TrimRight(scanner.Text(), "\t ")
		if strings.HasPrefix(line, "#") { // comment
			continue
		} else if strings.HasPrefix(line, "%") { // header
			headerLine := strings.TrimPrefix(line, "% ")
			if headerRow == LrnHeaderSize { // rows
				rows64, err := strconv.ParseInt(headerLine, 10, 64)
				if err != nil {
					return nil, fmt.Errorf("Dataset size information missing")
				}
				rows = int(rows64)
			} else if headerRow == LrnHeaderCols { // cols
				// discard
			} else if headerRow == LrnHeaderTypes { // col types
				colTypes := strings.Split(headerLine, "\t")
				for _, colType := range colTypes {
					ct, err := strconv.ParseInt(colType, 10, 64)
					if err != nil {
						return nil, err
					}
					columnTypes = append(columnTypes, int(ct))
					// we're interested in data columns only
					if ct == DataCol {
						cols++
					}
				}
				// allocate data matrix because we know rows and cols now
				mxData = make([]float64, rows*cols)
			} else if headerRow == LrnHeaderNames { // col names
				// discard
			}
			headerRow++
		} else { // data
			if headerRow < LrnHeaderRows {
				return nil, fmt.Errorf("Invalid header")
			}
			if valueRow >= rows {
				return nil, fmt.Errorf("Too many data rows")
			}
			vals := strings.Split(line, "\t")
			valueIndex := 0
			for i, val := range vals {
				if i > len(columnTypes) {
					return nil, fmt.Errorf("Too many columns")
				}
				if columnTypes[i] == DataCol {
					if valueIndex < cols {
						f, err := strconv.ParseFloat(val, 64)
						if err != nil {
							return nil, fmt.Errorf("Problem parsing value at line %d, col %d", valueRow, i)
						}
						mxData[valueRow*cols+valueIndex] = f
						valueIndex++
						continue
					}
					return nil, fmt.Errorf("Too many data columns")
				}
			}
			valueRow++
		}
	}
	if valueRow != rows {
		return nil, fmt.Errorf("Wrong number of data rows.  Expecting %d, but was %d", rows, valueRow)
	}

	return mat64.NewDense(rows, cols, mxData), nil
}

// LoadCLS reads classification information from a .cls file.
// See the specification here: http://databionic-esom.sourceforge.net/user.html#Classification_files____cls_
// The only supported header is the Number of datasets (n)
func LoadCLS(reader io.Reader) (map[int]int, error) {
	var rows *int
	valueRow := 0
	classifications := make(map[int]int)

	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		line := strings.TrimRight(scanner.Text(), "\t ")
		if strings.HasPrefix(line, "#") { // comment
			continue
		} else if strings.HasPrefix(line, "%") { // header
			if rows != nil {
				return nil, fmt.Errorf("Unsupported header")
			}
			headerLine := strings.TrimPrefix(line, "% ")
			rows64, err := strconv.ParseInt(headerLine, 10, 64)
			if err != nil {
				fmt.Println(err)
				return nil, fmt.Errorf("Classification data size information missing")
			}
			rowsTmp := int(rows64)
			rows = &rowsTmp
		} else { // classes
			if rows == nil {
				return nil, fmt.Errorf("Invalid header")
			}
			if valueRow >= *rows {
				return nil, fmt.Errorf("Too many classification rows")
			}
			vals := strings.Split(line, "\t")
			// classes come in pairs: index -> class
			var index, class *int
			for i, val := range vals {
				if i > 1 {
					return nil, fmt.Errorf("Too many classification columns")
				}
				num64, err := strconv.ParseInt(val, 10, 64)
				if err != nil {
					return nil, fmt.Errorf("Problem parsing value at line %d, col %d", valueRow, i)
				}
				num := int(num64)

				if i == 0 {
					index = &num
				} else if i == 1 {
					class = &num
				}
			}
			if index == nil || class == nil {
				return nil, fmt.Errorf("Incomplete classification row")
			}

			// CLS indexes are 1-based, but we're using 0-based
			classifications[*index-1] = *class
			valueRow++
		}
	}
	return classifications, nil
}

// Scale centers the data set to zero mean values in each column and then normalizes them.
// It does not modify the data stored in the matrix supplied as a parameter.
func Scale(mx mat64.Matrix) *mat64.Dense {
	return scale(mx, false)
}

// scale centers the supplied data set to zero mean in each column and then normalizes them.
// You can specify whether you want to scale data in place or return new data set
func scale(mx mat64.Matrix, inPlace bool) *mat64.Dense {
	rows, cols := mx.Dims()
	// mean/stdev store each column mean/stdev values
	col := make([]float64, rows)
	mean := make([]float64, cols)
	stdev := make([]float64, cols)
	// calculate mean and standard deviation for each column
	for i := 0; i < cols; i++ {
		// copy i-th column to col
		mat64.Col(col, i, mx)
		mean[i], stdev[i] = stat.MeanStdDev(col, nil)
	}
	// initialize scale function
	scale := func(i, j int, x float64) float64 {
		return (x - mean[j]) / stdev[j]
	}
	// if in place data should be modified
	if inPlace {
		mxDense := mx.(*mat64.Dense)
		mxDense.Apply(scale, mxDense)
		return mxDense
	}
	// otherwise allocate new data matrix
	dataMx := new(mat64.Dense)
	dataMx.Clone(mx)
	dataMx.Apply(scale, dataMx)
	return dataMx
}
<|endoftext|>


<commit_before>package httpproxy

import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"regexp"
	"strings"

	"github.com/sirupsen/logrus"
)

const (
	ForwardProto = "X-Forwarded-Proto"
	APIAuth      = "X-API-Auth-Header"
)

var (
	httpStart  = regexp.MustCompile("^http:/([^/])")
	httpsStart = regexp.MustCompile("^https:/([^/])")
	badHeaders = map[string]bool{
		"host":              true,
		"transfer-encoding": true,
		"content-length":    true,
		"x-api-auth-header": true,
	}
)

type Supplier func() []string

type proxy struct {
	prefix             string
	validHostsSupplier Supplier
}

func (p *proxy) isAllowed(host string) bool {
	for _, valid := range p.validHostsSupplier() {
		if valid == host {
			return true
		}

		if strings.HasPrefix(valid, "*") && strings.HasSuffix(host, valid[1:]) {
			return true
		}
	}

	return false
}

func NewProxy(prefix string, validHosts Supplier) http.Handler {
	p := proxy{
		prefix:             prefix,
		validHostsSupplier: validHosts,
	}

	return &httputil.ReverseProxy{
		Director: func(req *http.Request) {
			if err := p.proxy(req); err != nil {
				logrus.Infof("Failed to proxy %v: %v", req, err)
			}
		},
	}
}

func (p *proxy) proxy(req *http.Request) error {
	path := req.URL.String()
	index := strings.Index(path, p.prefix)
	destPath := path[index+len(p.prefix):]

	if strings.HasPrefix(destPath, "http") {
		dest := httpStart.ReplaceAll([]byte(destPath), []byte("http://$1"))
		dest = httpStart.ReplaceAll(dest, []byte("https://$1"))
		destPath = string(dest)
	}

	if !strings.HasPrefix(destPath, "http") {
		destPath = "https://" + destPath
	}

	destURL, err := url.Parse(destPath)
	if err != nil {
		return err
	}

	destURL.RawQuery = req.URL.RawQuery

	if !p.isAllowed(destURL.Host) {
		return fmt.Errorf("invalid host: %v", destURL.Host)
	}

	headerCopy := http.Header{}

	if req.TLS != nil {
		headerCopy.Set(ForwardProto, "https")
	}

	auth := req.Header.Get(APIAuth)
	if auth != "" {
		headerCopy.Set("Authorization", auth)
	}

	for name, value := range req.Header {
		if badHeaders[strings.ToLower(name)] {
			continue
		}

		copy := make([]string, len(value))
		for i := range value {
			copy[i] = strings.TrimPrefix(value[i], "rancher:")
		}
		headerCopy[name] = copy
	}

	req.Host = destURL.Hostname()
	req.URL = destURL
	req.Header = headerCopy

	return nil
}
<commit_msg>httpproxy: fixes https:/ to https:// rewriting<commit_after>package httpproxy

import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"regexp"
	"strings"

	"github.com/sirupsen/logrus"
)

const (
	ForwardProto = "X-Forwarded-Proto"
	APIAuth      = "X-API-Auth-Header"
)

var (
	httpStart  = regexp.MustCompile("^http:/([^/])")
	httpsStart = regexp.MustCompile("^https:/([^/])")
	badHeaders = map[string]bool{
		"host":              true,
		"transfer-encoding": true,
		"content-length":    true,
		"x-api-auth-header": true,
	}
)

type Supplier func() []string

type proxy struct {
	prefix             string
	validHostsSupplier Supplier
}

func (p *proxy) isAllowed(host string) bool {
	for _, valid := range p.validHostsSupplier() {
		if valid == host {
			return true
		}

		if strings.HasPrefix(valid, "*") && strings.HasSuffix(host, valid[1:]) {
			return true
		}
	}

	return false
}

func NewProxy(prefix string, validHosts Supplier) http.Handler {
	p := proxy{
		prefix:             prefix,
		validHostsSupplier: validHosts,
	}

	return &httputil.ReverseProxy{
		Director: func(req *http.Request) {
			if err := p.proxy(req); err != nil {
				logrus.Infof("Failed to proxy %v: %v", req, err)
			}
		},
	}
}

func (p *proxy) proxy(req *http.Request) error {
	path := req.URL.String()
	index := strings.Index(path, p.prefix)
	destPath := path[index+len(p.prefix):]

	if strings.HasPrefix(destPath, "https") {
		dest := httpsStart.ReplaceAll([]byte(destPath), []byte("http://$1"))
		dest = httpsStart.ReplaceAll(dest, []byte("https://$1"))
		destPath = string(dest)
	} else {
		dest := httpStart.ReplaceAll([]byte(destPath), []byte("http://$1"))
		dest = httpStart.ReplaceAll(dest, []byte("http://$1"))
		destPath = string(dest)
	}

	destURL, err := url.Parse(destPath)
	if err != nil {
		return err
	}

	destURL.RawQuery = req.URL.RawQuery

	if !p.isAllowed(destURL.Host) {
		return fmt.Errorf("invalid host: %v", destURL.Host)
	}

	headerCopy := http.Header{}

	if req.TLS != nil {
		headerCopy.Set(ForwardProto, "https")
	}

	auth := req.Header.Get(APIAuth)
	if auth != "" {
		headerCopy.Set("Authorization", auth)
	}

	for name, value := range req.Header {
		if badHeaders[strings.ToLower(name)] {
			continue
		}

		copy := make([]string, len(value))
		for i := range value {
			copy[i] = strings.TrimPrefix(value[i], "rancher:")
		}
		headerCopy[name] = copy
	}

	req.Host = destURL.Hostname()
	req.URL = destURL
	req.Header = headerCopy

	return nil
}
<|endoftext|>


<commit_before>package ingress

import (
	"fmt"
	"strings"

	"github.com/jetstack/kube-lego/pkg/kubelego_const"

	"github.com/Sirupsen/logrus"
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
	k8sMeta "k8s.io/apimachinery/pkg/apis/meta/v1"
	k8sExtensionsTyped "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
	k8sExtensions "k8s.io/client-go/pkg/apis/extensions/v1beta1"
)

func IsSupportedIngressClass(supportedClass []string, in string) (out string, err error) {
	out = strings.ToLower(in)
	for _, ingClass := range supportedClass {
		if ingClass == out {
			return out, nil
		}
	}
	return "", fmt.Errorf("unsupported ingress class '%s'. Did you you forget to specify LEGO_DEFAULT_INGRESS_CLASS ?", in)
}

func IsSupportedIngressProvider(supportedProviders []string, in string) (out string, err error) {
	out = strings.ToLower(in)
	for _, supportedProvider := range supportedProviders {
		if supportedProvider == out {
			return out, nil
		}
	}
	return "", fmt.Errorf("unsupported ingress provider '%s'. Did you you forget to specify LEGO_DEFAULT_INGRESS_PROVIDER ?", in)
}

func IgnoreIngress(ing *k8sExtensions.Ingress) error {

	key := kubelego.AnnotationEnabled

	val, ok := ing.Annotations[key]

	if !ok {
		return fmt.Errorf("has no annotation '%s'", key)
	}

	if strings.ToLower(val) != "true" {
		return fmt.Errorf("annotation '%s' is not true", key)
	}

	return nil
}

func New(client kubelego.KubeLego, namespace string, name string) *Ingress {
	ingress := &Ingress{
		exists:   true,
		kubelego: client,
	}

	var err error
	ingress.IngressApi, err = client.KubeClient().ExtensionsV1beta1().Ingresses(namespace).Get(name, k8sMeta.GetOptions{})
	if err != nil {
		if k8sErrors.IsNotFound(err) {
			ingress.IngressApi = &k8sExtensions.Ingress{
				ObjectMeta: k8sMeta.ObjectMeta{
					Namespace: namespace,
					Name:      name,
				},
			}
			ingress.exists = false

		} else {
			client.Log().Warn("Error while getting ingress: ", err)
		}
	}

	return ingress
}

func All(client kubelego.KubeLego) (ingresses []kubelego.Ingress, err error) {
	ingSlice, err := client.KubeClient().Extensions().Ingresses(client.LegoWatchNamespace()).List(k8sMeta.ListOptions{})

	if err != nil {
		return
	}

	for i, _ := range ingSlice.Items {
		ingresses = append(
			ingresses,
			&Ingress{
				IngressApi: &ingSlice.Items[i],
				exists:     true,
				kubelego:   client,
			},
		)
	}
	return ingresses, nil
}

var _ kubelego.Ingress = &Ingress{}

type Ingress struct {
	IngressApi *k8sExtensions.Ingress
	exists     bool
	kubelego   kubelego.KubeLego
}

func (i *Ingress) Log() *logrus.Entry {
	log := i.kubelego.Log().WithField("context", "ingress")

	if i.IngressApi != nil && i.IngressApi.Name != "" {
		log = log.WithField("name", i.IngressApi.Name)
	}
	if i.IngressApi != nil && i.IngressApi.Namespace != "" {
		log = log.WithField("namespace", i.IngressApi.Namespace)
	}
	return log
}

func (o *Ingress) client() k8sExtensionsTyped.IngressInterface {
	return o.kubelego.KubeClient().Extensions().Ingresses(o.IngressApi.Namespace)
}

func (o *Ingress) Save() (err error) {
	var obj *k8sExtensions.Ingress

	// check if it contains rules
	if len(o.IngressApi.Spec.Rules) > 0 {
		if o.exists {
			obj, err = o.client().Update(o.IngressApi)
		} else {
			obj, err = o.client().Create(o.IngressApi)
			o.exists = true
		}
	} else {
		if o.exists {
			err = o.client().Delete(o.IngressApi.Namespace, &k8sMeta.DeleteOptions{})
			obj = nil
		}
	}
	if err != nil {
		return
	}
	o.IngressApi = obj
	return
}

func (i *Ingress) Delete() error {

	if i.IngressApi == nil || !i.exists {
		return nil
	}

	err := i.client().Delete(i.IngressApi.Namespace, &k8sMeta.DeleteOptions{})
	if err != nil {
		return err
	}

	i.IngressApi = nil

	return nil
}

func (i *Ingress) Object() *k8sExtensions.Ingress {
	return i.IngressApi
}

func (i *Ingress) IngressClass() string {
	val, ok := i.IngressApi.Annotations[kubelego.AnnotationIngressClass]
	if !ok {
		return i.kubelego.LegoDefaultIngressClass()
	}
	return strings.ToLower(val)
}

func (i *Ingress) IngressProvider() string {
	val, ok := i.IngressApi.Annotations[kubelego.AnnotationIngressProvider]
	if !ok {
		return i.kubelego.LegoDefaultIngressProvider()
	}
	return strings.ToLower(val)
}

func (i *Ingress) Ignore() bool {
	err := IgnoreIngress(i.IngressApi)
	if err != nil {
		i.Log().Info("ignoring as ", err)
		return true
	}

	_, err = IsSupportedIngressClass(i.kubelego.LegoSupportedIngressClass(), i.IngressClass())
	if err != nil {
		i.Log().Info("ignoring as ", err)
		return true
	}

	return false
}

func (i *Ingress) KubeLego() kubelego.KubeLego {
	return i.kubelego
}

func (i *Ingress) Tls() (out []kubelego.Tls) {
	for count, _ := range i.IngressApi.Spec.TLS {
		out = append(out, &Tls{
			IngressTLS: &i.IngressApi.Spec.TLS[count],
			ingress:    i,
		})
	}
	return
}
<commit_msg>Return ingress.class value for ingress.provider if not set<commit_after>package ingress

import (
	"fmt"
	"strings"

	"github.com/jetstack/kube-lego/pkg/kubelego_const"

	"github.com/Sirupsen/logrus"
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
	k8sMeta "k8s.io/apimachinery/pkg/apis/meta/v1"
	k8sExtensionsTyped "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
	k8sExtensions "k8s.io/client-go/pkg/apis/extensions/v1beta1"
)

func IsSupportedIngressClass(supportedClass []string, in string) (out string, err error) {
	out = strings.ToLower(in)
	for _, ingClass := range supportedClass {
		if ingClass == out {
			return out, nil
		}
	}
	return "", fmt.Errorf("unsupported ingress class '%s'. Did you you forget to specify LEGO_DEFAULT_INGRESS_CLASS ?", in)
}

func IsSupportedIngressProvider(supportedProviders []string, in string) (out string, err error) {
	out = strings.ToLower(in)
	for _, supportedProvider := range supportedProviders {
		if supportedProvider == out {
			return out, nil
		}
	}
	return "", fmt.Errorf("unsupported ingress provider '%s'. Did you you forget to specify LEGO_DEFAULT_INGRESS_PROVIDER ?", in)
}

func IgnoreIngress(ing *k8sExtensions.Ingress) error {

	key := kubelego.AnnotationEnabled

	val, ok := ing.Annotations[key]

	if !ok {
		return fmt.Errorf("has no annotation '%s'", key)
	}

	if strings.ToLower(val) != "true" {
		return fmt.Errorf("annotation '%s' is not true", key)
	}

	return nil
}

func New(client kubelego.KubeLego, namespace string, name string) *Ingress {
	ingress := &Ingress{
		exists:   true,
		kubelego: client,
	}

	var err error
	ingress.IngressApi, err = client.KubeClient().ExtensionsV1beta1().Ingresses(namespace).Get(name, k8sMeta.GetOptions{})
	if err != nil {
		if k8sErrors.IsNotFound(err) {
			ingress.IngressApi = &k8sExtensions.Ingress{
				ObjectMeta: k8sMeta.ObjectMeta{
					Namespace: namespace,
					Name:      name,
				},
			}
			ingress.exists = false

		} else {
			client.Log().Warn("Error while getting ingress: ", err)
		}
	}

	return ingress
}

func All(client kubelego.KubeLego) (ingresses []kubelego.Ingress, err error) {
	ingSlice, err := client.KubeClient().Extensions().Ingresses(client.LegoWatchNamespace()).List(k8sMeta.ListOptions{})

	if err != nil {
		return
	}

	for i, _ := range ingSlice.Items {
		ingresses = append(
			ingresses,
			&Ingress{
				IngressApi: &ingSlice.Items[i],
				exists:     true,
				kubelego:   client,
			},
		)
	}
	return ingresses, nil
}

var _ kubelego.Ingress = &Ingress{}

type Ingress struct {
	IngressApi *k8sExtensions.Ingress
	exists     bool
	kubelego   kubelego.KubeLego
}

func (i *Ingress) Log() *logrus.Entry {
	log := i.kubelego.Log().WithField("context", "ingress")

	if i.IngressApi != nil && i.IngressApi.Name != "" {
		log = log.WithField("name", i.IngressApi.Name)
	}
	if i.IngressApi != nil && i.IngressApi.Namespace != "" {
		log = log.WithField("namespace", i.IngressApi.Namespace)
	}
	return log
}

func (o *Ingress) client() k8sExtensionsTyped.IngressInterface {
	return o.kubelego.KubeClient().Extensions().Ingresses(o.IngressApi.Namespace)
}

func (o *Ingress) Save() (err error) {
	var obj *k8sExtensions.Ingress

	// check if it contains rules
	if len(o.IngressApi.Spec.Rules) > 0 {
		if o.exists {
			obj, err = o.client().Update(o.IngressApi)
		} else {
			obj, err = o.client().Create(o.IngressApi)
			o.exists = true
		}
	} else {
		if o.exists {
			err = o.client().Delete(o.IngressApi.Namespace, &k8sMeta.DeleteOptions{})
			obj = nil
		}
	}
	if err != nil {
		return
	}
	o.IngressApi = obj
	return
}

func (i *Ingress) Delete() error {

	if i.IngressApi == nil || !i.exists {
		return nil
	}

	err := i.client().Delete(i.IngressApi.Namespace, &k8sMeta.DeleteOptions{})
	if err != nil {
		return err
	}

	i.IngressApi = nil

	return nil
}

func (i *Ingress) Object() *k8sExtensions.Ingress {
	return i.IngressApi
}

func (i *Ingress) IngressClass() string {
	val, ok := i.IngressApi.Annotations[kubelego.AnnotationIngressClass]
	if !ok {
		return i.kubelego.LegoDefaultIngressClass()
	}
	return strings.ToLower(val)
}

func (i *Ingress) IngressProvider() string {
	val, ok := i.IngressApi.Annotations[kubelego.AnnotationIngressProvider]
	if !ok {
		// we return IngressClass() here in order to not break backwards
		// compatibility with older versions of kube-lego
		return i.IngressClass()
	}
	return strings.ToLower(val)
}

func (i *Ingress) Ignore() bool {
	err := IgnoreIngress(i.IngressApi)
	if err != nil {
		i.Log().Info("ignoring as ", err)
		return true
	}

	_, err = IsSupportedIngressClass(i.kubelego.LegoSupportedIngressClass(), i.IngressClass())
	if err != nil {
		i.Log().Info("ignoring as ", err)
		return true
	}

	return false
}

func (i *Ingress) KubeLego() kubelego.KubeLego {
	return i.kubelego
}

func (i *Ingress) Tls() (out []kubelego.Tls) {
	for count, _ := range i.IngressApi.Spec.TLS {
		out = append(out, &Tls{
			IngressTLS: &i.IngressApi.Spec.TLS[count],
			ingress:    i,
		})
	}
	return
}
<|endoftext|>