<commit_before>package kazoo

import (
	"testing"
	"time"
)

func TestConsumergroups(t *testing.T) {
	kz, err := NewKazoo(zookeeperPeers, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer assertSuccessfulClose(t, kz)

	cg := kz.Consumergroup("test.kazoo.TestConsumergroups")

	cgs, err := kz.Consumergroups()
	if err != nil {
		t.Error(err)
	}
	originalCount := len(cgs)

	if _, ok := cgs[cg.Name]; ok {
		t.Error("Consumergoup `test.kazoo.TestConsumergroups` should not be returned")
	}

	if exists, _ := cg.Exists(); exists {
		t.Error("Consumergoup `test.kazoo.TestConsumergroups` should not be registered yet")
	}

	if err := cg.Create(); err != nil {
		t.Error(err)
	}

	if exists, _ := cg.Exists(); !exists {
		t.Error("Consumergoup `test.kazoo.TestConsumergroups` should be registered now")
	}

	cgs, err = kz.Consumergroups()
	if err != nil {
		t.Error(err)
	}

	if len(cgs) != originalCount+1 {
		t.Error("Should have one more consumergroup than at the start")
	}

	if err := cg.Delete(); err != nil {
		t.Error(err)
	}

	if exists, _ := cg.Exists(); exists {
		t.Error("Consumergoup `test.kazoo.TestConsumergroups` should not be registered anymore")
	}

	cgs, err = kz.Consumergroups()
	if err != nil {
		t.Error(err)
	}

	if len(cgs) != originalCount {
		t.Error("Should have the original number of consumergroups again")
	}
}

func TestConsumergroupInstances(t *testing.T) {
	kz, err := NewKazoo(zookeeperPeers, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer assertSuccessfulClose(t, kz)

	cg := kz.Consumergroup("test.kazoo.TestConsumergroupInstances")
	if err := cg.Create(); err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := cg.Delete(); err != nil {
			t.Error(err)
		}
	}()

	instances, err := cg.Instances()
	if err != nil {
		t.Error(err)
	}
	if len(instances) != 0 {
		t.Error("Expected no active consumergroup instances")
	}

	// Register a new instance
	instance1 := cg.NewInstance()
	if instance1.ID == "" {
		t.Error("It should generate a valid instance ID")
	}
	if err := instance1.Register([]string{"topic"}); err != nil {
		t.Error(err)
	}

	// Try to register an instance with the same ID.
	if err := cg.Instance(instance1.ID).Register([]string{"topic"}); err != ErrInstanceAlreadyRegistered {
		t.Error("The instance should already be registered")
	}

	instance2 := cg.Instance("test")
	if err := instance2.Register([]string{"topic"}); err != nil {
		t.Error(err)
	}

	time.Sleep(50 * time.Millisecond)

	instances, err = cg.Instances()
	if err != nil {
		t.Error(err)
	}
	if len(instances) != 2 {
		t.Error("Expected 2 active consumergroup instances")
	}
	if _, ok := instances[instance1.ID]; !ok {
		t.Error("Expected instance1 to be registered.")
	}
	if _, ok := instances[instance1.ID]; !ok {
		t.Error("Expected instance2 to be registered.")
	}

	// Deregister the two running instances
	if err := instance1.Deregister(); err != nil {
		t.Error(err)
	}
	if err := instance2.Deregister(); err != nil {
		t.Error(err)
	}

	// Try to deregister an instance that was not register
	instance3 := cg.NewInstance()
	if err := instance3.Deregister(); err != ErrInstanceNotRegistered {
		t.Error("Expected new instance to not be registered")
	}

	instances, err = cg.Instances()
	if err != nil {
		t.Error(err)
	}
	if len(instances) != 0 {
		t.Error("Expected no active consumergroup instances")
	}
}

func TestConsumergroupInstanceCrash(t *testing.T) {
	kz, err := NewKazoo(zookeeperPeers, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer assertSuccessfulClose(t, kz)

	cg := kz.Consumergroup("test.kazoo.TestConsumergroupInstancesEphemeral")
	if err := cg.Create(); err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := cg.Delete(); err != nil {
			t.Error(err)
		}
	}()

	// Create a kazoo instance on which we will simulate a crash.
	config := NewConfig()
	config.Timeout = 50 * time.Millisecond
	crashingKazoo, err := NewKazoo(zookeeperPeers, config)
	if err != nil {
		t.Fatal(err)
	}
	crashingCG := crashingKazoo.Consumergroup(cg.Name)

	// Instantiate and register the instance.
	instance := crashingCG.NewInstance()
	if err := instance.Register([]string{"test.1"}); err != nil {
		t.Error(err)
	}

	time.Sleep(50 * time.Millisecond)
	if instances, err := cg.Instances(); err != nil {
		t.Error(err)
	} else if len(instances) != 1 {
		t.Error("Should have 1 running instance, found", len(instances))
	}

	// Simulate a crash, and wait for Zookeeper to pick it up
	_ = crashingKazoo.Close()
	time.Sleep(200 * time.Millisecond)

	if instances, err := cg.Instances(); err != nil {
		t.Error(err)
	} else if len(instances) != 0 {
		t.Error("Should have 0 running instances")
	}
}

func TestConsumergroupOffsets(t *testing.T) {
	kz, err := NewKazoo(zookeeperPeers, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer assertSuccessfulClose(t, kz)

	cg := kz.Consumergroup("test.kazoo.TestConsumergroupOffsets")
	if err := cg.Create(); err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := cg.Delete(); err != nil {
			t.Error(err)
		}
	}()

	offset, err := cg.FetchOffset("test", 0)
	if err != nil {
		t.Error(err)
	}

	if offset != 0 {
		t.Error("Expected to get offset 0 for a partition that hasn't seen an offset commit yet")
	}

	if err := cg.CommitOffset("test", 0, 1234); err != nil {
		t.Error(err)
	}

	offset, err = cg.FetchOffset("test", 0)
	if err != nil {
		t.Error(err)
	}
	if offset != 1234 {
		t.Error("Expected to get the offset that was committed.")
	}
}
<commit_msg>Cleanup test.<commit_after>package kazoo

import (
	"testing"
	"time"
)

func TestConsumergroups(t *testing.T) {
	kz, err := NewKazoo(zookeeperPeers, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer assertSuccessfulClose(t, kz)

	cg := kz.Consumergroup("test.kazoo.TestConsumergroups")

	cgs, err := kz.Consumergroups()
	if err != nil {
		t.Error(err)
	}
	originalCount := len(cgs)

	if _, ok := cgs[cg.Name]; ok {
		t.Error("Consumergoup `test.kazoo.TestConsumergroups` should not be returned")
	}

	if exists, _ := cg.Exists(); exists {
		t.Error("Consumergoup `test.kazoo.TestConsumergroups` should not be registered yet")
	}

	if err := cg.Create(); err != nil {
		t.Error(err)
	}

	if exists, _ := cg.Exists(); !exists {
		t.Error("Consumergoup `test.kazoo.TestConsumergroups` should be registered now")
	}

	cgs, err = kz.Consumergroups()
	if err != nil {
		t.Error(err)
	}

	if len(cgs) != originalCount+1 {
		t.Error("Should have one more consumergroup than at the start")
	}

	if err := cg.Delete(); err != nil {
		t.Error(err)
	}

	if exists, _ := cg.Exists(); exists {
		t.Error("Consumergoup `test.kazoo.TestConsumergroups` should not be registered anymore")
	}

	cgs, err = kz.Consumergroups()
	if err != nil {
		t.Error(err)
	}

	if len(cgs) != originalCount {
		t.Error("Should have the original number of consumergroups again")
	}
}

func TestConsumergroupInstances(t *testing.T) {
	kz, err := NewKazoo(zookeeperPeers, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer assertSuccessfulClose(t, kz)

	cg := kz.Consumergroup("test.kazoo.TestConsumergroupInstances")
	if err := cg.Create(); err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := cg.Delete(); err != nil {
			t.Error(err)
		}
	}()

	if instances, err := cg.Instances(); err != nil {
		t.Error(err)
	} else if len(instances) != 0 {
		t.Fatal("Expected no active consumergroup instances")
	}

	// Register a new instance
	instance1 := cg.NewInstance()
	if instance1.ID == "" {
		t.Error("It should generate a valid instance ID")
	}
	if err := instance1.Register([]string{"topic"}); err != nil {
		t.Error(err)
	}

	// Try to register an instance with the same ID.
	if err := cg.Instance(instance1.ID).Register([]string{"topic"}); err != ErrInstanceAlreadyRegistered {
		t.Error("The instance should already be registered")
	}

	instance2 := cg.Instance("test")
	if err := instance2.Register([]string{"topic"}); err != nil {
		t.Error(err)
	}

	time.Sleep(50 * time.Millisecond)

	if instances, err := cg.Instances(); err != nil {
		t.Error(err)
	} else {
		if len(instances) != 2 {
			t.Error("Expected 2 active consumergroup instances")
		}
		if _, ok := instances[instance1.ID]; !ok {
			t.Error("Expected instance1 to be registered.")
		}
		if _, ok := instances[instance1.ID]; !ok {
			t.Error("Expected instance2 to be registered.")
		}
	}

	// Deregister the two running instances
	if err := instance1.Deregister(); err != nil {
		t.Error(err)
	}
	if err := instance2.Deregister(); err != nil {
		t.Error(err)
	}

	// Try to deregister an instance that was not register
	instance3 := cg.NewInstance()
	if err := instance3.Deregister(); err != ErrInstanceNotRegistered {
		t.Error("Expected new instance to not be registered")
	}

	if instances, err := cg.Instances(); err != nil {
		t.Error(err)
	} else if len(instances) != 0 {
		t.Error("Expected no active consumergroup instances")
	}
}

func TestConsumergroupInstanceCrash(t *testing.T) {
	kz, err := NewKazoo(zookeeperPeers, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer assertSuccessfulClose(t, kz)

	cg := kz.Consumergroup("test.kazoo.TestConsumergroupInstancesEphemeral")
	if err := cg.Create(); err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := cg.Delete(); err != nil {
			t.Error(err)
		}
	}()

	// Create a kazoo instance on which we will simulate a crash.
	config := NewConfig()
	config.Timeout = 50 * time.Millisecond
	crashingKazoo, err := NewKazoo(zookeeperPeers, config)
	if err != nil {
		t.Fatal(err)
	}
	crashingCG := crashingKazoo.Consumergroup(cg.Name)

	// Instantiate and register the instance.
	instance := crashingCG.NewInstance()
	if err := instance.Register([]string{"test.1"}); err != nil {
		t.Error(err)
	}

	time.Sleep(50 * time.Millisecond)
	if instances, err := cg.Instances(); err != nil {
		t.Error(err)
	} else if len(instances) != 1 {
		t.Error("Should have 1 running instance, found", len(instances))
	}

	// Simulate a crash, and wait for Zookeeper to pick it up
	_ = crashingKazoo.Close()
	time.Sleep(200 * time.Millisecond)

	if instances, err := cg.Instances(); err != nil {
		t.Error(err)
	} else if len(instances) != 0 {
		t.Error("Should have 0 running instances")
	}
}

func TestConsumergroupOffsets(t *testing.T) {
	kz, err := NewKazoo(zookeeperPeers, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer assertSuccessfulClose(t, kz)

	cg := kz.Consumergroup("test.kazoo.TestConsumergroupOffsets")
	if err := cg.Create(); err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := cg.Delete(); err != nil {
			t.Error(err)
		}
	}()

	offset, err := cg.FetchOffset("test", 0)
	if err != nil {
		t.Error(err)
	}

	if offset != 0 {
		t.Error("Expected to get offset 0 for a partition that hasn't seen an offset commit yet")
	}

	if err := cg.CommitOffset("test", 0, 1234); err != nil {
		t.Error(err)
	}

	offset, err = cg.FetchOffset("test", 0)
	if err != nil {
		t.Error(err)
	}
	if offset != 1234 {
		t.Error("Expected to get the offset that was committed.")
	}
}
<|endoftext|>


<commit_before>package command_registry

import (
	"fmt"
	"os"
	"time"

	"github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/configuration/config_helpers"
	"github.com/cloudfoundry/cli/cf/configuration/core_config"
	"github.com/cloudfoundry/cli/cf/configuration/plugin_config"
	"github.com/cloudfoundry/cli/cf/i18n/detection"
	"github.com/cloudfoundry/cli/cf/manifest"
	"github.com/cloudfoundry/cli/cf/net"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/cf/trace"
	"github.com/cloudfoundry/cli/plugin/models"
)

type Dependency struct {
	Ui           terminal.UI
	Config       core_config.Repository
	RepoLocator  api.RepositoryLocator
	Detector     detection.Detector
	PluginConfig plugin_config.PluginConfiguration
	ManifestRepo manifest.ManifestRepository
	Gateways     map[string]net.Gateway
	TeePrinter   *terminal.TeePrinter
	PluginModels *pluginModels
}

type pluginModels struct {
	Application   *plugin_models.GetAppModel
	AppsSummary   *[]plugin_models.GetAppsModel
	Organizations *[]plugin_models.GetOrgs_Model
	Organization  *plugin_models.GetOrg_Model
	Spaces        *[]plugin_models.GetSpaces_Model
	Space         *plugin_models.GetSpace_Model
	OrgUsers      *[]plugin_models.GetOrgUsers_Model
	SpaceUsers    *[]plugin_models.GetSpaceUsers_Model
	Services      *[]plugin_models.GetServices_Model
	Service       *plugin_models.GetService_Model
}

func NewDependency() Dependency {
	deps := Dependency{}
	deps.TeePrinter = terminal.NewTeePrinter()
	deps.Ui = terminal.NewUI(os.Stdin, deps.TeePrinter)
	deps.ManifestRepo = manifest.NewManifestDiskRepository()

	errorHandler := func(err error) {
		if err != nil {
			deps.Ui.Failed(fmt.Sprintf("Config error: %s", err))
		}
	}
	deps.Config = core_config.NewRepositoryFromFilepath(config_helpers.DefaultFilePath(), errorHandler)
	deps.PluginConfig = plugin_config.NewPluginConfig(errorHandler)
	deps.Detector = &detection.JibberJabberDetector{}

	terminal.UserAskedForColors = deps.Config.ColorEnabled()
	terminal.InitColorSupport()

	if os.Getenv("CF_TRACE") != "" {
		trace.Logger = trace.NewLogger(os.Getenv("CF_TRACE"))
	} else {
		trace.Logger = trace.NewLogger(deps.Config.Trace())
	}

	deps.Gateways = map[string]net.Gateway{
		"auth":             net.NewUAAGateway(deps.Config, deps.Ui),
		"cloud-controller": net.NewCloudControllerGateway(deps.Config, time.Now, deps.Ui),
		"uaa":              net.NewUAAGateway(deps.Config, deps.Ui),
	}
	deps.RepoLocator = api.NewRepositoryLocator(deps.Config, deps.Gateways)

	deps.PluginModels = &pluginModels{Application: nil}

	return deps
}
<commit_msg>add AppManifest to dependency object<commit_after>package command_registry

import (
	"fmt"
	"os"
	"time"

	"github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/configuration/config_helpers"
	"github.com/cloudfoundry/cli/cf/configuration/core_config"
	"github.com/cloudfoundry/cli/cf/configuration/plugin_config"
	"github.com/cloudfoundry/cli/cf/i18n/detection"
	"github.com/cloudfoundry/cli/cf/manifest"
	"github.com/cloudfoundry/cli/cf/net"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/cf/trace"
	"github.com/cloudfoundry/cli/plugin/models"
)

type Dependency struct {
	Ui           terminal.UI
	Config       core_config.Repository
	RepoLocator  api.RepositoryLocator
	Detector     detection.Detector
	PluginConfig plugin_config.PluginConfiguration
	ManifestRepo manifest.ManifestRepository
	AppManifest  manifest.AppManifest
	Gateways     map[string]net.Gateway
	TeePrinter   *terminal.TeePrinter
	PluginModels *pluginModels
}

type pluginModels struct {
	Application   *plugin_models.GetAppModel
	AppsSummary   *[]plugin_models.GetAppsModel
	Organizations *[]plugin_models.GetOrgs_Model
	Organization  *plugin_models.GetOrg_Model
	Spaces        *[]plugin_models.GetSpaces_Model
	Space         *plugin_models.GetSpace_Model
	OrgUsers      *[]plugin_models.GetOrgUsers_Model
	SpaceUsers    *[]plugin_models.GetSpaceUsers_Model
	Services      *[]plugin_models.GetServices_Model
	Service       *plugin_models.GetService_Model
}

func NewDependency() Dependency {
	deps := Dependency{}
	deps.TeePrinter = terminal.NewTeePrinter()
	deps.Ui = terminal.NewUI(os.Stdin, deps.TeePrinter)
	deps.ManifestRepo = manifest.NewManifestDiskRepository()

	errorHandler := func(err error) {
		if err != nil {
			deps.Ui.Failed(fmt.Sprintf("Config error: %s", err))
		}
	}
	deps.Config = core_config.NewRepositoryFromFilepath(config_helpers.DefaultFilePath(), errorHandler)
	deps.PluginConfig = plugin_config.NewPluginConfig(errorHandler)
	deps.Detector = &detection.JibberJabberDetector{}

	terminal.UserAskedForColors = deps.Config.ColorEnabled()
	terminal.InitColorSupport()

	if os.Getenv("CF_TRACE") != "" {
		trace.Logger = trace.NewLogger(os.Getenv("CF_TRACE"))
	} else {
		trace.Logger = trace.NewLogger(deps.Config.Trace())
	}

	deps.Gateways = map[string]net.Gateway{
		"auth":             net.NewUAAGateway(deps.Config, deps.Ui),
		"cloud-controller": net.NewCloudControllerGateway(deps.Config, time.Now, deps.Ui),
		"uaa":              net.NewUAAGateway(deps.Config, deps.Ui),
	}
	deps.RepoLocator = api.NewRepositoryLocator(deps.Config, deps.Gateways)

	deps.PluginModels = &pluginModels{Application: nil}

	return deps
}
<|endoftext|>


<commit_before>package us_street

type (
	Candidate struct {
		InputID              string     `json:"input_id"`
		InputIndex           int        `json:"input_index"`
		CandidateIndex       int        `json:"candidate_index"`
		Addressee            string     `json:"addressee"`
		DeliveryLine1        string     `json:"delivery_line_1"`
		DeliveryLine2        string     `json:"delivery_line_2"`
		LastLine             string     `json:"last_line"`
		DeliveryPointBarcode string     `json:"delivery_point_barcode"`
		Components           Components `json:"components"`
		Metadata             Metadata   `json:"metadata"`
		Analysis             Analysis   `json:"analysis"`
	}

	Components struct {
		PrimaryNumber            string `json:"primary_number"`
		StreetPredirection       string `json:"street_predirection"`
		StreetName               string `json:"street_name"`
		StreetPostdirection      string `json:"street_postdirection"`
		StreetSuffix             string `json:"street_suffix"`
		SecondaryNumber          string `json:"secondary_number"`
		SecondaryDesignator      string `json:"secondary_designator"`
		ExtraSecondaryNumber     string `json:"extra_secondary_number"`
		ExtraSecondaryDesignator string `json:"extra_secondary_designator"`
		PMBNumber                string `json:"pmb_number"`
		PMBDesignator            string `json:"pmb_designator"`
		CityName                 string `json:"city_name"`
		DefaultCityName          string `json:"default_city_name"`
		StateAbbreviation        string `json:"state_abbreviation"`
		ZIPCode                  string `json:"zipcode"`
		Plus4Code                string `json:"plus4_code"`
		DeliveryPoint            string `json:"delivery_point"`
		DeliveryPointCheckDigit  string `json:"delivery_point_check_digit"`
		Urbanization             string `json:"urbanization"`
	}

	Metadata struct {
		RecordType               string  `json:"record_type"`
		ZIPType                  string  `json:"zip_type"`
		CountyFIPS               string  `json:"county_fips"`
		CountyName               string  `json:"county_name"`
		CarrierRoute             string  `json:"carrier_route"`
		CongressionalDistrict    string  `json:"congressional_district"`
		BuildingDefaultIndicator string  `json:"building_default_indicator"`
		RDI                      string  `json:"rdi"`
		ELOTSequence             string  `json:"elot_sequence"`
		ELOTSort                 string  `json:"elot_sort"`
		Latitude                 float64 `json:"latitude"`
		Longitude                float64 `json:"longitude"`
		Precision                string  `json:"precision"`
		TimeZone                 string  `json:"time_zone"`
		UTCOffset                float32 `json:"utc_offset"`
		DST                      bool    `json:"dst"`
	}

	Analysis struct {
		DPVMatchCode      string `json:"dpv_match_code"`
		DPVFootnotes      string `json:"dpv_footnotes"`
		DPVCMRACode       string `json:"dpv_cmra"`
		DPVVacantCode     string `json:"dpv_vacant"`
		Active            string `json:"active"`
		Footnotes         string `json:"footnotes"`
		LACSLinkCode      string `json:"lacslink_code"`
		LACSLinkIndicator string `json:"lacslink_indicator"`
		SuiteLinkMatch    bool   `json:"suitelink_match"`
		EWSMatch          bool   `json:"ews_match"`
	}
)
<commit_msg>Marked fields with "omitempty".<commit_after>package us_street

type (
	Candidate struct {
		InputID              string     `json:"input_id,omitempty"`
		InputIndex           int        `json:"input_index"`
		CandidateIndex       int        `json:"candidate_index"`
		Addressee            string     `json:"addressee,omitempty"`
		DeliveryLine1        string     `json:"delivery_line_1,omitempty"`
		DeliveryLine2        string     `json:"delivery_line_2,omitempty"`
		LastLine             string     `json:"last_line,omitempty"`
		DeliveryPointBarcode string     `json:"delivery_point_barcode,omitempty"`
		Components           Components `json:"components,omitempty"`
		Metadata             Metadata   `json:"metadata,omitempty"`
		Analysis             Analysis   `json:"analysis,omitempty"`
	}

	Components struct {
		PrimaryNumber            string `json:"primary_number,omitempty"`
		StreetPredirection       string `json:"street_predirection,omitempty"`
		StreetName               string `json:"street_name,omitempty"`
		StreetPostdirection      string `json:"street_postdirection,omitempty"`
		StreetSuffix             string `json:"street_suffix,omitempty"`
		SecondaryNumber          string `json:"secondary_number,omitempty"`
		SecondaryDesignator      string `json:"secondary_designator,omitempty"`
		ExtraSecondaryNumber     string `json:"extra_secondary_number,omitempty"`
		ExtraSecondaryDesignator string `json:"extra_secondary_designator,omitempty"`
		PMBNumber                string `json:"pmb_number,omitempty"`
		PMBDesignator            string `json:"pmb_designator,omitempty"`
		CityName                 string `json:"city_name,omitempty"`
		DefaultCityName          string `json:"default_city_name,omitempty"`
		StateAbbreviation        string `json:"state_abbreviation,omitempty"`
		ZIPCode                  string `json:"zipcode,omitempty"`
		Plus4Code                string `json:"plus4_code,omitempty"`
		DeliveryPoint            string `json:"delivery_point,omitempty"`
		DeliveryPointCheckDigit  string `json:"delivery_point_check_digit,omitempty"`
		Urbanization             string `json:"urbanization,omitempty"`
	}

	Metadata struct {
		RecordType               string  `json:"record_type,omitempty"`
		ZIPType                  string  `json:"zip_type,omitempty"`
		CountyFIPS               string  `json:"county_fips,omitempty"`
		CountyName               string  `json:"county_name,omitempty"`
		CarrierRoute             string  `json:"carrier_route,omitempty"`
		CongressionalDistrict    string  `json:"congressional_district,omitempty"`
		BuildingDefaultIndicator string  `json:"building_default_indicator,omitempty"`
		RDI                      string  `json:"rdi,omitempty"`
		ELOTSequence             string  `json:"elot_sequence,omitempty"`
		ELOTSort                 string  `json:"elot_sort,omitempty"`
		Latitude                 float64 `json:"latitude,omitempty"`
		Longitude                float64 `json:"longitude,omitempty"`
		Precision                string  `json:"precision,omitempty"`
		TimeZone                 string  `json:"time_zone,omitempty"`
		UTCOffset                float32 `json:"utc_offset,omitempty"`
		DST                      bool    `json:"dst,omitempty"`
	}

	Analysis struct {
		DPVMatchCode      string `json:"dpv_match_code,omitempty"`
		DPVFootnotes      string `json:"dpv_footnotes,omitempty"`
		DPVCMRACode       string `json:"dpv_cmra,omitempty"`
		DPVVacantCode     string `json:"dpv_vacant,omitempty"`
		Active            string `json:"active,omitempty"`
		Footnotes         string `json:"footnotes,omitempty"`
		LACSLinkCode      string `json:"lacslink_code,omitempty"`
		LACSLinkIndicator string `json:"lacslink_indicator,omitempty"`
		SuiteLinkMatch    bool   `json:"suitelink_match,omitempty"`
		EWSMatch          bool   `json:"ews_match,omitempty"`
	}
)
<|endoftext|>


<commit_before>// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package repo

import (
	"net/http"
	"strings"

	repo_model "code.gitea.io/gitea/models/repo"
	"code.gitea.io/gitea/modules/context"
	"code.gitea.io/gitea/modules/convert"
	"code.gitea.io/gitea/modules/log"
	api "code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/modules/web"
	"code.gitea.io/gitea/routers/api/v1/utils"
)

// ListTopics returns list of current topics for repo
func ListTopics(ctx *context.APIContext) {
	// swagger:operation GET /repos/{owner}/{repo}/topics repository repoListTopics
	// ---
	// summary: Get list of topics that a repository has
	// produces:
	//   - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// - name: page
	//   in: query
	//   description: page number of results to return (1-based)
	//   type: integer
	// - name: limit
	//   in: query
	//   description: page size of results
	//   type: integer
	// responses:
	//   "200":
	//     "$ref": "#/responses/TopicNames"

	opts := &repo_model.FindTopicOptions{
		ListOptions: utils.GetListOptions(ctx),
		RepoID:      ctx.Repo.Repository.ID,
	}

	topics, total, err := repo_model.FindTopics(opts)
	if err != nil {
		ctx.InternalServerError(err)
		return
	}

	topicNames := make([]string, len(topics))
	for i, topic := range topics {
		topicNames[i] = topic.Name
	}

	ctx.SetTotalCountHeader(total)
	ctx.JSON(http.StatusOK, map[string]interface{}{
		"topics": topicNames,
	})
}

// UpdateTopics updates repo with a new set of topics
func UpdateTopics(ctx *context.APIContext) {
	// swagger:operation PUT /repos/{owner}/{repo}/topics repository repoUpdateTopics
	// ---
	// summary: Replace list of topics for a repository
	// produces:
	//   - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// - name: body
	//   in: body
	//   schema:
	//     "$ref": "#/definitions/RepoTopicOptions"
	// responses:
	//   "204":
	//     "$ref": "#/responses/empty"
	//   "422":
	//     "$ref": "#/responses/invalidTopicsError"

	form := web.GetForm(ctx).(*api.RepoTopicOptions)
	topicNames := form.Topics
	validTopics, invalidTopics := repo_model.SanitizeAndValidateTopics(topicNames)

	if len(validTopics) > 25 {
		ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{
			"invalidTopics": nil,
			"message":       "Exceeding maximum number of topics per repo",
		})
		return
	}

	if len(invalidTopics) > 0 {
		ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{
			"invalidTopics": invalidTopics,
			"message":       "Topic names are invalid",
		})
		return
	}

	err := repo_model.SaveTopics(ctx.Repo.Repository.ID, validTopics...)
	if err != nil {
		log.Error("SaveTopics failed: %v", err)
		ctx.InternalServerError(err)
		return
	}

	ctx.Status(http.StatusNoContent)
}

// AddTopic adds a topic name to a repo
func AddTopic(ctx *context.APIContext) {
	// swagger:operation PUT /repos/{owner}/{repo}/topics/{topic} repository repoAddTopic
	// ---
	// summary: Add a topic to a repository
	// produces:
	//   - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// - name: topic
	//   in: path
	//   description: name of the topic to add
	//   type: string
	//   required: true
	// responses:
	//   "204":
	//     "$ref": "#/responses/empty"
	//   "422":
	//     "$ref": "#/responses/invalidTopicsError"

	topicName := strings.TrimSpace(strings.ToLower(ctx.Params(":topic")))

	if !repo_model.ValidateTopic(topicName) {
		ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{
			"invalidTopics": topicName,
			"message":       "Topic name is invalid",
		})
		return
	}

	// Prevent adding more topics than allowed to repo
	count, err := repo_model.CountTopics(&repo_model.FindTopicOptions{
		RepoID: ctx.Repo.Repository.ID,
	})
	if err != nil {
		log.Error("CountTopics failed: %v", err)
		ctx.InternalServerError(err)
		return
	}
	if count >= 25 {
		ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{
			"message": "Exceeding maximum allowed topics per repo.",
		})
		return
	}

	_, err = repo_model.AddTopic(ctx.Repo.Repository.ID, topicName)
	if err != nil {
		log.Error("AddTopic failed: %v", err)
		ctx.InternalServerError(err)
		return
	}

	ctx.Status(http.StatusNoContent)
}

// DeleteTopic removes topic name from repo
func DeleteTopic(ctx *context.APIContext) {
	// swagger:operation DELETE /repos/{owner}/{repo}/topics/{topic} repository repoDeleteTopic
	// ---
	// summary: Delete a topic from a repository
	// produces:
	//   - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// - name: topic
	//   in: path
	//   description: name of the topic to delete
	//   type: string
	//   required: true
	// responses:
	//   "204":
	//     "$ref": "#/responses/empty"
	//   "422":
	//     "$ref": "#/responses/invalidTopicsError"

	topicName := strings.TrimSpace(strings.ToLower(ctx.Params(":topic")))

	if !repo_model.ValidateTopic(topicName) {
		ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{
			"invalidTopics": topicName,
			"message":       "Topic name is invalid",
		})
		return
	}

	topic, err := repo_model.DeleteTopic(ctx.Repo.Repository.ID, topicName)
	if err != nil {
		log.Error("DeleteTopic failed: %v", err)
		ctx.InternalServerError(err)
		return
	}

	if topic == nil {
		ctx.NotFound()
	}

	ctx.Status(http.StatusNoContent)
}

// TopicSearch search for creating topic
func TopicSearch(ctx *context.APIContext) {
	// swagger:operation GET /topics/search repository topicSearch
	// ---
	// summary: search topics via keyword
	// produces:
	//   - application/json
	// parameters:
	//   - name: q
	//     in: query
	//     description: keywords to search
	//     required: true
	//     type: string
	//   - name: page
	//     in: query
	//     description: page number of results to return (1-based)
	//     type: integer
	//   - name: limit
	//     in: query
	//     description: page size of results
	//     type: integer
	// responses:
	//   "200":
	//     "$ref": "#/responses/TopicListResponse"
	//   "403":
	//     "$ref": "#/responses/forbidden"

	opts := &repo_model.FindTopicOptions{
		Keyword:     ctx.FormString("q"),
		ListOptions: utils.GetListOptions(ctx),
	}

	topics, total, err := repo_model.FindTopics(opts)
	if err != nil {
		ctx.InternalServerError(err)
		return
	}

	topicResponses := make([]*api.TopicResponse, len(topics))
	for i, topic := range topics {
		topicResponses[i] = convert.ToTopicResponse(topic)
	}

	ctx.SetTotalCountHeader(total)
	ctx.JSON(http.StatusOK, map[string]interface{}{
		"topics": topicResponses,
	})
}
<commit_msg>Add missing return for when topic isn't found (#20351)<commit_after>// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package repo

import (
	"net/http"
	"strings"

	repo_model "code.gitea.io/gitea/models/repo"
	"code.gitea.io/gitea/modules/context"
	"code.gitea.io/gitea/modules/convert"
	"code.gitea.io/gitea/modules/log"
	api "code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/modules/web"
	"code.gitea.io/gitea/routers/api/v1/utils"
)

// ListTopics returns list of current topics for repo
func ListTopics(ctx *context.APIContext) {
	// swagger:operation GET /repos/{owner}/{repo}/topics repository repoListTopics
	// ---
	// summary: Get list of topics that a repository has
	// produces:
	//   - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// - name: page
	//   in: query
	//   description: page number of results to return (1-based)
	//   type: integer
	// - name: limit
	//   in: query
	//   description: page size of results
	//   type: integer
	// responses:
	//   "200":
	//     "$ref": "#/responses/TopicNames"

	opts := &repo_model.FindTopicOptions{
		ListOptions: utils.GetListOptions(ctx),
		RepoID:      ctx.Repo.Repository.ID,
	}

	topics, total, err := repo_model.FindTopics(opts)
	if err != nil {
		ctx.InternalServerError(err)
		return
	}

	topicNames := make([]string, len(topics))
	for i, topic := range topics {
		topicNames[i] = topic.Name
	}

	ctx.SetTotalCountHeader(total)
	ctx.JSON(http.StatusOK, map[string]interface{}{
		"topics": topicNames,
	})
}

// UpdateTopics updates repo with a new set of topics
func UpdateTopics(ctx *context.APIContext) {
	// swagger:operation PUT /repos/{owner}/{repo}/topics repository repoUpdateTopics
	// ---
	// summary: Replace list of topics for a repository
	// produces:
	//   - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// - name: body
	//   in: body
	//   schema:
	//     "$ref": "#/definitions/RepoTopicOptions"
	// responses:
	//   "204":
	//     "$ref": "#/responses/empty"
	//   "422":
	//     "$ref": "#/responses/invalidTopicsError"

	form := web.GetForm(ctx).(*api.RepoTopicOptions)
	topicNames := form.Topics
	validTopics, invalidTopics := repo_model.SanitizeAndValidateTopics(topicNames)

	if len(validTopics) > 25 {
		ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{
			"invalidTopics": nil,
			"message":       "Exceeding maximum number of topics per repo",
		})
		return
	}

	if len(invalidTopics) > 0 {
		ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{
			"invalidTopics": invalidTopics,
			"message":       "Topic names are invalid",
		})
		return
	}

	err := repo_model.SaveTopics(ctx.Repo.Repository.ID, validTopics...)
	if err != nil {
		log.Error("SaveTopics failed: %v", err)
		ctx.InternalServerError(err)
		return
	}

	ctx.Status(http.StatusNoContent)
}

// AddTopic adds a topic name to a repo
func AddTopic(ctx *context.APIContext) {
	// swagger:operation PUT /repos/{owner}/{repo}/topics/{topic} repository repoAddTopic
	// ---
	// summary: Add a topic to a repository
	// produces:
	//   - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// - name: topic
	//   in: path
	//   description: name of the topic to add
	//   type: string
	//   required: true
	// responses:
	//   "204":
	//     "$ref": "#/responses/empty"
	//   "422":
	//     "$ref": "#/responses/invalidTopicsError"

	topicName := strings.TrimSpace(strings.ToLower(ctx.Params(":topic")))

	if !repo_model.ValidateTopic(topicName) {
		ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{
			"invalidTopics": topicName,
			"message":       "Topic name is invalid",
		})
		return
	}

	// Prevent adding more topics than allowed to repo
	count, err := repo_model.CountTopics(&repo_model.FindTopicOptions{
		RepoID: ctx.Repo.Repository.ID,
	})
	if err != nil {
		log.Error("CountTopics failed: %v", err)
		ctx.InternalServerError(err)
		return
	}
	if count >= 25 {
		ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{
			"message": "Exceeding maximum allowed topics per repo.",
		})
		return
	}

	_, err = repo_model.AddTopic(ctx.Repo.Repository.ID, topicName)
	if err != nil {
		log.Error("AddTopic failed: %v", err)
		ctx.InternalServerError(err)
		return
	}

	ctx.Status(http.StatusNoContent)
}

// DeleteTopic removes topic name from repo
func DeleteTopic(ctx *context.APIContext) {
	// swagger:operation DELETE /repos/{owner}/{repo}/topics/{topic} repository repoDeleteTopic
	// ---
	// summary: Delete a topic from a repository
	// produces:
	//   - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
	//   required: true
	// - name: topic
	//   in: path
	//   description: name of the topic to delete
	//   type: string
	//   required: true
	// responses:
	//   "204":
	//     "$ref": "#/responses/empty"
	//   "422":
	//     "$ref": "#/responses/invalidTopicsError"

	topicName := strings.TrimSpace(strings.ToLower(ctx.Params(":topic")))

	if !repo_model.ValidateTopic(topicName) {
		ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{
			"invalidTopics": topicName,
			"message":       "Topic name is invalid",
		})
		return
	}

	topic, err := repo_model.DeleteTopic(ctx.Repo.Repository.ID, topicName)
	if err != nil {
		log.Error("DeleteTopic failed: %v", err)
		ctx.InternalServerError(err)
		return
	}

	if topic == nil {
		ctx.NotFound()
		return
	}

	ctx.Status(http.StatusNoContent)
}

// TopicSearch search for creating topic
func TopicSearch(ctx *context.APIContext) {
	// swagger:operation GET /topics/search repository topicSearch
	// ---
	// summary: search topics via keyword
	// produces:
	//   - application/json
	// parameters:
	//   - name: q
	//     in: query
	//     description: keywords to search
	//     required: true
	//     type: string
	//   - name: page
	//     in: query
	//     description: page number of results to return (1-based)
	//     type: integer
	//   - name: limit
	//     in: query
	//     description: page size of results
	//     type: integer
	// responses:
	//   "200":
	//     "$ref": "#/responses/TopicListResponse"
	//   "403":
	//     "$ref": "#/responses/forbidden"

	opts := &repo_model.FindTopicOptions{
		Keyword:     ctx.FormString("q"),
		ListOptions: utils.GetListOptions(ctx),
	}

	topics, total, err := repo_model.FindTopics(opts)
	if err != nil {
		ctx.InternalServerError(err)
		return
	}

	topicResponses := make([]*api.TopicResponse, len(topics))
	for i, topic := range topics {
		topicResponses[i] = convert.ToTopicResponse(topic)
	}

	ctx.SetTotalCountHeader(total)
	ctx.JSON(http.StatusOK, map[string]interface{}{
		"topics": topicResponses,
	})
}
<|endoftext|>


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

package utils

import (
	"regexp"

	"github.com/jhump/protoreflect/desc"
	apb "google.golang.org/genproto/googleapis/api/annotations"
	"google.golang.org/protobuf/proto"
)

// GetHTTPRules returns a slice of HTTP rules for a given method descriptor.
//
// Note: This returns a slice -- it takes the google.api.http annotation,
// and then flattens the values in `additional_bindings`.
// This allows rule authors to simply range over all of the HTTP rules,
// since the common case is to want to apply the checks to all of them.
func GetHTTPRules(m *desc.MethodDescriptor) []*HTTPRule {
	rules := []*HTTPRule{}

	// Get the method options.
	opts := m.GetMethodOptions()

	// Get the "primary" rule (the direct google.api.http annotation).
	if x := proto.GetExtension(opts, apb.E_Http); x != nil {
		httpRule := x.(*apb.HttpRule)
		if parsedRule := parseRule(httpRule); parsedRule != nil {
			rules = append(rules, parsedRule)

			// Add any additional bindings and flatten them into `rules`.
			for _, binding := range httpRule.GetAdditionalBindings() {
				rules = append(rules, parseRule(binding))
			}
		}
	}

	// Done; return the rules.
	return rules
}

func parseRule(rule *apb.HttpRule) *HTTPRule {
	oneof := map[string]string{
		"GET":    rule.GetGet(),
		"POST":   rule.GetPost(),
		"PUT":    rule.GetPut(),
		"PATCH":  rule.GetPatch(),
		"DELETE": rule.GetDelete(),
	}
	if custom := rule.GetCustom(); custom != nil {
		oneof[custom.GetKind()] = custom.GetPath()
	}
	for method, uri := range oneof {
		if uri != "" {
			return &HTTPRule{
				Method:       method,
				URI:          uri,
				Body:         rule.GetBody(),
				ResponseBody: rule.GetResponseBody(),
			}
		}
	}
	return nil
}

// HTTPRule defines a parsed, easier-to-query equivalent to `apb.HttpRule`.
type HTTPRule struct {
	// The HTTP method. Guaranteed to be in all caps.
	// This is set to "CUSTOM" if the Custom property is set.
	Method string

	// The HTTP URI (the value corresponding to the selected HTTP method).
	URI string

	// The `body` value forwarded from the generated proto's HttpRule.
	Body string

	// The `response_body` value forwarded from the generated proto's HttpRule.
	ResponseBody string
}

// GetVariables returns the variable segments in a URI as a map.
func (h *HTTPRule) GetVariables() map[string]string {
	vars := map[string]string{}

	// Replace the version template variable with "v".
	uri := templateSegment.ReplaceAllString(h.URI, "v")
	for _, match := range plainVar.FindAllStringSubmatch(uri, -1) {
		vars[match[1]] = "*"
	}
	for _, match := range varSegment.FindAllStringSubmatch(uri, -1) {
		vars[match[1]] = match[2]
	}
	return vars
}

// GetPlainURI returns the URI with variable segment information removed.
func (h *HTTPRule) GetPlainURI() string {
	return plainVar.ReplaceAllString(
		varSegment.ReplaceAllString(
			templateSegment.ReplaceAllString(h.URI, "v"),
			"$2"),
		"*")
}

var plainVar = regexp.MustCompile(`\{([^}=]+)}`)
var varSegment = regexp.MustCompile(`\{([^}=]+)=([^}]+)\}`)
var templateSegment = regexp.MustCompile(`\{\$api_version\}`)
<commit_msg>fix: Make plainVar RegExp explicit and consistent. (#907)<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import (
	"regexp"

	"github.com/jhump/protoreflect/desc"
	apb "google.golang.org/genproto/googleapis/api/annotations"
	"google.golang.org/protobuf/proto"
)

// GetHTTPRules returns a slice of HTTP rules for a given method descriptor.
//
// Note: This returns a slice -- it takes the google.api.http annotation,
// and then flattens the values in `additional_bindings`.
// This allows rule authors to simply range over all of the HTTP rules,
// since the common case is to want to apply the checks to all of them.
func GetHTTPRules(m *desc.MethodDescriptor) []*HTTPRule {
	rules := []*HTTPRule{}

	// Get the method options.
	opts := m.GetMethodOptions()

	// Get the "primary" rule (the direct google.api.http annotation).
	if x := proto.GetExtension(opts, apb.E_Http); x != nil {
		httpRule := x.(*apb.HttpRule)
		if parsedRule := parseRule(httpRule); parsedRule != nil {
			rules = append(rules, parsedRule)

			// Add any additional bindings and flatten them into `rules`.
			for _, binding := range httpRule.GetAdditionalBindings() {
				rules = append(rules, parseRule(binding))
			}
		}
	}

	// Done; return the rules.
	return rules
}

func parseRule(rule *apb.HttpRule) *HTTPRule {
	oneof := map[string]string{
		"GET":    rule.GetGet(),
		"POST":   rule.GetPost(),
		"PUT":    rule.GetPut(),
		"PATCH":  rule.GetPatch(),
		"DELETE": rule.GetDelete(),
	}
	if custom := rule.GetCustom(); custom != nil {
		oneof[custom.GetKind()] = custom.GetPath()
	}
	for method, uri := range oneof {
		if uri != "" {
			return &HTTPRule{
				Method:       method,
				URI:          uri,
				Body:         rule.GetBody(),
				ResponseBody: rule.GetResponseBody(),
			}
		}
	}
	return nil
}

// HTTPRule defines a parsed, easier-to-query equivalent to `apb.HttpRule`.
type HTTPRule struct {
	// The HTTP method. Guaranteed to be in all caps.
	// This is set to "CUSTOM" if the Custom property is set.
	Method string

	// The HTTP URI (the value corresponding to the selected HTTP method).
	URI string

	// The `body` value forwarded from the generated proto's HttpRule.
	Body string

	// The `response_body` value forwarded from the generated proto's HttpRule.
	ResponseBody string
}

// GetVariables returns the variable segments in a URI as a map.
func (h *HTTPRule) GetVariables() map[string]string {
	vars := map[string]string{}

	// Replace the version template variable with "v".
	uri := templateSegment.ReplaceAllString(h.URI, "v")
	for _, match := range plainVar.FindAllStringSubmatch(uri, -1) {
		vars[match[1]] = "*"
	}
	for _, match := range varSegment.FindAllStringSubmatch(uri, -1) {
		vars[match[1]] = match[2]
	}
	return vars
}

// GetPlainURI returns the URI with variable segment information removed.
func (h *HTTPRule) GetPlainURI() string {
	return plainVar.ReplaceAllString(
		varSegment.ReplaceAllString(
			templateSegment.ReplaceAllString(h.URI, "v"),
			"$2"),
		"*")
}

var plainVar = regexp.MustCompile(`\{([^}=]+)\}`)
var varSegment = regexp.MustCompile(`\{([^}=]+)=([^}]+)\}`)
var templateSegment = regexp.MustCompile(`\{\$api_version\}`)
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package audit

import (
	"bufio"
	"bytes"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"reflect"
	"regexp"
	"strings"
	"testing"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apiserver"
	"k8s.io/kubernetes/pkg/auth/user"
	"k8s.io/kubernetes/pkg/util/sets"
)

type simpleResponseWriter struct {
	http.ResponseWriter
}

func (*simpleResponseWriter) WriteHeader(code int) {}

type fancyResponseWriter struct {
	simpleResponseWriter
}

func (*fancyResponseWriter) CloseNotify() <-chan bool { return nil }

func (*fancyResponseWriter) Flush() {}

func (*fancyResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { return nil, nil, nil }

func TestConstructResponseWriter(t *testing.T) {
	actual := decorateResponseWriter(&simpleResponseWriter{}, ioutil.Discard, "")
	switch v := actual.(type) {
	case *auditResponseWriter:
	default:
		t.Errorf("Expected auditResponseWriter, got %v", reflect.TypeOf(v))
	}

	actual = decorateResponseWriter(&fancyResponseWriter{}, ioutil.Discard, "")
	switch v := actual.(type) {
	case *fancyResponseWriterDelegator:
	default:
		t.Errorf("Expected fancyResponseWriterDelegator, got %v", reflect.TypeOf(v))
	}
}

type fakeHTTPHandler struct{}

func (*fakeHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(200)
}

type fakeRequestContextMapper struct{}

func (*fakeRequestContextMapper) Get(req *http.Request) (api.Context, bool) {
	return api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "admin"}), true

}

func (*fakeRequestContextMapper) Update(req *http.Request, context api.Context) error {
	return nil
}

func TestAudit(t *testing.T) {
	var buf bytes.Buffer
	attributeGetter := apiserver.NewRequestAttributeGetter(&fakeRequestContextMapper{},
		&apiserver.RequestInfoResolver{APIPrefixes: sets.NewString("api", "apis"), GrouplessAPIPrefixes: sets.NewString("api")})
	handler := WithAudit(&fakeHTTPHandler{}, attributeGetter, &buf)
	req, _ := http.NewRequest("GET", "/api/v1/namespaces/default/pods", nil)
	req.RemoteAddr = "127.0.0.1"
	handler.ServeHTTP(httptest.NewRecorder(), req)
	line := strings.Split(strings.TrimSpace(buf.String()), "\n")
	if len(line) != 2 {
		t.Fatalf("Unexpected amount of lines in audit log: %d", len(line))
	}
	match, err := regexp.MatchString(`[\d\:\-\.\+,T,Z]+ AUDIT: id="[\w-]+" ip="127.0.0.1" method="GET" user="admin" as="<self>" namespace="default" uri="/api/v1/namespaces/default/pods"`, line[0])
	if err != nil {
		t.Errorf("Unexpected error matching first line: %v", err)
	}
	if !match {
		t.Errorf("Unexpected first line of audit: %s", line[0])
	}
	match, err = regexp.MatchString(`[\d\:\-\.\+,T,Z]+ AUDIT: id="[\w-]+" response="200"`, line[1])
	if err != nil {
		t.Errorf("Unexpected error matching second line: %v", err)
	}
	if !match {
		t.Errorf("Unexpected second line of audit: %s", line[1])
	}
}
<commit_msg>Fixed edited regex in audit_test unit test<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package audit

import (
	"bufio"
	"bytes"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"reflect"
	"regexp"
	"strings"
	"testing"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apiserver"
	"k8s.io/kubernetes/pkg/auth/user"
	"k8s.io/kubernetes/pkg/util/sets"
)

type simpleResponseWriter struct {
	http.ResponseWriter
}

func (*simpleResponseWriter) WriteHeader(code int) {}

type fancyResponseWriter struct {
	simpleResponseWriter
}

func (*fancyResponseWriter) CloseNotify() <-chan bool { return nil }

func (*fancyResponseWriter) Flush() {}

func (*fancyResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { return nil, nil, nil }

func TestConstructResponseWriter(t *testing.T) {
	actual := decorateResponseWriter(&simpleResponseWriter{}, ioutil.Discard, "")
	switch v := actual.(type) {
	case *auditResponseWriter:
	default:
		t.Errorf("Expected auditResponseWriter, got %v", reflect.TypeOf(v))
	}

	actual = decorateResponseWriter(&fancyResponseWriter{}, ioutil.Discard, "")
	switch v := actual.(type) {
	case *fancyResponseWriterDelegator:
	default:
		t.Errorf("Expected fancyResponseWriterDelegator, got %v", reflect.TypeOf(v))
	}
}

type fakeHTTPHandler struct{}

func (*fakeHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(200)
}

type fakeRequestContextMapper struct{}

func (*fakeRequestContextMapper) Get(req *http.Request) (api.Context, bool) {
	return api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "admin"}), true

}

func (*fakeRequestContextMapper) Update(req *http.Request, context api.Context) error {
	return nil
}

func TestAudit(t *testing.T) {
	var buf bytes.Buffer
	attributeGetter := apiserver.NewRequestAttributeGetter(&fakeRequestContextMapper{},
		&apiserver.RequestInfoResolver{APIPrefixes: sets.NewString("api", "apis"), GrouplessAPIPrefixes: sets.NewString("api")})
	handler := WithAudit(&fakeHTTPHandler{}, attributeGetter, &buf)
	req, _ := http.NewRequest("GET", "/api/v1/namespaces/default/pods", nil)
	req.RemoteAddr = "127.0.0.1"
	handler.ServeHTTP(httptest.NewRecorder(), req)
	line := strings.Split(strings.TrimSpace(buf.String()), "\n")
	if len(line) != 2 {
		t.Fatalf("Unexpected amount of lines in audit log: %d", len(line))
	}
	match, err := regexp.MatchString(`[\d\:\-\.\+TZ]+ AUDIT: id="[\w-]+" ip="127.0.0.1" method="GET" user="admin" as="<self>" namespace="default" uri="/api/v1/namespaces/default/pods"`, line[0])
	if err != nil {
		t.Errorf("Unexpected error matching first line: %v", err)
	}
	if !match {
		t.Errorf("Unexpected first line of audit: %s", line[0])
	}
	match, err = regexp.MatchString(`[\d\:\-\.\+TZ]+ AUDIT: id="[\w-]+" response="200"`, line[1])
	if err != nil {
		t.Errorf("Unexpected error matching second line: %v", err)
	}
	if !match {
		t.Errorf("Unexpected second line of audit: %s", line[1])
	}
}
<|endoftext|>


<commit_before>package chrootarchive

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"syscall"

	"github.com/docker/docker/pkg/mount"
	rsystem "github.com/opencontainers/runc/libcontainer/system"
)

// chroot on linux uses pivot_root instead of chroot
// pivot_root takes a new root and an old root.
// Old root must be a sub-dir of new root, it is where the current rootfs will reside after the call to pivot_root.
// New root is where the new rootfs is set to.
// Old root is removed after the call to pivot_root so it is no longer available under the new root.
// This is similar to how libcontainer sets up a container's rootfs
func chroot(path string) (err error) {
	// if the engine is running in a user namespace we need to use actual chroot
	if rsystem.RunningInUserNS() {
		return realChroot(path)
	}
	if err := syscall.Unshare(syscall.CLONE_NEWNS); err != nil {
		return fmt.Errorf("Error creating mount namespace before pivot: %v", err)
	}

	if err := mount.MakeRPrivate(path); err != nil {
		return err
	}

	// setup oldRoot for pivot_root
	pivotDir, err := ioutil.TempDir(path, ".pivot_root")
	if err != nil {
		return fmt.Errorf("Error setting up pivot dir: %v", err)
	}

	var mounted bool
	defer func() {
		if mounted {
			// make sure pivotDir is not mounted before we try to remove it
			if errCleanup := syscall.Unmount(pivotDir, syscall.MNT_DETACH); errCleanup != nil {
				if err == nil {
					err = errCleanup
				}
				return
			}
		}

		errCleanup := os.Remove(pivotDir)
		// pivotDir doesn't exist if pivot_root failed and chroot+chdir was successful
		// because we already cleaned it up on failed pivot_root
		if errCleanup != nil && !os.IsNotExist(errCleanup) {
			errCleanup = fmt.Errorf("Error cleaning up after pivot: %v", errCleanup)
			if err == nil {
				err = errCleanup
			}
		}

		if errCleanup := syscall.Unmount("/", syscall.MNT_DETACH); errCleanup != nil {
			if err == nil {
				err = fmt.Errorf("error unmounting root: %v", errCleanup)
			}
			return
		}
	}()

	if err := syscall.PivotRoot(path, pivotDir); err != nil {
		// If pivot fails, fall back to the normal chroot after cleaning up temp dir
		if err := os.Remove(pivotDir); err != nil {
			return fmt.Errorf("Error cleaning up after failed pivot: %v", err)
		}
		return realChroot(path)
	}
	mounted = true

	// This is the new path for where the old root (prior to the pivot) has been moved to
	// This dir contains the rootfs of the caller, which we need to remove so it is not visible during extraction
	pivotDir = filepath.Join("/", filepath.Base(pivotDir))

	if err := syscall.Chdir("/"); err != nil {
		return fmt.Errorf("Error changing to new root: %v", err)
	}

	// Make the pivotDir (where the old root lives) private so it can be unmounted without propagating to the host
	if err := syscall.Mount("", pivotDir, "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil {
		return fmt.Errorf("Error making old root private after pivot: %v", err)
	}

	// Now unmount the old root so it's no longer visible from the new root
	if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil {
		return fmt.Errorf("Error while unmounting old root after pivot: %v", err)
	}
	mounted = false

	return nil
}

func realChroot(path string) error {
	if err := syscall.Chroot(path); err != nil {
		return fmt.Errorf("Error after fallback to chroot: %v", err)
	}
	if err := syscall.Chdir("/"); err != nil {
		return fmt.Errorf("Error changing to new root after chroot: %v", err)
	}
	return nil
}
<commit_msg>chroot: remount everything as private in new mntns<commit_after>package chrootarchive

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"syscall"

	"github.com/docker/docker/pkg/mount"
	rsystem "github.com/opencontainers/runc/libcontainer/system"
)

// chroot on linux uses pivot_root instead of chroot
// pivot_root takes a new root and an old root.
// Old root must be a sub-dir of new root, it is where the current rootfs will reside after the call to pivot_root.
// New root is where the new rootfs is set to.
// Old root is removed after the call to pivot_root so it is no longer available under the new root.
// This is similar to how libcontainer sets up a container's rootfs
func chroot(path string) (err error) {
	// if the engine is running in a user namespace we need to use actual chroot
	if rsystem.RunningInUserNS() {
		return realChroot(path)
	}
	if err := syscall.Unshare(syscall.CLONE_NEWNS); err != nil {
		return fmt.Errorf("Error creating mount namespace before pivot: %v", err)
	}

	// make everything in new ns private
	if err := mount.MakeRPrivate("/"); err != nil {
		return err
	}
	// ensure path is a mountpoint
	if err := mount.MakePrivate(path); err != nil {
		return err
	}

	// setup oldRoot for pivot_root
	pivotDir, err := ioutil.TempDir(path, ".pivot_root")
	if err != nil {
		return fmt.Errorf("Error setting up pivot dir: %v", err)
	}

	var mounted bool
	defer func() {
		if mounted {
			// make sure pivotDir is not mounted before we try to remove it
			if errCleanup := syscall.Unmount(pivotDir, syscall.MNT_DETACH); errCleanup != nil {
				if err == nil {
					err = errCleanup
				}
				return
			}
		}

		errCleanup := os.Remove(pivotDir)
		// pivotDir doesn't exist if pivot_root failed and chroot+chdir was successful
		// because we already cleaned it up on failed pivot_root
		if errCleanup != nil && !os.IsNotExist(errCleanup) {
			errCleanup = fmt.Errorf("Error cleaning up after pivot: %v", errCleanup)
			if err == nil {
				err = errCleanup
			}
		}

		if errCleanup := syscall.Unmount("/", syscall.MNT_DETACH); errCleanup != nil {
			if err == nil {
				err = fmt.Errorf("error unmounting root: %v", errCleanup)
			}
			return
		}
	}()

	if err := syscall.PivotRoot(path, pivotDir); err != nil {
		// If pivot fails, fall back to the normal chroot after cleaning up temp dir
		if err := os.Remove(pivotDir); err != nil {
			return fmt.Errorf("Error cleaning up after failed pivot: %v", err)
		}
		return realChroot(path)
	}
	mounted = true

	// This is the new path for where the old root (prior to the pivot) has been moved to
	// This dir contains the rootfs of the caller, which we need to remove so it is not visible during extraction
	pivotDir = filepath.Join("/", filepath.Base(pivotDir))

	if err := syscall.Chdir("/"); err != nil {
		return fmt.Errorf("Error changing to new root: %v", err)
	}

	// Make the pivotDir (where the old root lives) private so it can be unmounted without propagating to the host
	if err := syscall.Mount("", pivotDir, "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil {
		return fmt.Errorf("Error making old root private after pivot: %v", err)
	}

	// Now unmount the old root so it's no longer visible from the new root
	if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil {
		return fmt.Errorf("Error while unmounting old root after pivot: %v", err)
	}
	mounted = false

	return nil
}

func realChroot(path string) error {
	if err := syscall.Chroot(path); err != nil {
		return fmt.Errorf("Error after fallback to chroot: %v", err)
	}
	if err := syscall.Chdir("/"); err != nil {
		return fmt.Errorf("Error changing to new root after chroot: %v", err)
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Mirantis

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package libvirttools

import (
	"fmt"
	"os"

	libvirtxml "github.com/libvirt/libvirt-go-xml"

	"github.com/Mirantis/virtlet/pkg/virt"
)

// diskPath contains paths that can be used to locate a device inside
// the VM using Linux-specific /dev or /sys paths
type diskPath struct {
	// devPath denotes path to the device under /dev, e.g.
	// /dev/disk/by-path/virtio-pci-0000:00:03.0-scsi-0:0:0:0 or
	// /dev/disk/by-path/pci-0000:00:03.0-virtio-pci-0000:01:01.0
	devPath string
	// sysfsPath denotes a path to a directory in sysfs that
	// contains a file with the same name as the device in /dev, e.g.
	// /sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/virtio*/block/ or
	// /sys/devices/pci0000:00/0000:00:03.0/virtio*/host*/target*:0:0/*:0:0:0/block/sda
	// (note that in the latter case * is used instead of host because host number appear
	// to be wrong in sysfs for some reason)
	// The path needs to be globbed and the single file name from there
	// should be used as device name, e.g.
	// ls -l /dev/`ls /sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/virtio*/block/`
	sysfsPath string
}

// diskPathMap maps volume uuids to diskPath items
type diskPathMap map[string]diskPath

var supportedStoragePools = map[string]string{
	"default": "/var/lib/libvirt/images",
	"volumes": "/var/lib/virtlet/volumes",
}

func ensureStoragePool(conn virt.VirtStorageConnection, name string) (virt.VirtStoragePool, error) {
	poolDir, found := supportedStoragePools[name]
	if !found {
		return nil, fmt.Errorf("pool with name '%s' is unknown", name)
	}

	pool, err := conn.LookupStoragePoolByName(name)
	if err == nil {
		return pool, nil
	}
	return conn.CreateStoragePool(&libvirtxml.StoragePool{
		Type:   "dir",
		Name:   name,
		Target: &libvirtxml.StoragePoolTarget{Path: poolDir},
	})
}

func verifyRawDeviceAccess(path string) error {
	// TODO: verify access rights for qemu process to this path
	pathInfo, err := os.Stat(path)
	if err != nil {
		return err
	}

	// is this device and not char device?
	if pathInfo.Mode()&os.ModeDevice != 0 && pathInfo.Mode()&os.ModeCharDevice == 0 {
		return nil
	}

	return fmt.Errorf("path '%s' points to something other than block device", path)
}
<commit_msg>Fix raw device test on Mac<commit_after>/*
Copyright 2017 Mirantis

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package libvirttools

import (
	"fmt"
	"os"
	"runtime"

	libvirtxml "github.com/libvirt/libvirt-go-xml"

	"github.com/Mirantis/virtlet/pkg/virt"
)

// diskPath contains paths that can be used to locate a device inside
// the VM using Linux-specific /dev or /sys paths
type diskPath struct {
	// devPath denotes path to the device under /dev, e.g.
	// /dev/disk/by-path/virtio-pci-0000:00:03.0-scsi-0:0:0:0 or
	// /dev/disk/by-path/pci-0000:00:03.0-virtio-pci-0000:01:01.0
	devPath string
	// sysfsPath denotes a path to a directory in sysfs that
	// contains a file with the same name as the device in /dev, e.g.
	// /sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/virtio*/block/ or
	// /sys/devices/pci0000:00/0000:00:03.0/virtio*/host*/target*:0:0/*:0:0:0/block/sda
	// (note that in the latter case * is used instead of host because host number appear
	// to be wrong in sysfs for some reason)
	// The path needs to be globbed and the single file name from there
	// should be used as device name, e.g.
	// ls -l /dev/`ls /sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/virtio*/block/`
	sysfsPath string
}

// diskPathMap maps volume uuids to diskPath items
type diskPathMap map[string]diskPath

var supportedStoragePools = map[string]string{
	"default": "/var/lib/libvirt/images",
	"volumes": "/var/lib/virtlet/volumes",
}

func ensureStoragePool(conn virt.VirtStorageConnection, name string) (virt.VirtStoragePool, error) {
	poolDir, found := supportedStoragePools[name]
	if !found {
		return nil, fmt.Errorf("pool with name '%s' is unknown", name)
	}

	pool, err := conn.LookupStoragePoolByName(name)
	if err == nil {
		return pool, nil
	}
	return conn.CreateStoragePool(&libvirtxml.StoragePool{
		Type:   "dir",
		Name:   name,
		Target: &libvirtxml.StoragePoolTarget{Path: poolDir},
	})
}

func verifyRawDeviceAccess(path string) error {
	// XXX: make tests pass on non-Linux systems
	if runtime.GOOS != "linux" {
		return nil
	}

	// TODO: verify access rights for qemu process to this path
	pathInfo, err := os.Stat(path)
	if err != nil {
		return err
	}

	// is this device and not char device?
	if pathInfo.Mode()&os.ModeDevice != 0 && pathInfo.Mode()&os.ModeCharDevice == 0 {
		return nil
	}

	return fmt.Errorf("path '%s' points to something other than block device", path)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Mirantis

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package libvirttools

import (
	"fmt"
	"os"

	libvirtxml "github.com/libvirt/libvirt-go-xml"

	"github.com/Mirantis/virtlet/pkg/virt"
)

// diskPath contains paths that can be used to locate a device inside
// the VM using Linux-specific /dev or /sys paths
type diskPath struct {
	// devPath denotes path to the device under /dev, e.g.
	// /dev/disk/by-path/virtio-pci-0000:00:03.0-scsi-0:0:0:0 or
	// /dev/disk/by-path/pci-0000:00:03.0-virtio-pci-0000:01:01.0
	devPath string
	// sysfsPath denotes a path to a directory in sysfs that
	// contains a file with the same name as the device in /dev, e.g.
	// /sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/virtio*/block/ or
	// /sys/devices/pci0000:00/0000:00:03.0/virtio*/host*/target*:0:0/*:0:0:0/block/sda
	// (note that in the latter case * is used instead of host because host number appear
	// to be wrong in sysfs for some reason)
	// The path needs to be globbed and the single file name from there
	// should be used as device name, e.g.
	// ls -l /dev/`ls /sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/virtio*/block/`
	sysfsPath string
}

// diskPathMap maps volume uuids to diskPath items
type diskPathMap map[string]diskPath

var supportedStoragePools = map[string]string{
	"default": "/var/lib/libvirt/images",
	"volumes": "/var/lib/virtlet/volumes",
}

func ensureStoragePool(conn virt.VirtStorageConnection, name string) (virt.VirtStoragePool, error) {
	poolDir, found := supportedStoragePools[name]
	if !found {
		return nil, fmt.Errorf("pool with name '%s' is unknown", name)
	}

	pool, err := conn.LookupStoragePoolByName(name)
	if err == nil {
		return pool, nil
	}
	return conn.CreateStoragePool(&libvirtxml.StoragePool{
		Type:   "dir",
		Name:   name,
		Target: &libvirtxml.StoragePoolTarget{Path: poolDir},
	})
}

func verifyRawDeviceAccess(path string) error {
	// TODO: verify access rights for qemu process to this path
	pathInfo, err := os.Stat(path)
	if err != nil {
		return err
	}

	// is this device and not char device?
	if pathInfo.Mode()&os.ModeDevice != 0 && pathInfo.Mode()&os.ModeCharDevice == 0 {
		return nil
	}

	return fmt.Errorf("path '%s' points to something other than block device", path)
}
<commit_msg>Remove unused storage pool<commit_after>/*
Copyright 2017 Mirantis

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package libvirttools

import (
	"fmt"
	"os"

	libvirtxml "github.com/libvirt/libvirt-go-xml"

	"github.com/Mirantis/virtlet/pkg/virt"
)

// diskPath contains paths that can be used to locate a device inside
// the VM using Linux-specific /dev or /sys paths
type diskPath struct {
	// devPath denotes path to the device under /dev, e.g.
	// /dev/disk/by-path/virtio-pci-0000:00:03.0-scsi-0:0:0:0 or
	// /dev/disk/by-path/pci-0000:00:03.0-virtio-pci-0000:01:01.0
	devPath string
	// sysfsPath denotes a path to a directory in sysfs that
	// contains a file with the same name as the device in /dev, e.g.
	// /sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/virtio*/block/ or
	// /sys/devices/pci0000:00/0000:00:03.0/virtio*/host*/target*:0:0/*:0:0:0/block/sda
	// (note that in the latter case * is used instead of host because host number appear
	// to be wrong in sysfs for some reason)
	// The path needs to be globbed and the single file name from there
	// should be used as device name, e.g.
	// ls -l /dev/`ls /sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/virtio*/block/`
	sysfsPath string
}

// diskPathMap maps volume uuids to diskPath items
type diskPathMap map[string]diskPath

var supportedStoragePools = map[string]string{
	"volumes": "/var/lib/virtlet/volumes",
}

func ensureStoragePool(conn virt.VirtStorageConnection, name string) (virt.VirtStoragePool, error) {
	poolDir, found := supportedStoragePools[name]
	if !found {
		return nil, fmt.Errorf("pool with name '%s' is unknown", name)
	}

	pool, err := conn.LookupStoragePoolByName(name)
	if err == nil {
		return pool, nil
	}
	return conn.CreateStoragePool(&libvirtxml.StoragePool{
		Type:   "dir",
		Name:   name,
		Target: &libvirtxml.StoragePoolTarget{Path: poolDir},
	})
}

func verifyRawDeviceAccess(path string) error {
	// TODO: verify access rights for qemu process to this path
	pathInfo, err := os.Stat(path)
	if err != nil {
		return err
	}

	// is this device and not char device?
	if pathInfo.Mode()&os.ModeDevice != 0 && pathInfo.Mode()&os.ModeCharDevice == 0 {
		return nil
	}

	return fmt.Errorf("path '%s' points to something other than block device", path)
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package download

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

	"github.com/golang/glog"
	"github.com/hashicorp/go-getter"
	"github.com/pkg/errors"
)

var (
	// Mock allows tests to toggle making actual HTTP downloads
	Mock = false
)

// download is a well-configured atomic download function
func download(src string, dst string) error {
	tmpDst := dst + ".download"
	client := &getter.Client{
		Src:     src,
		Dst:     tmpDst,
		Dir:     false,
		Mode:    getter.ClientModeFile,
		Options: []getter.ClientOption{getter.WithProgress(DefaultProgressBar)},
		Getters: map[string]getter.Getter{
			"file":  &getter.FileGetter{Copy: false},
			"http":  &getter.HttpGetter{Netrc: false},
			"https": &getter.HttpGetter{Netrc: false},
		},
	}

	// Don't bother with getter.MockGetter, as we don't provide a way to inspect the outcome
	if Mock {
		glog.Infof("Mock download: %s -> %s", src, dst)
		return nil
	}

	// Politely prevent tests from shooting themselves in the foot
	if underTest() {
		return fmt.Errorf("unmocked download under test, set download.Mock=true")
	}

	if err := os.MkdirAll(filepath.Dir(dst), 0750); err != nil {
		return errors.Wrap(err, "mkdir")
	}

	glog.Infof("Downloading: %s -> %s", src, dst)
	if err := client.Get(); err != nil {
		return errors.Wrapf(err, "getter: %+v", client)
	}
	return os.Rename(tmpDst, dst)
}

// detect if we are under test
func underTest() bool {
	return flag.Lookup("test.v") != nil || strings.HasSuffix(os.Args[0], "test")
}
<commit_msg>Ensure that there is an output file to operate on<commit_after>/*
Copyright 2020 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package download

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

	"github.com/golang/glog"
	"github.com/hashicorp/go-getter"
	"github.com/pkg/errors"
)

var (
	// Mock allows tests to toggle making actual HTTP downloads
	Mock = false
)

// download is a well-configured atomic download function
func download(src string, dst string) error {
	tmpDst := dst + ".download"
	client := &getter.Client{
		Src:     src,
		Dst:     tmpDst,
		Dir:     false,
		Mode:    getter.ClientModeFile,
		Options: []getter.ClientOption{getter.WithProgress(DefaultProgressBar)},
		Getters: map[string]getter.Getter{
			"file":  &getter.FileGetter{Copy: false},
			"http":  &getter.HttpGetter{Netrc: false},
			"https": &getter.HttpGetter{Netrc: false},
		},
	}

	if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
		return errors.Wrap(err, "mkdir")
	}

	// Don't bother with getter.MockGetter, as we don't provide a way to inspect the outcome
	if Mock {
		glog.Infof("Mock download: %s -> %s", src, dst)
		// Callers expect the file to exist
		_, err := os.Create(dst)
		return err
	}

	// Politely prevent tests from shooting themselves in the foot
	if underTest() {
		return fmt.Errorf("unmocked download under test")
	}

	glog.Infof("Downloading: %s -> %s", src, dst)
	if err := client.Get(); err != nil {
		return errors.Wrapf(err, "getter: %+v", client)
	}
	return os.Rename(tmpDst, dst)
}

// detect if we are under test
func underTest() bool {
	return flag.Lookup("test.v") != nil || strings.HasSuffix(os.Args[0], "test")
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package testing

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"path"
	"testing"
	"time"

	etcd "github.com/coreos/etcd/client"
	"github.com/coreos/etcd/etcdserver"
	"github.com/coreos/etcd/etcdserver/etcdhttp"
	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/etcd/rafthttp"
	"github.com/golang/glog"
	"golang.org/x/net/context"
)

// EtcdTestServer encapsulates the datastructures needed to start local instance for testing
type EtcdTestServer struct {
	etcdserver.ServerConfig
	PeerListeners, ClientListeners []net.Listener
	Client                         etcd.Client

	CertificatesDir string
	CertFile        string
	KeyFile         string
	CAFile          string

	raftHandler http.Handler
	s           *etcdserver.EtcdServer
	hss         []*httptest.Server
}

// newLocalListener opens a port localhost using any port
func newLocalListener(t *testing.T) net.Listener {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	return l
}

// newSecuredLocalListener opens a port localhost using any port
// with SSL enable
func newSecuredLocalListener(t *testing.T, certFile, keyFile, caFile string) net.Listener {
	var l net.Listener
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	tlsInfo := transport.TLSInfo{
		CertFile: certFile,
		KeyFile:  keyFile,
		CAFile:   caFile,
	}
	l, err = transport.NewKeepAliveListener(l, "https", tlsInfo)
	if err != nil {
		t.Fatal(err)
	}
	return l
}

func newHttpTransport(t *testing.T, certFile, keyFile, caFile string) etcd.CancelableTransport {
	tlsInfo := transport.TLSInfo{
		CertFile: certFile,
		KeyFile:  keyFile,
		CAFile:   caFile,
	}
	tr, err := transport.NewTransport(tlsInfo)
	if err != nil {
		t.Fatal(err)
	}
	return tr
}

// configureTestCluster will set the params to start an etcd server
func configureTestCluster(t *testing.T, name string) *EtcdTestServer {
	var err error
	m := &EtcdTestServer{}

	pln := newLocalListener(t)
	m.PeerListeners = []net.Listener{pln}
	m.PeerURLs, err = types.NewURLs([]string{"http://" + pln.Addr().String()})
	if err != nil {
		t.Fatal(err)
	}

	m.CertificatesDir, err = ioutil.TempDir(os.TempDir(), "etcd_certificates")
	if err != nil {
		t.Fatal(err)
	}
	m.CertFile = path.Join(m.CertificatesDir, "etcdcert.pem")
	if err = ioutil.WriteFile(m.CertFile, []byte(CertFileContent), 0644); err != nil {
		t.Fatal(err)
	}
	m.KeyFile = path.Join(m.CertificatesDir, "etcdkey.pem")
	if err = ioutil.WriteFile(m.KeyFile, []byte(KeyFileContent), 0644); err != nil {
		t.Fatal(err)
	}
	m.CAFile = path.Join(m.CertificatesDir, "ca.pem")
	if err = ioutil.WriteFile(m.CAFile, []byte(CAFileContent), 0644); err != nil {
		t.Fatal(err)
	}

	cln := newSecuredLocalListener(t, m.CertFile, m.KeyFile, m.CAFile)
	m.ClientListeners = []net.Listener{cln}
	m.ClientURLs, err = types.NewURLs([]string{"https://" + cln.Addr().String()})
	if err != nil {
		t.Fatal(err)
	}

	m.Name = name
	m.DataDir, err = ioutil.TempDir(os.TempDir(), "etcd")
	if err != nil {
		t.Fatal(err)
	}

	clusterStr := fmt.Sprintf("%s=http://%s", name, pln.Addr().String())
	m.InitialPeerURLsMap, err = types.NewURLsMap(clusterStr)
	if err != nil {
		t.Fatal(err)
	}
	m.Transport, err = transport.NewTimeoutTransport(transport.TLSInfo{}, time.Second, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
	if err != nil {
		t.Fatal(err)
	}
	m.NewCluster = true
	m.ForceNewCluster = false
	m.ElectionTicks = 10
	m.TickMs = uint(10)

	return m
}

// launch will attempt to start the etcd server
func (m *EtcdTestServer) launch(t *testing.T) error {
	var err error
	if m.s, err = etcdserver.NewServer(&m.ServerConfig); err != nil {
		return fmt.Errorf("failed to initialize the etcd server: %v", err)
	}
	m.s.SyncTicker = time.Tick(500 * time.Millisecond)
	m.s.Start()
	m.raftHandler = etcdhttp.NewPeerHandler(m.s.Cluster(), m.s.RaftHandler())
	for _, ln := range m.PeerListeners {
		hs := &httptest.Server{
			Listener: ln,
			Config:   &http.Server{Handler: m.raftHandler},
		}
		hs.Start()
		m.hss = append(m.hss, hs)
	}
	for _, ln := range m.ClientListeners {
		hs := &httptest.Server{
			Listener: ln,
			Config:   &http.Server{Handler: etcdhttp.NewClientHandler(m.s, m.ServerConfig.ReqTimeout())},
		}
		hs.Start()
		m.hss = append(m.hss, hs)
	}
	return nil
}

// waitForEtcd wait until etcd is propagated correctly
func (m *EtcdTestServer) waitUntilUp() error {
	membersAPI := etcd.NewMembersAPI(m.Client)
	for start := time.Now(); time.Since(start) < 5*time.Second; time.Sleep(10 * time.Millisecond) {
		members, err := membersAPI.List(context.TODO())
		if err != nil {
			glog.Errorf("Error when getting etcd cluster members")
			continue
		}
		if len(members) == 1 && len(members[0].ClientURLs) > 0 {
			return nil
		}
	}
	return fmt.Errorf("timeout on waiting for etcd cluster")
}

// Terminate will shutdown the running etcd server
func (m *EtcdTestServer) Terminate(t *testing.T) {
	m.Client = nil
	m.s.Stop()
	// TODO: This is a pretty ugly hack to workaround races during closing
	// in-memory etcd server in unit tests - see #18928 for more details.
	// We should get rid of it as soon as we have a proper fix - etcd clients
	// have overwritten transport counting opened connections (probably by
	// overwriting Dial function) and termination function waiting for all
	// connections to be closed and stopping accepting new ones.
	time.Sleep(250 * time.Millisecond)
	for _, hs := range m.hss {
		hs.CloseClientConnections()
		// TODO: Uncomment when fix #19254
		// hs.Close()
	}
	if err := os.RemoveAll(m.ServerConfig.DataDir); err != nil {
		t.Fatal(err)
	}
	if err := os.RemoveAll(m.CertificatesDir); err != nil {
		t.Fatal(err)
	}
}

// NewEtcdTestClientServer creates a new client and server for testing
func NewEtcdTestClientServer(t *testing.T) *EtcdTestServer {
	server := configureTestCluster(t, "foo")
	err := server.launch(t)
	if err != nil {
		t.Fatal("Failed to start etcd server error=%v", err)
		return nil
	}
	cfg := etcd.Config{
		Endpoints: server.ClientURLs.StringSlice(),
		Transport: newHttpTransport(t, server.CertFile, server.KeyFile, server.CAFile),
	}
	server.Client, err = etcd.New(cfg)
	if err != nil {
		t.Errorf("Unexpected error in NewEtcdTestClientServer (%v)", err)
		server.Terminate(t)
		return nil
	}
	if err := server.waitUntilUp(); err != nil {
		t.Errorf("Unexpected error in waitUntilUp (%v)", err)
		server.Terminate(t)
		return nil
	}
	return server
}
<commit_msg>use Fatalf<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package testing

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"path"
	"testing"
	"time"

	etcd "github.com/coreos/etcd/client"
	"github.com/coreos/etcd/etcdserver"
	"github.com/coreos/etcd/etcdserver/etcdhttp"
	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/etcd/rafthttp"
	"github.com/golang/glog"
	"golang.org/x/net/context"
)

// EtcdTestServer encapsulates the datastructures needed to start local instance for testing
type EtcdTestServer struct {
	etcdserver.ServerConfig
	PeerListeners, ClientListeners []net.Listener
	Client                         etcd.Client

	CertificatesDir string
	CertFile        string
	KeyFile         string
	CAFile          string

	raftHandler http.Handler
	s           *etcdserver.EtcdServer
	hss         []*httptest.Server
}

// newLocalListener opens a port localhost using any port
func newLocalListener(t *testing.T) net.Listener {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	return l
}

// newSecuredLocalListener opens a port localhost using any port
// with SSL enable
func newSecuredLocalListener(t *testing.T, certFile, keyFile, caFile string) net.Listener {
	var l net.Listener
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	tlsInfo := transport.TLSInfo{
		CertFile: certFile,
		KeyFile:  keyFile,
		CAFile:   caFile,
	}
	l, err = transport.NewKeepAliveListener(l, "https", tlsInfo)
	if err != nil {
		t.Fatal(err)
	}
	return l
}

func newHttpTransport(t *testing.T, certFile, keyFile, caFile string) etcd.CancelableTransport {
	tlsInfo := transport.TLSInfo{
		CertFile: certFile,
		KeyFile:  keyFile,
		CAFile:   caFile,
	}
	tr, err := transport.NewTransport(tlsInfo)
	if err != nil {
		t.Fatal(err)
	}
	return tr
}

// configureTestCluster will set the params to start an etcd server
func configureTestCluster(t *testing.T, name string) *EtcdTestServer {
	var err error
	m := &EtcdTestServer{}

	pln := newLocalListener(t)
	m.PeerListeners = []net.Listener{pln}
	m.PeerURLs, err = types.NewURLs([]string{"http://" + pln.Addr().String()})
	if err != nil {
		t.Fatal(err)
	}

	m.CertificatesDir, err = ioutil.TempDir(os.TempDir(), "etcd_certificates")
	if err != nil {
		t.Fatal(err)
	}
	m.CertFile = path.Join(m.CertificatesDir, "etcdcert.pem")
	if err = ioutil.WriteFile(m.CertFile, []byte(CertFileContent), 0644); err != nil {
		t.Fatal(err)
	}
	m.KeyFile = path.Join(m.CertificatesDir, "etcdkey.pem")
	if err = ioutil.WriteFile(m.KeyFile, []byte(KeyFileContent), 0644); err != nil {
		t.Fatal(err)
	}
	m.CAFile = path.Join(m.CertificatesDir, "ca.pem")
	if err = ioutil.WriteFile(m.CAFile, []byte(CAFileContent), 0644); err != nil {
		t.Fatal(err)
	}

	cln := newSecuredLocalListener(t, m.CertFile, m.KeyFile, m.CAFile)
	m.ClientListeners = []net.Listener{cln}
	m.ClientURLs, err = types.NewURLs([]string{"https://" + cln.Addr().String()})
	if err != nil {
		t.Fatal(err)
	}

	m.Name = name
	m.DataDir, err = ioutil.TempDir(os.TempDir(), "etcd")
	if err != nil {
		t.Fatal(err)
	}

	clusterStr := fmt.Sprintf("%s=http://%s", name, pln.Addr().String())
	m.InitialPeerURLsMap, err = types.NewURLsMap(clusterStr)
	if err != nil {
		t.Fatal(err)
	}
	m.Transport, err = transport.NewTimeoutTransport(transport.TLSInfo{}, time.Second, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
	if err != nil {
		t.Fatal(err)
	}
	m.NewCluster = true
	m.ForceNewCluster = false
	m.ElectionTicks = 10
	m.TickMs = uint(10)

	return m
}

// launch will attempt to start the etcd server
func (m *EtcdTestServer) launch(t *testing.T) error {
	var err error
	if m.s, err = etcdserver.NewServer(&m.ServerConfig); err != nil {
		return fmt.Errorf("failed to initialize the etcd server: %v", err)
	}
	m.s.SyncTicker = time.Tick(500 * time.Millisecond)
	m.s.Start()
	m.raftHandler = etcdhttp.NewPeerHandler(m.s.Cluster(), m.s.RaftHandler())
	for _, ln := range m.PeerListeners {
		hs := &httptest.Server{
			Listener: ln,
			Config:   &http.Server{Handler: m.raftHandler},
		}
		hs.Start()
		m.hss = append(m.hss, hs)
	}
	for _, ln := range m.ClientListeners {
		hs := &httptest.Server{
			Listener: ln,
			Config:   &http.Server{Handler: etcdhttp.NewClientHandler(m.s, m.ServerConfig.ReqTimeout())},
		}
		hs.Start()
		m.hss = append(m.hss, hs)
	}
	return nil
}

// waitForEtcd wait until etcd is propagated correctly
func (m *EtcdTestServer) waitUntilUp() error {
	membersAPI := etcd.NewMembersAPI(m.Client)
	for start := time.Now(); time.Since(start) < 5*time.Second; time.Sleep(10 * time.Millisecond) {
		members, err := membersAPI.List(context.TODO())
		if err != nil {
			glog.Errorf("Error when getting etcd cluster members")
			continue
		}
		if len(members) == 1 && len(members[0].ClientURLs) > 0 {
			return nil
		}
	}
	return fmt.Errorf("timeout on waiting for etcd cluster")
}

// Terminate will shutdown the running etcd server
func (m *EtcdTestServer) Terminate(t *testing.T) {
	m.Client = nil
	m.s.Stop()
	// TODO: This is a pretty ugly hack to workaround races during closing
	// in-memory etcd server in unit tests - see #18928 for more details.
	// We should get rid of it as soon as we have a proper fix - etcd clients
	// have overwritten transport counting opened connections (probably by
	// overwriting Dial function) and termination function waiting for all
	// connections to be closed and stopping accepting new ones.
	time.Sleep(250 * time.Millisecond)
	for _, hs := range m.hss {
		hs.CloseClientConnections()
		// TODO: Uncomment when fix #19254
		// hs.Close()
	}
	if err := os.RemoveAll(m.ServerConfig.DataDir); err != nil {
		t.Fatal(err)
	}
	if err := os.RemoveAll(m.CertificatesDir); err != nil {
		t.Fatal(err)
	}
}

// NewEtcdTestClientServer creates a new client and server for testing
func NewEtcdTestClientServer(t *testing.T) *EtcdTestServer {
	server := configureTestCluster(t, "foo")
	err := server.launch(t)
	if err != nil {
		t.Fatalf("Failed to start etcd server error=%v", err)
		return nil
	}
	cfg := etcd.Config{
		Endpoints: server.ClientURLs.StringSlice(),
		Transport: newHttpTransport(t, server.CertFile, server.KeyFile, server.CAFile),
	}
	server.Client, err = etcd.New(cfg)
	if err != nil {
		t.Errorf("Unexpected error in NewEtcdTestClientServer (%v)", err)
		server.Terminate(t)
		return nil
	}
	if err := server.waitUntilUp(); err != nil {
		t.Errorf("Unexpected error in waitUntilUp (%v)", err)
		server.Terminate(t)
		return nil
	}
	return server
}
<|endoftext|>


<commit_before>package seccomp

import (
	"fmt"
	"strconv"
	"strings"

	rspec "github.com/opencontainers/runtime-spec/specs-go"
)

// SyscallOpts contain options for parsing syscall rules
type SyscallOpts struct {
	Action   string
	Syscall  string
	Index    string
	Value    string
	ValueTwo string
	Operator string
}

// ParseSyscallFlag takes a SyscallOpts struct and the seccomp configuration
// and sets the new syscall rule accordingly
func ParseSyscallFlag(args SyscallOpts, config *rspec.LinuxSeccomp) error {
	var arguments []string
	if args.Index != "" && args.Value != "" && args.ValueTwo != "" && args.Operator != "" {
		arguments = []string{args.Action, args.Syscall, args.Index, args.Value,
			args.ValueTwo, args.Operator}
	} else {
		arguments = []string{args.Action, args.Syscall}
	}

	action, _ := parseAction(arguments[0])
	if action == config.DefaultAction {
		return fmt.Errorf("default action already set as %s", action)
	}

	var newSyscall rspec.LinuxSyscall
	numOfArgs := len(arguments)
	if numOfArgs == 6 || numOfArgs == 2 {
		argStruct, err := parseArguments(arguments[1:])
		if err != nil {
			return err
		}
		newSyscall = newSyscallStruct(arguments[1], action, argStruct)
	} else {
		return fmt.Errorf("incorrect number of arguments to ParseSyscall: %d", numOfArgs)
	}

	descison, err := decideCourseOfAction(&newSyscall, config.Syscalls)
	if err != nil {
		return err
	}
	delimDescison := strings.Split(descison, ":")

	if delimDescison[0] == seccompAppend {
		config.Syscalls = append(config.Syscalls, newSyscall)
	}

	if delimDescison[0] == seccompOverwrite {
		indexForOverwrite, err := strconv.ParseInt(delimDescison[1], 10, 32)
		if err != nil {
			return err
		}
		config.Syscalls[indexForOverwrite] = newSyscall
	}

	return nil
}

var actions = map[string]rspec.LinuxSeccompAction{
	"allow": rspec.ActAllow,
	"errno": rspec.ActErrno,
	"kill":  rspec.ActKill,
	"trace": rspec.ActTrace,
	"trap":  rspec.ActTrap,
}

// Take passed action, return the SCMP_ACT_<ACTION> version of it
func parseAction(action string) (rspec.LinuxSeccompAction, error) {
	a, ok := actions[action]
	if !ok {
		return "", fmt.Errorf("unrecognized action: %s", action)
	}
	return a, nil
}

// ParseDefaultAction sets the default action of the seccomp configuration
// and then removes any rules that were already specified with this action
func ParseDefaultAction(action string, config *rspec.LinuxSeccomp) error {
	if action == "" {
		return nil
	}

	defaultAction, err := parseAction(action)
	if err != nil {
		return err
	}
	config.DefaultAction = defaultAction
	err = RemoveAllMatchingRules(config, action)
	if err != nil {
		return err
	}
	return nil
}

// ParseDefaultActionForce simply sets the default action of the seccomp configuration
func ParseDefaultActionForce(action string, config *rspec.LinuxSeccomp) error {
	if action == "" {
		return nil
	}

	defaultAction, err := parseAction(action)
	if err != nil {
		return err
	}
	config.DefaultAction = defaultAction
	return nil
}

func newSyscallStruct(name string, action rspec.LinuxSeccompAction, args []rspec.LinuxSeccompArg) rspec.LinuxSyscall {
	syscallStruct := rspec.LinuxSyscall{
		Names:  []string{name},
		Action: action,
		Args:   args,
	}
	return syscallStruct
}
<commit_msg>remove error return<commit_after>package seccomp

import (
	"fmt"
	"strconv"
	"strings"

	rspec "github.com/opencontainers/runtime-spec/specs-go"
)

// SyscallOpts contain options for parsing syscall rules
type SyscallOpts struct {
	Action   string
	Syscall  string
	Index    string
	Value    string
	ValueTwo string
	Operator string
}

// ParseSyscallFlag takes a SyscallOpts struct and the seccomp configuration
// and sets the new syscall rule accordingly
func ParseSyscallFlag(args SyscallOpts, config *rspec.LinuxSeccomp) error {
	var arguments []string
	if args.Index != "" && args.Value != "" && args.ValueTwo != "" && args.Operator != "" {
		arguments = []string{args.Action, args.Syscall, args.Index, args.Value,
			args.ValueTwo, args.Operator}
	} else {
		arguments = []string{args.Action, args.Syscall}
	}

	action, _ := parseAction(arguments[0])
	if action == config.DefaultAction && args.argsAreEmpty() {
		// default already set, no need to make changes
		return nil
	}

	var newSyscall rspec.LinuxSyscall
	numOfArgs := len(arguments)
	if numOfArgs == 6 || numOfArgs == 2 {
		argStruct, err := parseArguments(arguments[1:])
		if err != nil {
			return err
		}
		newSyscall = newSyscallStruct(arguments[1], action, argStruct)
	} else {
		return fmt.Errorf("incorrect number of arguments to ParseSyscall: %d", numOfArgs)
	}

	descison, err := decideCourseOfAction(&newSyscall, config.Syscalls)
	if err != nil {
		return err
	}
	delimDescison := strings.Split(descison, ":")

	if delimDescison[0] == seccompAppend {
		config.Syscalls = append(config.Syscalls, newSyscall)
	}

	if delimDescison[0] == seccompOverwrite {
		indexForOverwrite, err := strconv.ParseInt(delimDescison[1], 10, 32)
		if err != nil {
			return err
		}
		config.Syscalls[indexForOverwrite] = newSyscall
	}

	return nil
}

var actions = map[string]rspec.LinuxSeccompAction{
	"allow": rspec.ActAllow,
	"errno": rspec.ActErrno,
	"kill":  rspec.ActKill,
	"trace": rspec.ActTrace,
	"trap":  rspec.ActTrap,
}

// Take passed action, return the SCMP_ACT_<ACTION> version of it
func parseAction(action string) (rspec.LinuxSeccompAction, error) {
	a, ok := actions[action]
	if !ok {
		return "", fmt.Errorf("unrecognized action: %s", action)
	}
	return a, nil
}

// ParseDefaultAction sets the default action of the seccomp configuration
// and then removes any rules that were already specified with this action
func ParseDefaultAction(action string, config *rspec.LinuxSeccomp) error {
	if action == "" {
		return nil
	}

	defaultAction, err := parseAction(action)
	if err != nil {
		return err
	}
	config.DefaultAction = defaultAction
	err = RemoveAllMatchingRules(config, action)
	if err != nil {
		return err
	}
	return nil
}

// ParseDefaultActionForce simply sets the default action of the seccomp configuration
func ParseDefaultActionForce(action string, config *rspec.LinuxSeccomp) error {
	if action == "" {
		return nil
	}

	defaultAction, err := parseAction(action)
	if err != nil {
		return err
	}
	config.DefaultAction = defaultAction
	return nil
}

func newSyscallStruct(name string, action rspec.LinuxSeccompAction, args []rspec.LinuxSeccompArg) rspec.LinuxSyscall {
	syscallStruct := rspec.LinuxSyscall{
		Names:  []string{name},
		Action: action,
		Args:   args,
	}
	return syscallStruct
}

func (s SyscallOpts) argsAreEmpty() bool {
	return (s.Index == "" &&
		s.Value == "" &&
		s.ValueTwo == "" &&
		s.Operator == "")
}
<|endoftext|>


<commit_before>// +build s3

package builder

import (
	"io"
	"testing"
)

func TestS3Logger(t *testing.T) {
	f, _ := S3Logger("conveyor-data")
	w, _ := f(BuildOptions{
		Repository: "test",
		Branch:     "master",
		Sha:        "abcd",
	})

	io.WriteString(w, "Foobar")
	if err := w.Close(); err != nil {
		t.Fatal(err)
	}
}
<commit_msg>Remove unused test file.<commit_after><|endoftext|>