<commit_before>package providers

import (
	"github.com/hashicorp/terraform/configs/configschema"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"
)

// Interface represents the set of methods required for a complete resource
// provider plugin.
type Interface interface {
	// GetSchema returns the complete schema for the provider.
	GetSchema() GetSchemaResponse

	// ValidateProviderConfig allows the provider to validate the configuration
	// values.
	ValidateProviderConfig(ValidateProviderConfigRequest) ValidateProviderConfigResponse

	// ValidateResourceTypeConfig allows the provider to validate the resource
	// configuration values.
	ValidateResourceTypeConfig(ValidateResourceTypeConfigRequest) ValidateResourceTypeConfigResponse

	// ValidateDataSource allows the provider to validate the data source
	// configuration values.
	ValidateDataSourceConfig(ValidateDataSourceConfigRequest) ValidateDataSourceConfigResponse

	// UpgradeResourceState is called when the state loader encounters an
	// instance state whose schema version is less than the one reported by the
	// currently-used version of the corresponding provider, and the upgraded
	// result is used for any further processing.
	UpgradeResourceState(UpgradeResourceStateRequest) UpgradeResourceStateResponse

	// Configure configures and initialized the provider.
	Configure(ConfigureRequest) ConfigureResponse

	// Stop is called when the provider should halt any in-flight actions.
	//
	// Stop should not block waiting for in-flight actions to complete. It
	// should take any action it wants and return immediately acknowledging it
	// has received the stop request. Terraform will not make any further API
	// calls to the provider after Stop is called.
	//
	// The error returned, if non-nil, is assumed to mean that signaling the
	// stop somehow failed and that the user should expect potentially waiting
	// a longer period of time.
	Stop() error

	// ReadResource refreshes a resource and returns its current state.
	ReadResource(ReadResourceRequest) ReadResourceResponse

	// PlanResourceChange takes the current state and proposed state of a
	// resource, and returns the planned final state.
	PlanResourceChange(PlanResourceChangeRequest) PlanResourceChangeResponse

	// ApplyResourceChange takes the planned state for a resource, which may
	// yet contain unknown computed values, and applies the changes returning
	// the final state.
	ApplyResourceChange(ApplyResourceChangeRequest) ApplyResourceChangeResponse

	// ImportResourceState requests that the given resource be imported.
	ImportResourceState(ImportResourceStateRequest) ImportResourceStateResponse

	// ReadDataSource returns the data source's current state.
	ReadDataSource(ReadDataSourceRequest) ReadDataSourceResponse
}

type GetSchemaResponse struct {
	// Provider is the schema for the provider itself.
	Provider Schema

	// ResourceTypes map the resource type name to that type's schema.
	ResourceTypes map[string]Schema

	// DataSources maps the data source name to that data source's schema.
	DataSources map[string]Schema

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

// Schema pairs a provider or resource schema with that schema's version.
// This is used to be able to upgrade the schema in
type Schema struct {
	Version int
	Block   *configschema.Block
}

type ValidateProviderConfigRequest struct {
	// Config is the complete configuration value for the provider.
	Config cty.Value
}

type ValidateProviderConfigResponse struct {
	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type ValidateResourceTypeConfigRequest struct {
	// TypeName is the name of the resource type to validate.
	TypeName string

	// Config is the configuration value to validate, which may contain unknown
	// values.
	Config cty.Value
}

type ValidateResourceTypeConfigResponse struct {
	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type ValidateDataSourceConfigRequest struct {
	// TypeName is the name of the data source type to validate.
	TypeName string

	// Config is the configuration value to validate, which may contain unknown
	// values.
	Config cty.Value
}

type ValidateDataSourceConfigResponse struct {
	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type UpgradeResourceStateRequest struct {
	// TypeName is the name of the resource type being upgraded
	TypeName string

	// Version is version of the schema that created the current state.
	Version int

	// RawStateJSON and RawStateFlatmap contiain the state that needs to be
	// upgraded to match the current schema version. Because the schema is
	// unknown, this contains only the raw data as stored in the state.
	// RawStateJSON is the current json state encoding.
	// RawStateFlatmap is the legacy flatmap encoding.
	// Only on of these fields may be set for the upgrade request.
	RawStateJSON    []byte
	RawStateFlatmap map[string]string
}

type UpgradeResourceStateResponse struct {
	// UpgradedState is the newly upgraded resource state.
	UpgradedState cty.Value

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type ConfigureRequest struct {
	// Config is the complete configuration value for the provider.
	Config cty.Value
}

type ConfigureResponse struct {
	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type ReadResourceRequest struct {
	// TypeName is the name of the resource type being read.
	TypeName string

	// PriorState contains the previously saved state value for this resource.
	PriorState cty.Value
}

type ReadResourceResponse struct {
	// NewState contains the current state of the resource.
	NewState cty.Value

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type PlanResourceChangeRequest struct {
	// TypeName is the name of the resource type to plan.
	TypeName string

	// PriorState is the previously saved state value for this resource.
	PriorState cty.Value

	// ProposedNewState is the expected state after the new configuration is
	// applied. This is created by directly applying the configuration to the
	// PriorState. The provider is then responsible for applying any further
	// changes required to create the proposed final state.
	ProposedNewState cty.Value

	// Config is the resource configuration, before being merged with the
	// PriorState. Any value not explicitly set in the configuration will be
	// null. Config is supplied for reference, but Provider implementations
	// should prefer the ProposedNewState in most circumstances.
	Config cty.Value

	// PriorPrivate is the previously saved private data returned from the
	// provider during the last apply.
	PriorPrivate []byte
}

type PlanResourceChangeResponse struct {
	// PlannedState is the expected state of the resource once the current
	// configuration is applied.
	PlannedState cty.Value

	// RequiresReplace is the list of thee attributes that are requiring
	// resource replacement.
	RequiresReplace []cty.Path

	// PlannedPrivate is an opaque blob that is not interpreted by terraform
	// core. This will be saved and relayed back to the provider during
	// ApplyResourceChange.
	PlannedPrivate []byte

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type ApplyResourceChangeRequest struct {
	// TypeName is the name of the resource type being applied.
	TypeName string

	// PriorState is the current state of resource.
	PriorState cty.Value

	// Planned state is the state returned from PlanResourceChange, and should
	// represent the new state, minus any remaining computed attributes.
	PlannedState cty.Value

	// Config is the resource configuration, before being merged with the
	// PriorState. Any value not explicitly set in the configuration will be
	// null. Config is supplied for reference, but Provider implementations
	// should prefer the PlannedState in most circumstances.
	Config cty.Value

	// PlannedPrivate is the same value as returned by PlanResourceChange.
	PlannedPrivate []byte
}

type ApplyResourceChangeResponse struct {
	// NewState is the new complete state after applying the planned change.
	// In the event of an error, NewState should represent the most recent
	// known state of the resource, if it exists.
	NewState cty.Value

	// Private is an opaque blob that will be stored in state along with the
	// resource. It is intended only for interpretation by the provider itself.
	Private []byte

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type ImportResourceStateRequest struct {
	// TypeName is the name of the resource type to be imported.
	TypeName string

	// ID is a string with which the provider can identify the resource to be
	// imported.
	ID string
}

type ImportResourceStateResponse struct {
	// ImportedResources contains one or more state values related to the
	// imported resource. It is not required that these be complete, only that
	// there is enough identifying information for the provider to successfully
	// update the states in ReadResource.
	ImportedResources []ImportedResource

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

// ImportedResource represents an object being imported into Terraform with the
// help of a provider. An ImportedObject is a RemoteObject that has been read
// by the provider's import handler but hasn't yet been committed to state.
type ImportedResource struct {
	// ResourceType is the name of the resource type associated with the
	// returned state. It's possible for providers to import multiple related
	// types with a single import request.
	ResourceType string

	// State is the state of the remote object being imported. This may not be
	// complete, but must contain enough information to uniquely identify the
	// resource.
	State cty.Value

	// Private is an opaque blob that will be stored in state along with the
	// resource. It is intended only for interpretation by the provider itself.
	Private []byte
}

type ReadDataSourceRequest struct {
	// TypeName is the name of the data source type to Read.
	TypeName string

	// Config is the complete configuration for the requested data source.
	Config cty.Value
}

type ReadDataSourceResponse struct {
	// State is the current state of the requested data source.
	State cty.Value

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}
<commit_msg>minor providers.Interface fixes<commit_after>package providers

import (
	"github.com/hashicorp/terraform/configs/configschema"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"
)

// Interface represents the set of methods required for a complete resource
// provider plugin.
type Interface interface {
	// GetSchema returns the complete schema for the provider.
	GetSchema() GetSchemaResponse

	// ValidateProviderConfig allows the provider to validate the configuration
	// values.
	ValidateProviderConfig(ValidateProviderConfigRequest) ValidateProviderConfigResponse

	// ValidateResourceTypeConfig allows the provider to validate the resource
	// configuration values.
	ValidateResourceTypeConfig(ValidateResourceTypeConfigRequest) ValidateResourceTypeConfigResponse

	// ValidateDataSource allows the provider to validate the data source
	// configuration values.
	ValidateDataSourceConfig(ValidateDataSourceConfigRequest) ValidateDataSourceConfigResponse

	// UpgradeResourceState is called when the state loader encounters an
	// instance state whose schema version is less than the one reported by the
	// currently-used version of the corresponding provider, and the upgraded
	// result is used for any further processing.
	UpgradeResourceState(UpgradeResourceStateRequest) UpgradeResourceStateResponse

	// Configure configures and initialized the provider.
	Configure(ConfigureRequest) ConfigureResponse

	// Stop is called when the provider should halt any in-flight actions.
	//
	// Stop should not block waiting for in-flight actions to complete. It
	// should take any action it wants and return immediately acknowledging it
	// has received the stop request. Terraform will not make any further API
	// calls to the provider after Stop is called.
	//
	// The error returned, if non-nil, is assumed to mean that signaling the
	// stop somehow failed and that the user should expect potentially waiting
	// a longer period of time.
	Stop() error

	// ReadResource refreshes a resource and returns its current state.
	ReadResource(ReadResourceRequest) ReadResourceResponse

	// PlanResourceChange takes the current state and proposed state of a
	// resource, and returns the planned final state.
	PlanResourceChange(PlanResourceChangeRequest) PlanResourceChangeResponse

	// ApplyResourceChange takes the planned state for a resource, which may
	// yet contain unknown computed values, and applies the changes returning
	// the final state.
	ApplyResourceChange(ApplyResourceChangeRequest) ApplyResourceChangeResponse

	// ImportResourceState requests that the given resource be imported.
	ImportResourceState(ImportResourceStateRequest) ImportResourceStateResponse

	// ReadDataSource returns the data source's current state.
	ReadDataSource(ReadDataSourceRequest) ReadDataSourceResponse
}

type GetSchemaResponse struct {
	// Provider is the schema for the provider itself.
	Provider Schema

	// ResourceTypes map the resource type name to that type's schema.
	ResourceTypes map[string]Schema

	// DataSources maps the data source name to that data source's schema.
	DataSources map[string]Schema

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

// Schema pairs a provider or resource schema with that schema's version.
// This is used to be able to upgrade the schema in UpgradeResourceState.
type Schema struct {
	Version int
	Block   *configschema.Block
}

type ValidateProviderConfigRequest struct {
	// Config is the complete configuration value for the provider.
	Config cty.Value
}

type ValidateProviderConfigResponse struct {
	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type ValidateResourceTypeConfigRequest struct {
	// TypeName is the name of the resource type to validate.
	TypeName string

	// Config is the configuration value to validate, which may contain unknown
	// values.
	Config cty.Value
}

type ValidateResourceTypeConfigResponse struct {
	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type ValidateDataSourceConfigRequest struct {
	// TypeName is the name of the data source type to validate.
	TypeName string

	// Config is the configuration value to validate, which may contain unknown
	// values.
	Config cty.Value
}

type ValidateDataSourceConfigResponse struct {
	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type UpgradeResourceStateRequest struct {
	// TypeName is the name of the resource type being upgraded
	TypeName string

	// Version is version of the schema that created the current state.
	Version int

	// RawStateJSON and RawStateFlatmap contiain the state that needs to be
	// upgraded to match the current schema version. Because the schema is
	// unknown, this contains only the raw data as stored in the state.
	// RawStateJSON is the current json state encoding.
	// RawStateFlatmap is the legacy flatmap encoding.
	// Only on of these fields may be set for the upgrade request.
	RawStateJSON    []byte
	RawStateFlatmap map[string]string
}

type UpgradeResourceStateResponse struct {
	// UpgradedState is the newly upgraded resource state.
	UpgradedState cty.Value

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type ConfigureRequest struct {
	// Terraform version is the version string from the running instance of
	// terraform. Providers can use TerraformVersion to verify compatibility,
	// and to store for informational purposes.
	TerraformVersion string

	// Config is the complete configuration value for the provider.
	Config cty.Value
}

type ConfigureResponse struct {
	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type ReadResourceRequest struct {
	// TypeName is the name of the resource type being read.
	TypeName string

	// PriorState contains the previously saved state value for this resource.
	PriorState cty.Value
}

type ReadResourceResponse struct {
	// NewState contains the current state of the resource.
	NewState cty.Value

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type PlanResourceChangeRequest struct {
	// TypeName is the name of the resource type to plan.
	TypeName string

	// PriorState is the previously saved state value for this resource.
	PriorState cty.Value

	// ProposedNewState is the expected state after the new configuration is
	// applied. This is created by directly applying the configuration to the
	// PriorState. The provider is then responsible for applying any further
	// changes required to create the proposed final state.
	ProposedNewState cty.Value

	// Config is the resource configuration, before being merged with the
	// PriorState. Any value not explicitly set in the configuration will be
	// null. Config is supplied for reference, but Provider implementations
	// should prefer the ProposedNewState in most circumstances.
	Config cty.Value

	// PriorPrivate is the previously saved private data returned from the
	// provider during the last apply.
	PriorPrivate []byte
}

type PlanResourceChangeResponse struct {
	// PlannedState is the expected state of the resource once the current
	// configuration is applied.
	PlannedState cty.Value

	// RequiresReplace is the list of thee attributes that are requiring
	// resource replacement.
	RequiresReplace []cty.Path

	// PlannedPrivate is an opaque blob that is not interpreted by terraform
	// core. This will be saved and relayed back to the provider during
	// ApplyResourceChange.
	PlannedPrivate []byte

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type ApplyResourceChangeRequest struct {
	// TypeName is the name of the resource type being applied.
	TypeName string

	// PriorState is the current state of resource.
	PriorState cty.Value

	// Planned state is the state returned from PlanResourceChange, and should
	// represent the new state, minus any remaining computed attributes.
	PlannedState cty.Value

	// Config is the resource configuration, before being merged with the
	// PriorState. Any value not explicitly set in the configuration will be
	// null. Config is supplied for reference, but Provider implementations
	// should prefer the PlannedState in most circumstances.
	Config cty.Value

	// PlannedPrivate is the same value as returned by PlanResourceChange.
	PlannedPrivate []byte
}

type ApplyResourceChangeResponse struct {
	// NewState is the new complete state after applying the planned change.
	// In the event of an error, NewState should represent the most recent
	// known state of the resource, if it exists.
	NewState cty.Value

	// Private is an opaque blob that will be stored in state along with the
	// resource. It is intended only for interpretation by the provider itself.
	Private []byte

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

type ImportResourceStateRequest struct {
	// TypeName is the name of the resource type to be imported.
	TypeName string

	// ID is a string with which the provider can identify the resource to be
	// imported.
	ID string
}

type ImportResourceStateResponse struct {
	// ImportedResources contains one or more state values related to the
	// imported resource. It is not required that these be complete, only that
	// there is enough identifying information for the provider to successfully
	// update the states in ReadResource.
	ImportedResources []ImportedResource

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}

// ImportedResource represents an object being imported into Terraform with the
// help of a provider. An ImportedObject is a RemoteObject that has been read
// by the provider's import handler but hasn't yet been committed to state.
type ImportedResource struct {
	// TypeName is the name of the resource type associated with the
	// returned state. It's possible for providers to import multiple related
	// types with a single import request.
	TypeName string

	// State is the state of the remote object being imported. This may not be
	// complete, but must contain enough information to uniquely identify the
	// resource.
	State cty.Value

	// Private is an opaque blob that will be stored in state along with the
	// resource. It is intended only for interpretation by the provider itself.
	Private []byte
}

type ReadDataSourceRequest struct {
	// TypeName is the name of the data source type to Read.
	TypeName string

	// Config is the complete configuration for the requested data source.
	Config cty.Value
}

type ReadDataSourceResponse struct {
	// State is the current state of the requested data source.
	State cty.Value

	// Diagnostics contains any warnings or errors from the method call.
	Diagnostics tfdiags.Diagnostics
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"strings"
	"syscall"
)

func main() {
	var dependencies []StaticLibrary
	parallels := 0
	done := make(chan bool)

	// set parallel numbers
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Parse flags
	version := flag.String("v", NGINX_VERSION, "nginx version")
	nginxConfigurePath := flag.String("c", "", "configuration file for building nginx")
	modulesConfPath := flag.String("m", "", "configuration file for 3rd party modules")
	workParentDir := flag.String("d", "", "working directory")
	jobs := flag.Int("j", runtime.NumCPU(), "jobs to build nginx")
	verbose := flag.Bool("verbose", false, "verbose mode")
	pcreStatic := flag.Bool("pcre", false, "embedded PCRE statically")
	pcreVersion := flag.String("pcreversion", PCRE_VERSION, "PCRE version")
	openSSLStatic := flag.Bool("openssl", false, "embedded PCRE statically")
	openSSLVersion := flag.String("opensslversion", OPENSSL_VERSION, "OpenSSL version")
	zlibStatic := flag.Bool("zlib", false, "embedded zlib statically")
	zlibVersion := flag.String("zlibversion", ZLIB_VERSION, "zlib version")
	clear := flag.Bool("clear", false, "remove entries in working directory")
	versionPrint := flag.Bool("version", false, "print nginx-build versions")
	versionsPrint := flag.Bool("versions", false, "print nginx versions")
	openResty := flag.Bool("openresty", false, "download openresty instead of nginx")
	tengine := flag.Bool("tengine", false, "download tengine instead of nginx")
	openRestyVersion := flag.String("openrestyversion", OPENRESTY_VERSION, "openresty version")
	tengineVersion := flag.String("tengineversion", TENGINE_VERSION, "tengine version")
	flag.Parse()

	if *versionPrint {
		printNginxBuildVersion()
		os.Exit(0)
	}

	if *versionsPrint {
		printNginxVersions()
		os.Exit(0)
	}

	printFirstMsg()

	// set verbose mode
	VerboseEnabled = *verbose

	var nginxBuilder Builder
	if *openResty && *tengine {
		log.Fatal("select one between '-openresty' and '-tengine'.")
	}
	if *openResty {
		nginxBuilder = makeBuilder(COMPONENT_OPENRESTY, *openRestyVersion)
	} else if *tengine {
		nginxBuilder = makeBuilder(COMPONENT_TENGINE, *tengineVersion)
	} else {
		nginxBuilder = makeBuilder(COMPONENT_NGINX, *version)
	}
	pcreBuilder := makeBuilder(COMPONENT_PCRE, *pcreVersion)
	openSSLBuilder := makeBuilder(COMPONENT_OPENSSL, *openSSLVersion)
	zlibBuilder := makeBuilder(COMPONENT_ZLIB, *zlibVersion)

	// change default umask
	_ = syscall.Umask(0)

	versionCheck(*version)

	nginxConfigure, err := fileGetContents(*nginxConfigurePath)
	if err != nil {
		log.Fatal(err.Error())
	}
	nginxConfigure = normalizeConfigure(nginxConfigure)

	modules3rd, err := loadModules3rdFile(*modulesConfPath)
	if err != nil {
		log.Fatal(err.Error())
	}

	if len(*workParentDir) == 0 {
		log.Fatal("set working directory with -d")
	}

	if !fileExists(*workParentDir) {
		err := os.Mkdir(*workParentDir, 0755)
		if err != nil {
			log.Fatalf("Failed to create working directory(%s) does not exist.", *workParentDir)
		}
	}

	var workDir string
	if *openResty {
		workDir = *workParentDir + "/" + *openRestyVersion
	} else {
		workDir = *workParentDir + "/" + *version
	}
	if *clear {
		err := clearWorkDir(workDir)
		if err != nil {
			log.Fatal(err.Error())
		}
	}

	if !fileExists(workDir) {
		err := os.Mkdir(workDir, 0755)
		if err != nil {
			log.Fatalf("Failed to create working directory(%s) does not exist.", workDir)
		}
	}

	rootDir := saveCurrentDir()
	// cd workDir
	err = os.Chdir(workDir)
	if err != nil {
		log.Fatal(err.Error())
	}

	if *pcreStatic {
		parallels++
		go downloadAndExtractParallel(&pcreBuilder, done)
	}

	if *openSSLStatic {
		parallels++
		go downloadAndExtractParallel(&openSSLBuilder, done)
	}

	if *zlibStatic {
		parallels++
		go downloadAndExtractParallel(&zlibBuilder, done)
	}

	parallels++
	go downloadAndExtractParallel(&nginxBuilder, done)

	if len(modules3rd) > 0 {
		parallels += len(modules3rd)
		for _, m := range modules3rd {
			go downloadAndExtractModule3rdParallel(m, done)
		}

	}

	// wait until all downloading processes by goroutine finish
	for i := 0; i < parallels; i++ {
		<-done
	}

	if len(modules3rd) > 0 {
		for _, m := range modules3rd {
			provideModule3rd(&m)
		}
	}

	// cd workDir/nginx-${version}
	os.Chdir(nginxBuilder.sourcePath())

	if *pcreStatic {
		dependencies = append(dependencies, makeStaticLibrary(&pcreBuilder))
	}

	if *openSSLStatic {
		dependencies = append(dependencies, makeStaticLibrary(&openSSLBuilder))
	}

	if *zlibStatic {
		dependencies = append(dependencies, makeStaticLibrary(&zlibBuilder))
	}

	log.Printf("Generate configure script for %s.....", nginxBuilder.sourcePath())

	if *pcreStatic && pcreBuilder.isIncludeWithOption(nginxConfigure) {
		log.Printf(pcreBuilder.warnMsgWithLibrary())
	}

	if *openSSLStatic && openSSLBuilder.isIncludeWithOption(nginxConfigure) {
		log.Printf(openSSLBuilder.warnMsgWithLibrary())

	}

	if *zlibStatic && zlibBuilder.isIncludeWithOption(nginxConfigure) {
		log.Printf(zlibBuilder.warnMsgWithLibrary())
	}

	if strings.Contains(nginxConfigure, "--add-module=") {
		log.Println("[warn]Using '--add-module' is discouraged. Instead give ini-file with '-m' to 'nginx-build'")
	}

	configureScript := configureGen(nginxConfigure, modules3rd, dependencies)
	err = ioutil.WriteFile("./nginx-configure", []byte(configureScript), 0655)
	if err != nil {
		log.Fatalf("Failed to generate configure script for %s", nginxBuilder.sourcePath())
	}
	log.Printf("Configure %s.....", nginxBuilder.sourcePath())
	err = configureNginx()
	if err != nil {
		log.Fatalf("Failed to configure %s", nginxBuilder.sourcePath())
	}

	log.Printf("Build %s.....", nginxBuilder.sourcePath())
	if *openSSLStatic {
		// Workarounds for protecting a failure of building nginx with static-linked OpenSSL.

		// Unfortunately a build of OpenSSL fails when multi-CPUs are used.
		*jobs = 1

		// Sometimes machine hardware name('uname -m') is different
		// from machine processor architecture name('uname -p') on Mac.
		// Specifically, `uname -p` is 'i386' and `uname -m` is 'x86_64'.
		// In this case, a build of OpenSSL fails.
		// So it needs to convince OpenSSL with KERNEL_BITS.
		if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" {
			os.Setenv("KERNEL_BITS", "64")
		}
	}
	err = buildNginx(*jobs)
	if err != nil {
		fmt.Println(err.Error())
		log.Fatalf("Failed to build %s", nginxBuilder.sourcePath())
	}

	printLastMsg(workDir, nginxBuilder.sourcePath(), *openResty)

	// cd rootDir
	os.Chdir(rootDir)
}
<commit_msg>bug-fix: working directory name is invalid for tengine.<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"strings"
	"syscall"
)

func main() {
	var dependencies []StaticLibrary
	parallels := 0
	done := make(chan bool)

	// set parallel numbers
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Parse flags
	version := flag.String("v", NGINX_VERSION, "nginx version")
	nginxConfigurePath := flag.String("c", "", "configuration file for building nginx")
	modulesConfPath := flag.String("m", "", "configuration file for 3rd party modules")
	workParentDir := flag.String("d", "", "working directory")
	jobs := flag.Int("j", runtime.NumCPU(), "jobs to build nginx")
	verbose := flag.Bool("verbose", false, "verbose mode")
	pcreStatic := flag.Bool("pcre", false, "embedded PCRE statically")
	pcreVersion := flag.String("pcreversion", PCRE_VERSION, "PCRE version")
	openSSLStatic := flag.Bool("openssl", false, "embedded PCRE statically")
	openSSLVersion := flag.String("opensslversion", OPENSSL_VERSION, "OpenSSL version")
	zlibStatic := flag.Bool("zlib", false, "embedded zlib statically")
	zlibVersion := flag.String("zlibversion", ZLIB_VERSION, "zlib version")
	clear := flag.Bool("clear", false, "remove entries in working directory")
	versionPrint := flag.Bool("version", false, "print nginx-build versions")
	versionsPrint := flag.Bool("versions", false, "print nginx versions")
	openResty := flag.Bool("openresty", false, "download openresty instead of nginx")
	tengine := flag.Bool("tengine", false, "download tengine instead of nginx")
	openRestyVersion := flag.String("openrestyversion", OPENRESTY_VERSION, "openresty version")
	tengineVersion := flag.String("tengineversion", TENGINE_VERSION, "tengine version")
	flag.Parse()

	if *versionPrint {
		printNginxBuildVersion()
		os.Exit(0)
	}

	if *versionsPrint {
		printNginxVersions()
		os.Exit(0)
	}

	printFirstMsg()

	// set verbose mode
	VerboseEnabled = *verbose

	var nginxBuilder Builder
	if *openResty && *tengine {
		log.Fatal("select one between '-openresty' and '-tengine'.")
	}
	if *openResty {
		nginxBuilder = makeBuilder(COMPONENT_OPENRESTY, *openRestyVersion)
	} else if *tengine {
		nginxBuilder = makeBuilder(COMPONENT_TENGINE, *tengineVersion)
	} else {
		nginxBuilder = makeBuilder(COMPONENT_NGINX, *version)
	}
	pcreBuilder := makeBuilder(COMPONENT_PCRE, *pcreVersion)
	openSSLBuilder := makeBuilder(COMPONENT_OPENSSL, *openSSLVersion)
	zlibBuilder := makeBuilder(COMPONENT_ZLIB, *zlibVersion)

	// change default umask
	_ = syscall.Umask(0)

	versionCheck(*version)

	nginxConfigure, err := fileGetContents(*nginxConfigurePath)
	if err != nil {
		log.Fatal(err.Error())
	}
	nginxConfigure = normalizeConfigure(nginxConfigure)

	modules3rd, err := loadModules3rdFile(*modulesConfPath)
	if err != nil {
		log.Fatal(err.Error())
	}

	if len(*workParentDir) == 0 {
		log.Fatal("set working directory with -d")
	}

	if !fileExists(*workParentDir) {
		err := os.Mkdir(*workParentDir, 0755)
		if err != nil {
			log.Fatalf("Failed to create working directory(%s) does not exist.", *workParentDir)
		}
	}

	var workDir string
	if *openResty {
		workDir = *workParentDir + "/" + *openRestyVersion
	} else if *tengine {
		workDir = *workParentDir + "/" + *tengineVersion
	} else {
		workDir = *workParentDir + "/" + *version
	}
	if *clear {
		err := clearWorkDir(workDir)
		if err != nil {
			log.Fatal(err.Error())
		}
	}

	if !fileExists(workDir) {
		err := os.Mkdir(workDir, 0755)
		if err != nil {
			log.Fatalf("Failed to create working directory(%s) does not exist.", workDir)
		}
	}

	rootDir := saveCurrentDir()
	// cd workDir
	err = os.Chdir(workDir)
	if err != nil {
		log.Fatal(err.Error())
	}

	if *pcreStatic {
		parallels++
		go downloadAndExtractParallel(&pcreBuilder, done)
	}

	if *openSSLStatic {
		parallels++
		go downloadAndExtractParallel(&openSSLBuilder, done)
	}

	if *zlibStatic {
		parallels++
		go downloadAndExtractParallel(&zlibBuilder, done)
	}

	parallels++
	go downloadAndExtractParallel(&nginxBuilder, done)

	if len(modules3rd) > 0 {
		parallels += len(modules3rd)
		for _, m := range modules3rd {
			go downloadAndExtractModule3rdParallel(m, done)
		}

	}

	// wait until all downloading processes by goroutine finish
	for i := 0; i < parallels; i++ {
		<-done
	}

	if len(modules3rd) > 0 {
		for _, m := range modules3rd {
			provideModule3rd(&m)
		}
	}

	// cd workDir/nginx-${version}
	os.Chdir(nginxBuilder.sourcePath())

	if *pcreStatic {
		dependencies = append(dependencies, makeStaticLibrary(&pcreBuilder))
	}

	if *openSSLStatic {
		dependencies = append(dependencies, makeStaticLibrary(&openSSLBuilder))
	}

	if *zlibStatic {
		dependencies = append(dependencies, makeStaticLibrary(&zlibBuilder))
	}

	log.Printf("Generate configure script for %s.....", nginxBuilder.sourcePath())

	if *pcreStatic && pcreBuilder.isIncludeWithOption(nginxConfigure) {
		log.Printf(pcreBuilder.warnMsgWithLibrary())
	}

	if *openSSLStatic && openSSLBuilder.isIncludeWithOption(nginxConfigure) {
		log.Printf(openSSLBuilder.warnMsgWithLibrary())

	}

	if *zlibStatic && zlibBuilder.isIncludeWithOption(nginxConfigure) {
		log.Printf(zlibBuilder.warnMsgWithLibrary())
	}

	if strings.Contains(nginxConfigure, "--add-module=") {
		log.Println("[warn]Using '--add-module' is discouraged. Instead give ini-file with '-m' to 'nginx-build'")
	}

	configureScript := configureGen(nginxConfigure, modules3rd, dependencies)
	err = ioutil.WriteFile("./nginx-configure", []byte(configureScript), 0655)
	if err != nil {
		log.Fatalf("Failed to generate configure script for %s", nginxBuilder.sourcePath())
	}
	log.Printf("Configure %s.....", nginxBuilder.sourcePath())
	err = configureNginx()
	if err != nil {
		log.Fatalf("Failed to configure %s", nginxBuilder.sourcePath())
	}

	log.Printf("Build %s.....", nginxBuilder.sourcePath())
	if *openSSLStatic {
		// Workarounds for protecting a failure of building nginx with static-linked OpenSSL.

		// Unfortunately a build of OpenSSL fails when multi-CPUs are used.
		*jobs = 1

		// Sometimes machine hardware name('uname -m') is different
		// from machine processor architecture name('uname -p') on Mac.
		// Specifically, `uname -p` is 'i386' and `uname -m` is 'x86_64'.
		// In this case, a build of OpenSSL fails.
		// So it needs to convince OpenSSL with KERNEL_BITS.
		if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" {
			os.Setenv("KERNEL_BITS", "64")
		}
	}
	err = buildNginx(*jobs)
	if err != nil {
		fmt.Println(err.Error())
		log.Fatalf("Failed to build %s", nginxBuilder.sourcePath())
	}

	printLastMsg(workDir, nginxBuilder.sourcePath(), *openResty)

	// cd rootDir
	os.Chdir(rootDir)
}
<|endoftext|>


<commit_before>package detector

import (
	"bytes"
	"runtime/debug"
	"sync"
	"sync/atomic"
	"time"

	"qiniupkg.com/x/log.v7"

	"gopkg.in/mgo.v2/bson"
	"gopkg.in/robfig/cron.v2"
)

// TaskArgs is a task query struct
type TaskArgs struct {
	ID       string        `json:"id,omitempty"`
	Name     string        `json:"name,omitempty"`
	URL      string        `json:"url,omitempty"`
	Protocol string        `json:"protocol,omitempty"`
	Interval time.Duration `json:"interval,omitempty"`
	Creator  string        `json:"creator,omitempty"`
}

// Task Cron
type Task struct {
	// Task options
	ID          bson.ObjectId `bson:"_id,omitempty"         json:"id"`
	Name        string        `bson:"name,omitempty"        json:"name"`
	Description string        `bson:"description,omitempty" json:"description"`
	Creator     string        `bson:"creator,omitempty"     json:"creator"`
	CreatedAt   time.Time     `bson:"created_at"            json:"created_at"`
	UpdatedAt   time.Time     `bson:"updated_at"            json:"updated_at"`
	Interval    time.Duration `bson:"interval,omitempty"    json:"interval"`
	Enabled     bool          `bson:"enabled,omitempty"     json:"enabled"`
	Status      string        `bson:"status,omitempty"      json:"status"`

	// HTTP Request args
	URL         string            `bson:"url,omitempty"          json:"url"`
	Protocol    string            `bson:"protocol,omitempty"     json:"protocol"`
	Method      string            `bson:"method,omitempty"       json:"method"`
	ContentType string            `bson:"content_type,omitempty" json:"content_type"`
	UserAgent   string            `bson:"user_agent,omitempty"   json:"user_agent"`
	Username    string            `bson:"username,omitempty"     json:"username"`
	Password    string            `bson:"password,omitempty"     json:"password"`
	Body        string            `bson:"body,omitempty"         json:"body"`
	BodyForm    map[string]string `bson:"body_form,omitempty"    json:"body_form"`

	// Last HTTP Response results
	Response TaskResponse `json:"response"`

	// Job Info
	Job Job `json:"job"`
}

// TaskResponse the task result
type TaskResponse struct {
	StatusCode  int           `bson:"status_code"  json:"status_code"`
	TimeLatency time.Duration `bson:"time_latency" json:"time_latency"`
	Message     string        `bson:"message"      json:"message"`
}

// Job cron exec
type Job struct {
	ID      cron.EntryID `json:"id"`
	status  uint32
	Status  string        `json:"status"`
	Latency time.Duration `json:"latency"`
	running sync.Mutex

	Next time.Time `json:"next"`
	Prev time.Time `json:"prev"`
}

// JobStatusUpdate Update task cron job status
func (t *Task) JobStatusUpdate() string {
	if atomic.LoadUint32(&t.Job.status) > 0 {
		t.Job.Status = "RUNNING"
	} else {
		t.Job.Status = "IDLE"
	}

	entry := MainCron.Entry(t.Job.ID)
	t.Job.Prev = entry.Prev
	t.Job.Next = entry.Next

	return t.Job.Status
}

// Run auto trigger in cron
func (t *Task) Run() {
	start := time.Now()

	// If the job panics, just print a stack trace.
	// Don't let the whole process die.
	defer func() {
		if err := recover(); err != nil {
			var buf bytes.Buffer
			logger := log.New(&buf, "Runner Log: ", log.Lshortfile)
			logger.Panic(err, "\n", string(debug.Stack()))
		}
	}()

	if !selfConcurrent {
		t.Job.running.Lock()
		defer t.Job.running.Unlock()
	}

	if workPermits != nil {
		workPermits <- struct{}{}
		defer func() { <-workPermits }()
	}

	atomic.StoreUint32(&t.Job.status, 1)
	t.JobStatusUpdate()

	defer atomic.StoreUint32(&t.Job.status, 0)
	defer t.JobStatusUpdate()

	// TODO

	t.Job.Latency = time.Since(start)
}

// Start task
func (t *Task) Start() {
	entry := MainCron.Entry(t.Job.ID)
	if entry.ID == 0 && t.Interval > 0 {
		t.Job.ID = MainCron.Schedule(cron.Every(t.Interval), t)
	}
	t.Job.Next = entry.Next
	t.Job.Prev = entry.Prev
}

// Stop task
func (t *Task) Stop() {
	if t.Job.ID > 0 {
		MainCron.Remove(t.Job.ID)
		t.Job.ID = 0
	}
}
<commit_msg>Add exec method to detector task.<commit_after>package detector

import (
	"bytes"
	"net/http"
	"runtime/debug"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/miclle/observer/xlog"

	"qiniupkg.com/x/log.v7"

	"gopkg.in/mgo.v2/bson"
	"gopkg.in/robfig/cron.v2"
)

// TaskArgs is a task query struct
type TaskArgs struct {
	ID       string        `json:"id,omitempty"`
	Name     string        `json:"name,omitempty"`
	URL      string        `json:"url,omitempty"`
	Protocol string        `json:"protocol,omitempty"`
	Interval time.Duration `json:"interval,omitempty"`
	Creator  string        `json:"creator,omitempty"`
}

// Task Cron
type Task struct {
	// Task options
	ID          bson.ObjectId `bson:"_id,omitempty"         json:"id"`
	Name        string        `bson:"name,omitempty"        json:"name"`
	Description string        `bson:"description,omitempty" json:"description"`
	Creator     string        `bson:"creator,omitempty"     json:"creator"`
	CreatedAt   time.Time     `bson:"created_at"            json:"created_at"`
	UpdatedAt   time.Time     `bson:"updated_at"            json:"updated_at"`
	Interval    time.Duration `bson:"interval,omitempty"    json:"interval"`
	Enabled     bool          `bson:"enabled,omitempty"     json:"enabled"`
	Status      string        `bson:"status,omitempty"      json:"status"`

	// HTTP Request args
	URL         string            `bson:"url,omitempty"          json:"url"`
	Protocol    string            `bson:"protocol,omitempty"     json:"protocol"`
	Method      string            `bson:"method,omitempty"       json:"method"`
	ContentType string            `bson:"content_type,omitempty" json:"content_type"`
	UserAgent   string            `bson:"user_agent,omitempty"   json:"user_agent"`
	Username    string            `bson:"username,omitempty"     json:"username"`
	Password    string            `bson:"password,omitempty"     json:"password"`
	Body        string            `bson:"body,omitempty"         json:"body"`
	BodyForm    map[string]string `bson:"body_form,omitempty"    json:"body_form"`

	// Last HTTP Response results
	Response TaskResponse `json:"response"`

	// Job Info
	Job Job `json:"job"`
}

// TaskResponse the task result
type TaskResponse struct {
	StatusCode  int           `bson:"status_code"  json:"status_code"`
	TimeLatency time.Duration `bson:"time_latency" json:"time_latency"`
	Message     string        `bson:"message"      json:"message"`
}

// Job cron exec
type Job struct {
	ID      cron.EntryID `json:"id"`
	status  uint32
	Status  string        `json:"status"`
	Latency time.Duration `json:"latency"`
	running sync.Mutex

	Next time.Time `json:"next"`
	Prev time.Time `json:"prev"`
}

// Exec the task
func (t *Task) Exec() (resp *http.Response, err error) {
	client := &Client{
		Client:   &http.Client{Transport: http.DefaultTransport},
		Delivery: nil,
	}

	l := xlog.NewLogger()

	switch t.Method {

	case "HEAD":
		resp, err = client.Head(l, t.URL)

	case "GET":
		resp, err = client.Get(l, t.URL)

	case "DELETE":
		resp, err = client.Delete(l, t.URL)

	case "POST":
		switch t.ContentType {
		case "application/x-www-form-urlencoded":
			data := make(map[string][]string)
			for k, v := range t.BodyForm {
				data[k] = []string{v}
			}
			resp, err = client.PostWithForm(l, t.URL, data)
		default:
			resp, err = client.PostWith(l, t.URL, t.ContentType, strings.NewReader(t.Body), len(t.Body))
		}
	}

	return nil, nil
}

// JobStatusUpdate Update task cron job status
func (t *Task) JobStatusUpdate() string {
	if atomic.LoadUint32(&t.Job.status) > 0 {
		t.Job.Status = "RUNNING"
	} else {
		t.Job.Status = "IDLE"
	}

	entry := MainCron.Entry(t.Job.ID)
	t.Job.Prev = entry.Prev
	t.Job.Next = entry.Next

	return t.Job.Status
}

// Run auto trigger in cron
func (t *Task) Run() {
	start := time.Now()

	// If the job panics, just print a stack trace.
	// Don't let the whole process die.
	defer func() {
		if err := recover(); err != nil {
			var buf bytes.Buffer
			logger := log.New(&buf, "Runner Log: ", log.Lshortfile)
			logger.Panic(err, "\n", string(debug.Stack()))
		}
	}()

	if !selfConcurrent {
		t.Job.running.Lock()
		defer t.Job.running.Unlock()
	}

	if workPermits != nil {
		workPermits <- struct{}{}
		defer func() { <-workPermits }()
	}

	atomic.StoreUint32(&t.Job.status, 1)
	t.JobStatusUpdate()

	defer atomic.StoreUint32(&t.Job.status, 0)
	defer t.JobStatusUpdate()

	// TODO

	t.Job.Latency = time.Since(start)
}

// Start task
func (t *Task) Start() {
	entry := MainCron.Entry(t.Job.ID)
	if entry.ID == 0 && t.Interval > 0 {
		t.Job.ID = MainCron.Schedule(cron.Every(t.Interval), t)
	}
	t.Job.Next = entry.Next
	t.Job.Prev = entry.Prev
}

// Stop task
func (t *Task) Stop() {
	if t.Job.ID > 0 {
		MainCron.Remove(t.Job.ID)
		t.Job.ID = 0
	}
}
<|endoftext|>


<commit_before>package multipass

import (
	"errors"
	"fmt"
	"testing"
)

func ExampleError_IsA() {
	ErrFoo := NewError()

	err := error(ErrFoo.New("foo"))
	fmt.Println(ErrFoo.IsA(err))

	err = errors.New("bar")
	fmt.Println(ErrFoo.IsA(err))

	// Output: true
	// false

}

func TestIsANew(t *testing.T) {
	ErrBar := NewError()
	err := error(ErrBar.New("foo"))
	if !ErrBar.IsA(err) {
		t.Error("err should be an ErrBar")
	}

	ErrFoo := NewError()
	if ErrFoo.IsA(err) {
		t.Error("err should not be an ErrFoo")
	}

	err = errors.New("baz")
	if ErrBar.IsA(err) {
		t.Error("err should not be an ErrBar")
	}
}

func TestEmbedCause(t *testing.T) {
	ErrFoo := NewError()
	ErrBar := errors.New("bar")
	err := ErrFoo.Embed(ErrBar, "foo")
	expected := "foo: bar"
	if err.Error() != expected {
		t.Error("Expected: %s, got: %s", expected, err.Error())
	}
	if err.Cause() != ErrBar {
		t.Error("err.Cause should be ErrBar")
	}

	ErrBaz := NewError()
	err = ErrBaz.Embed(err, "baz")
	expected = "baz: foo: bar"
	if err.Error() != expected {
		t.Error("Expected: %s, got: %s", expected, err.Error())
	}
	if err.Cause() != ErrBar {
		t.Error("err.Cause should be ErrBar")
	}
}

func TestError(t *testing.T) {
	ErrBar := NewError()
	expected := "foo"
	err := ErrBar.New(expected)
	if err.Error() != expected {
		t.Error("Expected %s, got: %s", expected, err.Error())
	}
}
<commit_msg>More examples<commit_after>package multipass

import (
	"errors"
	"fmt"
	"testing"
)

func ExampleError_IsA() {
	ErrFoo := NewError()

	err := error(ErrFoo.New("foo"))
	fmt.Println(ErrFoo.IsA(err))

	err = errors.New("bar")
	fmt.Println(ErrFoo.IsA(err))

	// Output: true
	// false

}

func ExampleError_Cause() {
	ErrFoo := NewError()
	ErrBar := NewError()
	ErrBaz := errors.New("baz")

	err := ErrBar.Embed(ErrBaz, "bar")
	err = ErrFoo.Embed(err, "foo")
	fmt.Println(err.Cause() == ErrBaz)

	// Output: true
}

func ExampleError_Error() {
	ErrFoo := NewError()
	ErrBar := NewError()
	ErrBaz := errors.New("baz")

	err := ErrBar.Embed(ErrBaz, "bar")
	err = ErrFoo.Embed(err, "foo")
	fmt.Println(err.Error())

	// Output foo: bar: baz
}

func TestIsANew(t *testing.T) {
	ErrBar := NewError()
	err := error(ErrBar.New("foo"))
	if !ErrBar.IsA(err) {
		t.Error("err should be an ErrBar")
	}

	ErrFoo := NewError()
	if ErrFoo.IsA(err) {
		t.Error("err should not be an ErrFoo")
	}

	err = errors.New("baz")
	if ErrBar.IsA(err) {
		t.Error("err should not be an ErrBar")
	}
}

func TestEmbedCause(t *testing.T) {
	ErrFoo := NewError()
	ErrBar := errors.New("bar")
	err := ErrFoo.Embed(ErrBar, "foo")
	expected := "foo: bar"
	if err.Error() != expected {
		t.Error("Expected: %s, got: %s", expected, err.Error())
	}
	if err.Cause() != ErrBar {
		t.Error("err.Cause should be ErrBar")
	}

	ErrBaz := NewError()
	err = ErrBaz.Embed(err, "baz")
	expected = "baz: foo: bar"
	if err.Error() != expected {
		t.Error("Expected: %s, got: %s", expected, err.Error())
	}
	if err.Cause() != ErrBar {
		t.Error("err.Cause should be ErrBar")
	}
}

func TestError(t *testing.T) {
	ErrBar := NewError()
	expected := "foo"
	err := ErrBar.New(expected)
	if err.Error() != expected {
		t.Error("Expected %s, got: %s", expected, err.Error())
	}
}
<|endoftext|>


<commit_before>package device

import (
	"fmt"
	"sync"

	"github.com/jochenvg/go-udev"
	"github.com/tarm/serial"
)

// Manager manages devices that are plugged into the system. It supports auto
// detection of devices.
//
// Serial ports are opened each for a device, and a clean API for communicating
// is provided via Read, Write and Flush methods.
//
// The devices are monitored via udev, and any changes that requires reloading
// of the  ports are handled by reloading the ports to the devices.
//
// This is safe to use concurrently in multiple goroutines
type Manager struct {
	devices map[string]serial.Config
	conn    []*Conn
	mu      sync.RWMutex
	monitor *udev.Monitor
	done    chan struct{}
	stop    chan struct{}
}

// New returns a new Manager instance
func New() *Manager {
	return &Manager{
		devices: make(map[string]serial.Config),
		done:    make(chan struct{}),
		stop:    make(chan struct{}),
	}
}

// Init initializes the manager. This involves creating a new goroutine to watch
// over the changes detected by udev for any device interaction with the system.
//
// The only interesting device actions are add and reomove for adding and
// removing devices respctively.
func (m *Manager) Init() {
	u := udev.Udev{}
	monitor := u.NewMonitorFromNetlink("udev")
	devCh, err := monitor.DeviceChan(m.done)
	if err != nil {
		panic(err)
	}
	m.monitor = monitor
	go func() {
	stop:
		for {
			select {
			case d := <-devCh:
				switch d.Action() {
				case "add":
					fmt.Printf(" new device added ad %s\n", d.Devpath())
					m.AddDevice(d.Devpath())
				case "remove":
					fmt.Printf(" %s was removed\n", d.Devpath())
					m.RemoveDevice(d.Devpath())
				default:
					fmt.Println(d.Action())
				}
			case quit := <-m.stop:
				m.done <- quit
				break stop
			}
		}
	}()
}

// AddDevice adds device name to the manager
func (m *Manager) AddDevice(name string) error {
	cfg := serial.Config{Name: name}
	m.mu.Lock()
	m.devices[name] = cfg
	m.mu.Unlock()
	return nil
}

// RemoveDevice removes device name from the manager
func (m *Manager) RemoveDevice(name string) error {
	m.mu.RLock()
	delete(m.devices, name)
	m.mu.RUnlock()
	return nil
}

//Close shuts down the device manager. This makes sure the udev monitor is
//closed and all goroutines are properly exited.
func (m *Manager) Close() {
	m.stop <- struct{}{}
}

// Conn is a device serial connection
type Conn struct {
	device serial.Config
	port   *serial.Port
	isOpen bool
}

// Open opens a serial port to the undelying device
func (c *Conn) Open() error {
	p, err := serial.OpenPort(&c.device)
	if err != nil {
		return nil
	}
	c.port = p
	c.isOpen = true
	return nil
}

// Close closes the port helt by *Conn.
func (c *Conn) Close() error {
	if c.isOpen {
		return c.port.Close()
	}
	return nil
}
<commit_msg>Add *Manager.reload<commit_after>package device

import (
	"fmt"
	"log"
	"sync"

	"github.com/jochenvg/go-udev"
	"github.com/tarm/serial"
)

// Manager manages devices that are plugged into the system. It supports auto
// detection of devices.
//
// Serial ports are opened each for a device, and a clean API for communicating
// is provided via Read, Write and Flush methods.
//
// The devices are monitored via udev, and any changes that requires reloading
// of the  ports are handled by reloading the ports to the devices.
//
// This is safe to use concurrently in multiple goroutines
type Manager struct {
	devices map[string]serial.Config
	conn    []*Conn
	mu      sync.RWMutex
	monitor *udev.Monitor
	done    chan struct{}
	stop    chan struct{}
}

// New returns a new Manager instance
func New() *Manager {
	return &Manager{
		devices: make(map[string]serial.Config),
		done:    make(chan struct{}),
		stop:    make(chan struct{}),
	}
}

// Init initializes the manager. This involves creating a new goroutine to watch
// over the changes detected by udev for any device interaction with the system.
//
// The only interesting device actions are add and reomove for adding and
// removing devices respctively.
func (m *Manager) Init() {
	u := udev.Udev{}
	monitor := u.NewMonitorFromNetlink("udev")
	devCh, err := monitor.DeviceChan(m.done)
	if err != nil {
		panic(err)
	}
	m.monitor = monitor
	go func() {
	stop:
		for {
			select {
			case d := <-devCh:
				switch d.Action() {
				case "add":
					fmt.Printf(" new device added ad %s\n", d.Devpath())
					m.AddDevice(d.Devpath())
				case "remove":
					fmt.Printf(" %s was removed\n", d.Devpath())
					m.RemoveDevice(d.Devpath())
				default:
					fmt.Println(d.Action())
				}
			case quit := <-m.stop:
				m.done <- quit
				break stop
			}
		}
	}()
}

// AddDevice adds device name to the manager
func (m *Manager) AddDevice(name string) error {
	cfg := serial.Config{Name: name}
	m.mu.Lock()
	m.devices[name] = cfg
	m.mu.Unlock()
	return nil
}

// RemoveDevice removes device name from the manager
func (m *Manager) RemoveDevice(name string) error {
	m.mu.RLock()
	delete(m.devices, name)
	m.mu.RUnlock()
	return nil
}

// close all ports that are open for the devices
func (m *Manager) releaseAllPorts() {
	for _, c := range m.conn {
		err := c.Close()
		if err != nil {
			log.Printf("[ERR] closing port %s %v\n", c.device.Name, err)
		}
	}
}

func (m *Manager) reload() {
	m.releaseAllPorts()
	var conns []*Conn
	for _, v := range m.devises {
		conn := &Conn{device: v}
		err := conn.Open()
		if err != nil {
			log.Printf("[ERR] closing port %s %v\n", c.device.Name, err)
		}
	}
	m.conn = conns
}

//Close shuts down the device manager. This makes sure the udev monitor is
//closed and all goroutines are properly exited.
func (m *Manager) Close() {
	m.stop <- struct{}{}
}

// Conn is a device serial connection
type Conn struct {
	device serial.Config
	port   *serial.Port
	isOpen bool
}

// Open opens a serial port to the undelying device
func (c *Conn) Open() error {
	p, err := serial.OpenPort(&c.device)
	if err != nil {
		return nil
	}
	c.port = p
	c.isOpen = true
	return nil
}

// Close closes the port helt by *Conn.
func (c *Conn) Close() error {
	if c.isOpen {
		return c.port.Close()
	}
	return nil
}
<|endoftext|>


<commit_before>package feeds

import (
	"bytes"
	"testing"
	"time"
)

var atomOutput = `<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title>jmoiron.net blog</title>
  <id>http://jmoiron.net/blog</id>
  <updated>2013-01-16T21:52:35-05:00</updated>
  <rights>This work is copyright © Benjamin Button</rights>
  <subtitle>discussion about tech, footie, photos</subtitle>
  <link href="http://jmoiron.net/blog"></link>
  <author>
    <name>Jason Moiron</name>
    <email>jmoiron@jmoiron.net</email>
  </author>
  <entry>
    <title>Limiting Concurrency in Go</title>
    <updated>2013-01-16T21:52:35-05:00</updated>
    <id>tag:jmoiron.net,2013-01-16:/blog/limiting-concurrency-in-go/</id>
    <content type="html">A discussion on controlled parallelism in golang</content>
    <link href="http://jmoiron.net/blog/limiting-concurrency-in-go/"></link>
    <author>
      <name>Jason Moiron</name>
      <email>jmoiron@jmoiron.net</email>
    </author>
  </entry>
  <entry>
    <title>Logic-less Template Redux</title>
    <updated>2013-01-16T21:52:35-05:00</updated>
    <id>tag:jmoiron.net,2013-01-16:/blog/logicless-template-redux/</id>
    <content type="html">More thoughts on logicless templates</content>
    <link href="http://jmoiron.net/blog/logicless-template-redux/"></link>
  </entry>
  <entry>
    <title>Idiomatic Code Reuse in Go</title>
    <updated>2013-01-16T21:52:35-05:00</updated>
    <id>tag:jmoiron.net,2013-01-16:/blog/idiomatic-code-reuse-in-go/</id>
    <content type="html">How to use interfaces <em>effectively</em></content>
    <link href="http://jmoiron.net/blog/idiomatic-code-reuse-in-go/"></link>
  </entry>
  <entry>
    <title>Never Gonna Give You Up Mp3</title>
    <updated>2013-01-16T21:52:35-05:00</updated>
    <id>tag:example.com,2013-01-16:/RickRoll.mp3</id>
    <content type="html">Never gonna give you up - Never gonna let you down.</content>
    <link href="http://example.com/RickRoll.mp3" rel="enclosure" type="audio/mpeg" length="123456"></link>
  </entry>
  <entry>
    <title>String formatting in Go</title>
    <updated>2013-01-16T21:52:35-05:00</updated>
    <id>tag:example.com,2013-01-16:/strings</id>
    <content type="html">How to use things like %s, %v, %d, etc.</content>
    <link href="http://example.com/strings"></link>
  </entry>
</feed>`

var rssOutput = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">
  <channel>
    <title>jmoiron.net blog</title>
    <link>http://jmoiron.net/blog</link>
    <description>discussion about tech, footie, photos</description>
    <copyright>This work is copyright © Benjamin Button</copyright>
    <managingEditor>jmoiron@jmoiron.net (Jason Moiron)</managingEditor>
    <pubDate>16 Jan 13 21:52 EST</pubDate>
    <item>
      <title>Limiting Concurrency in Go</title>
      <link>http://jmoiron.net/blog/limiting-concurrency-in-go/</link>
      <description>A discussion on controlled parallelism in golang</description>
      <author>Jason Moiron</author>
      <pubDate>16 Jan 13 21:52 EST</pubDate>
    </item>
    <item>
      <title>Logic-less Template Redux</title>
      <link>http://jmoiron.net/blog/logicless-template-redux/</link>
      <description>More thoughts on logicless templates</description>
      <pubDate>16 Jan 13 21:52 EST</pubDate>
    </item>
    <item>
      <title>Idiomatic Code Reuse in Go</title>
      <link>http://jmoiron.net/blog/idiomatic-code-reuse-in-go/</link>
      <description>How to use interfaces <em>effectively</em></description>
      <pubDate>16 Jan 13 21:52 EST</pubDate>
    </item>
    <item>
      <title>Never Gonna Give You Up Mp3</title>
      <link>http://example.com/RickRoll.mp3</link>
      <description>Never gonna give you up - Never gonna let you down.</description>
      <enclosure url="http://example.com/RickRoll.mp3" length="123456" type="audio/mpeg"></enclosure>
      <pubDate>16 Jan 13 21:52 EST</pubDate>
    </item>
    <item>
      <title>String formatting in Go</title>
      <link>http://example.com/strings</link>
      <description>How to use things like %s, %v, %d, etc.</description>
      <pubDate>16 Jan 13 21:52 EST</pubDate>
    </item>
  </channel>
</rss>`

func TestFeed(t *testing.T) {
	now, err := time.Parse(time.RFC3339, "2013-01-16T21:52:35-05:00")
	if err != nil {
		t.Error(err)
	}
	tz := time.FixedZone("EST", -5*60*60)
	now = now.In(tz)

	feed := &Feed{
		Title:       "jmoiron.net blog",
		Link:        &Link{Href: "http://jmoiron.net/blog"},
		Description: "discussion about tech, footie, photos",
		Author:      &Author{"Jason Moiron", "jmoiron@jmoiron.net"},
		Created:     now,
		Copyright:   "This work is copyright © Benjamin Button",
	}

	feed.Items = []*Item{
		&Item{
			Title:       "Limiting Concurrency in Go",
			Link:        &Link{Href: "http://jmoiron.net/blog/limiting-concurrency-in-go/"},
			Description: "A discussion on controlled parallelism in golang",
			Author:      &Author{"Jason Moiron", "jmoiron@jmoiron.net"},
			Created:     now,
		},
		&Item{
			Title:       "Logic-less Template Redux",
			Link:        &Link{Href: "http://jmoiron.net/blog/logicless-template-redux/"},
			Description: "More thoughts on logicless templates",
			Created:     now,
		},
		&Item{
			Title:       "Idiomatic Code Reuse in Go",
			Link:        &Link{Href: "http://jmoiron.net/blog/idiomatic-code-reuse-in-go/"},
			Description: "How to use interfaces effectively",
			Created:     now,
		},
		&Item{
			Title:       "Never Gonna Give You Up Mp3",
			Link:        &Link{Href: "http://example.com/RickRoll.mp3", Length: "123456", Type: "audio/mpeg"},
			Description: "Never gonna give you up - Never gonna let you down.",
			Created:     now,
		},
		&Item{
			Title:       "String formatting in Go",
			Link:        &Link{Href: "http://example.com/strings"},
			Description: "How to use things like %s, %v, %d, etc.",
			Created:     now,
		},
	}

	atom, err := feed.ToAtom()
	if err != nil {
		t.Errorf("unexpected error encoding Atom: %v", err)
	}
	if atom != atomOutput {
		t.Errorf("Atom not what was expected.  Got:\n%s\n\nExpected:\n%s\n", atom, atomOutput)
	}
	var buf bytes.Buffer
	if err := feed.WriteAtom(&buf); err != nil {
		t.Errorf("unexpected error writing Atom: %v", err)
	}
	if got := buf.String(); got != atomOutput {
		t.Errorf("Atom not what was expected.  Got:\n%s\n\nExpected:\n%s\n", got, atomOutput)
	}

	rss, err := feed.ToRss()
	if err != nil {
		t.Errorf("unexpected error encoding RSS: %v", err)
	}
	if rss != rssOutput {
		t.Errorf("Rss not what was expected.  Got:\n%s\n\nExpected:\n%s\n", rss, rssOutput)
	}
	buf.Reset()
	if err := feed.WriteRss(&buf); err != nil {
		t.Errorf("unexpected error writing RSS: %v", err)
	}
	if got := buf.String(); got != rssOutput {
		t.Errorf("Rss not what was expected.  Got:\n%s\n\nExpected:\n%s\n", got, rssOutput)
	}
}
<commit_msg>[fmt] gofmt'ed structs in test.<commit_after>package feeds

import (
	"bytes"
	"testing"
	"time"
)

var atomOutput = `<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title>jmoiron.net blog</title>
  <id>http://jmoiron.net/blog</id>
  <updated>2013-01-16T21:52:35-05:00</updated>
  <rights>This work is copyright © Benjamin Button</rights>
  <subtitle>discussion about tech, footie, photos</subtitle>
  <link href="http://jmoiron.net/blog"></link>
  <author>
    <name>Jason Moiron</name>
    <email>jmoiron@jmoiron.net</email>
  </author>
  <entry>
    <title>Limiting Concurrency in Go</title>
    <updated>2013-01-16T21:52:35-05:00</updated>
    <id>tag:jmoiron.net,2013-01-16:/blog/limiting-concurrency-in-go/</id>
    <content type="html">A discussion on controlled parallelism in golang</content>
    <link href="http://jmoiron.net/blog/limiting-concurrency-in-go/"></link>
    <author>
      <name>Jason Moiron</name>
      <email>jmoiron@jmoiron.net</email>
    </author>
  </entry>
  <entry>
    <title>Logic-less Template Redux</title>
    <updated>2013-01-16T21:52:35-05:00</updated>
    <id>tag:jmoiron.net,2013-01-16:/blog/logicless-template-redux/</id>
    <content type="html">More thoughts on logicless templates</content>
    <link href="http://jmoiron.net/blog/logicless-template-redux/"></link>
  </entry>
  <entry>
    <title>Idiomatic Code Reuse in Go</title>
    <updated>2013-01-16T21:52:35-05:00</updated>
    <id>tag:jmoiron.net,2013-01-16:/blog/idiomatic-code-reuse-in-go/</id>
    <content type="html">How to use interfaces <em>effectively</em></content>
    <link href="http://jmoiron.net/blog/idiomatic-code-reuse-in-go/"></link>
  </entry>
  <entry>
    <title>Never Gonna Give You Up Mp3</title>
    <updated>2013-01-16T21:52:35-05:00</updated>
    <id>tag:example.com,2013-01-16:/RickRoll.mp3</id>
    <content type="html">Never gonna give you up - Never gonna let you down.</content>
    <link href="http://example.com/RickRoll.mp3" rel="enclosure" type="audio/mpeg" length="123456"></link>
  </entry>
  <entry>
    <title>String formatting in Go</title>
    <updated>2013-01-16T21:52:35-05:00</updated>
    <id>tag:example.com,2013-01-16:/strings</id>
    <content type="html">How to use things like %s, %v, %d, etc.</content>
    <link href="http://example.com/strings"></link>
  </entry>
</feed>`

var rssOutput = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">
  <channel>
    <title>jmoiron.net blog</title>
    <link>http://jmoiron.net/blog</link>
    <description>discussion about tech, footie, photos</description>
    <copyright>This work is copyright © Benjamin Button</copyright>
    <managingEditor>jmoiron@jmoiron.net (Jason Moiron)</managingEditor>
    <pubDate>16 Jan 13 21:52 EST</pubDate>
    <item>
      <title>Limiting Concurrency in Go</title>
      <link>http://jmoiron.net/blog/limiting-concurrency-in-go/</link>
      <description>A discussion on controlled parallelism in golang</description>
      <author>Jason Moiron</author>
      <pubDate>16 Jan 13 21:52 EST</pubDate>
    </item>
    <item>
      <title>Logic-less Template Redux</title>
      <link>http://jmoiron.net/blog/logicless-template-redux/</link>
      <description>More thoughts on logicless templates</description>
      <pubDate>16 Jan 13 21:52 EST</pubDate>
    </item>
    <item>
      <title>Idiomatic Code Reuse in Go</title>
      <link>http://jmoiron.net/blog/idiomatic-code-reuse-in-go/</link>
      <description>How to use interfaces <em>effectively</em></description>
      <pubDate>16 Jan 13 21:52 EST</pubDate>
    </item>
    <item>
      <title>Never Gonna Give You Up Mp3</title>
      <link>http://example.com/RickRoll.mp3</link>
      <description>Never gonna give you up - Never gonna let you down.</description>
      <enclosure url="http://example.com/RickRoll.mp3" length="123456" type="audio/mpeg"></enclosure>
      <pubDate>16 Jan 13 21:52 EST</pubDate>
    </item>
    <item>
      <title>String formatting in Go</title>
      <link>http://example.com/strings</link>
      <description>How to use things like %s, %v, %d, etc.</description>
      <pubDate>16 Jan 13 21:52 EST</pubDate>
    </item>
  </channel>
</rss>`

func TestFeed(t *testing.T) {
	now, err := time.Parse(time.RFC3339, "2013-01-16T21:52:35-05:00")
	if err != nil {
		t.Error(err)
	}
	tz := time.FixedZone("EST", -5*60*60)
	now = now.In(tz)

	feed := &Feed{
		Title:       "jmoiron.net blog",
		Link:        &Link{Href: "http://jmoiron.net/blog"},
		Description: "discussion about tech, footie, photos",
		Author:      &Author{"Jason Moiron", "jmoiron@jmoiron.net"},
		Created:     now,
		Copyright:   "This work is copyright © Benjamin Button",
	}

	feed.Items = []*Item{
		{
			Title:       "Limiting Concurrency in Go",
			Link:        &Link{Href: "http://jmoiron.net/blog/limiting-concurrency-in-go/"},
			Description: "A discussion on controlled parallelism in golang",
			Author:      &Author{"Jason Moiron", "jmoiron@jmoiron.net"},
			Created:     now,
		},
		{
			Title:       "Logic-less Template Redux",
			Link:        &Link{Href: "http://jmoiron.net/blog/logicless-template-redux/"},
			Description: "More thoughts on logicless templates",
			Created:     now,
		},
		{
			Title:       "Idiomatic Code Reuse in Go",
			Link:        &Link{Href: "http://jmoiron.net/blog/idiomatic-code-reuse-in-go/"},
			Description: "How to use interfaces effectively",
			Created:     now,
		},
		{
			Title:       "Never Gonna Give You Up Mp3",
			Link:        &Link{Href: "http://example.com/RickRoll.mp3", Length: "123456", Type: "audio/mpeg"},
			Description: "Never gonna give you up - Never gonna let you down.",
			Created:     now,
		},
		{
			Title:       "String formatting in Go",
			Link:        &Link{Href: "http://example.com/strings"},
			Description: "How to use things like %s, %v, %d, etc.",
			Created:     now,
		}}

	atom, err := feed.ToAtom()
	if err != nil {
		t.Errorf("unexpected error encoding Atom: %v", err)
	}
	if atom != atomOutput {
		t.Errorf("Atom not what was expected.  Got:\n%s\n\nExpected:\n%s\n", atom, atomOutput)
	}
	var buf bytes.Buffer
	if err := feed.WriteAtom(&buf); err != nil {
		t.Errorf("unexpected error writing Atom: %v", err)
	}
	if got := buf.String(); got != atomOutput {
		t.Errorf("Atom not what was expected.  Got:\n%s\n\nExpected:\n%s\n", got, atomOutput)
	}

	rss, err := feed.ToRss()
	if err != nil {
		t.Errorf("unexpected error encoding RSS: %v", err)
	}
	if rss != rssOutput {
		t.Errorf("Rss not what was expected.  Got:\n%s\n\nExpected:\n%s\n", rss, rssOutput)
	}
	buf.Reset()
	if err := feed.WriteRss(&buf); err != nil {
		t.Errorf("unexpected error writing RSS: %v", err)
	}
	if got := buf.String(); got != rssOutput {
		t.Errorf("Rss not what was expected.  Got:\n%s\n\nExpected:\n%s\n", got, rssOutput)
	}
}
<|endoftext|>


<commit_before>package proxmox

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"strings"
)

// ConfigLxc - Proxmox API LXC options
type ConfigLxc struct {
	Ostemplate      string       `json:"ostemplate"`
	Storage         string       `json:"storage"`
	Pool            string       `json:"pool"`
	Password        string       `json:"password"`
	Hostname        string       `json:"hostname"`
	Networks        QemuDevices  `json:"networks"`
}

func NewConfigLxcFromJson(io io.Reader) (config *ConfigLxc, err error) {
	config = &ConfigLxc{}
	err = json.NewDecoder(io).Decode(config)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	log.Println(config)
	return
}

// create LXC container using the Proxmox API
func (config ConfigLxc) CreateLxc(vmr *VmRef, client *Client) (err error) {
	vmr.SetVmType("lxc")

        // convert config to map
        params, _ := json.Marshal(&config)
        var paramMap map[string]interface{}
        json.Unmarshal(params, &paramMap)

        // build list of network parameters
	for nicID, nicConfMap := range config.Networks {
		nicConfParam := QemuDeviceParam{}
		nicConfParam = nicConfParam.createDeviceParam(nicConfMap, nil)

		// add nic to lxc parameters
		nicName := fmt.Sprintf("net%v", nicID)
		paramMap[nicName] = strings.Join(nicConfParam, ",")
        }

        // now that we concatenated the key value parameter
        // list for the networks, remove the original network key
        // since the Proxmox API does not know how to handle this key
        delete(paramMap, "networks")

        // amend vmid
        paramMap["vmid"] = vmr.vmId

	exitStatus, err := client.CreateLxcContainer(vmr.node, paramMap)
	if err != nil {
		return fmt.Errorf("Error creating LXC container: %v, error status: %s (params: %v)", err, exitStatus, params)
	}
	return
}
<commit_msg>add parameters for /nodes/{n}/lxc<commit_after>package proxmox

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"strings"
)

type (
	LxcMountpoints  map[int]map[string]interface{}
	LxcUnused       map[int]map[string]interface{}
	LxcDevice       map[string]interface{}
	LxcDeviceParam  []string
)

// LXC options for the Proxmox API
type configLxc struct {
	Ostemplate         string         `json:"ostemplate"`
        Arch               string         `json:"arch"`
        BWLimit            int            `json:"bwlimit,omitempty"`
        CMode              string         `json:"cmode"`
        Console            bool           `json:"console"`
        Cores              int            `json:"cores,omitempty"`
        CPULimit           int            `json:"cpulimit"`
        CPUUnits           int            `json:"cpuunits"`
        Description        string         `json:"description,omitempty"`
        Features           string         `json:"features,omitempty"`
        Force              bool           `json:"force,omitempty"`
        Hookscript         string         `json:"hookscript,omitempty"`
	Hostname           string         `json:"hostname,omitempty"`
        IgnoreUnpackErrors bool           `json:"ignore-unpack-errors,omitempty"`
	Lock               string         `json:"lock,omitempty"`
        Memory             int            `json:"memory"`
        Mountpoints        LxcMountpoints `json:"mountpoints,omitempty"`
        Nameserver         string         `json:"nameserver,omitempty"`
	Networks           QemuDevices    `json:"networks,omitempty"`
        OnBoot             bool           `json:"onboot"`
        OsType             string         `json:"ostype,omitempty"`
	Password           string         `json:"password,omitempty"`
	Pool               string         `json:"pool,omitempty"`
        Protection         bool           `json:"protection"`
        Restore            bool           `json:"restore,omitempty"`
	RootFs             string         `json:"rootfs,omitempty"`
	SearchDomain       string         `json:"searchdomain,omitempty"`
	SSHPublicKeys      string         `json:"ssh-public-keys,omitempty"`
        Start              bool           `json:"start"`
	Startup            string         `json:"startup,omitempty"`
	Storage            string         `json:"storage"`
        Swap               int            `json:"swap"`
	Template           bool           `json:"template,omitempty"`
        Tty                int            `json:"tty"`
	Unique             bool           `json:"unique,omitempty"`
	Unprivileged       bool           `json:"unprivileged"`
	Unused             LxcUnused      `json:"unused,omitempty"`
}

func NewConfigLxc() (configLxc) {
	return configLxc{
		Arch: "amd64",
		CMode: "tty",
		Console: true,
		CPULimit: 0,
		CPUUnits: 1024,
		Memory: 512,
		OnBoot: false,
		Protection: false,
		Start: false,
		Storage: "local",
		Swap: 512,
		Template: false,
		Tty: 2,
		Unprivileged: false,
	}
}

func NewConfigLxcFromJson(io io.Reader) (config configLxc, err error) {
	config = NewConfigLxc()
	err = json.NewDecoder(io).Decode(config)
	if err != nil {
		log.Fatal(err)
		return config, err
	}
	log.Println(config)
	return
}

// create LXC container using the Proxmox API
func (config configLxc) CreateLxc(vmr *VmRef, client *Client) (err error) {
	vmr.SetVmType("lxc")

        // convert config to map
        params, _ := json.Marshal(&config)
        var paramMap map[string]interface{}
        json.Unmarshal(params, &paramMap)

        // build list of network parameters
	for nicID, nicConfMap := range config.Networks {
		nicConfParam := QemuDeviceParam{}
		nicConfParam = nicConfParam.createDeviceParam(nicConfMap, nil)

		// add nic to lxc parameters
		nicName := fmt.Sprintf("net%v", nicID)
		paramMap[nicName] = strings.Join(nicConfParam, ",")
        }

        // now that we concatenated the key value parameter
        // list for the networks, remove the original network key
        // since the Proxmox API does not know how to handle this key
        delete(paramMap, "networks")

        // amend vmid
        paramMap["vmid"] = vmr.vmId

	exitStatus, err := client.CreateLxcContainer(vmr.node, paramMap)
	if err != nil {
		return fmt.Errorf("Error creating LXC container: %v, error status: %s (params: %v)", err, exitStatus, params)
	}
	return
}
<|endoftext|>


<commit_before>package proxystorage

import (
	"context"
	"fmt"
	"net/url"
	"reflect"
	"strconv"
	"sync/atomic"
	"time"

	"github.com/jacksontj/promxy/config"
	"github.com/jacksontj/promxy/promclient"
	"github.com/jacksontj/promxy/proxyquerier"
	"github.com/jacksontj/promxy/servergroup"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/pkg/timestamp"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/storage"
	"github.com/sirupsen/logrus"
)

type proxyStorageState struct {
	serverGroups []*servergroup.ServerGroup
	cfg          *proxyconfig.PromxyConfig
}

func (p *proxyStorageState) Ready() {
	for _, sg := range p.serverGroups {
		<-sg.Ready
	}
}

func (p *proxyStorageState) Cancel() {
	if p.serverGroups != nil {
		for _, sg := range p.serverGroups {
			sg.Cancel()
		}
	}
}

func NewProxyStorage() (*ProxyStorage, error) {
	return &ProxyStorage{}, nil
}

// TODO: rename?
type ProxyStorage struct {
	state atomic.Value
}

func (p *ProxyStorage) GetState() *proxyStorageState {
	tmp := p.state.Load()
	if sg, ok := tmp.(*proxyStorageState); ok {
		return sg
	} else {
		return nil
	}
}

func (p *ProxyStorage) ApplyConfig(c *proxyconfig.Config) error {
	failed := false

	newState := &proxyStorageState{
		serverGroups: make([]*servergroup.ServerGroup, len(c.ServerGroups)),
		cfg:          &c.PromxyConfig,
	}
	for i, sgCfg := range c.ServerGroups {
		tmp := servergroup.New()
		if err := tmp.ApplyConfig(sgCfg); err != nil {
			failed = true
			logrus.Errorf("Error applying config to server group: %s", err)
		}
		newState.serverGroups[i] = tmp
	}

	if failed {
		for _, sg := range newState.serverGroups {
			sg.Cancel()
		}
		return fmt.Errorf("Error Applying Config to one or more server group(s)")
	}

	newState.Ready()         // Wait for the newstate to be ready
	oldState := p.GetState() // Fetch the old state
	p.state.Store(newState)  // Store the new state
	if oldState != nil {
		oldState.Cancel() // Cancel the old one
	}

	return nil
}

func (p *ProxyStorage) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) {
	state := p.GetState()
	return &proxyquerier.ProxyQuerier{
		ctx,
		timestamp.Time(mint),
		timestamp.Time(maxt),
		state.serverGroups,

		state.cfg,
	}, nil
}

func (p *ProxyStorage) StartTime() (int64, error) {
	return 0, nil
}

// TODO: remove?
type appenderStub struct{}

func (a *appenderStub) Add(l labels.Labels, t int64, v float64) (uint64, error) { return 0, nil }

func (a *appenderStub) AddFast(l labels.Labels, ref uint64, t int64, v float64) error { return nil }

// Commit submits the collected samples and purges the batch.
func (a *appenderStub) Commit() error { return nil }

func (a *appenderStub) Rollback() error { return nil }

func (p *ProxyStorage) Appender() (storage.Appender, error) {
	return &appenderStub{}, nil
}

// TODO: actually close things?
func (p *ProxyStorage) Close() error { return nil }

// This replaces promql Nodes with more efficient-to-fetch ones. This works by taking lower-layer
// chunks of the query, farming them out to prometheus hosts, then stitching the results back together.
// An example would be a sum, we can sum multiple sums and come up with the same result -- so we do.
// There are a few ground rules for this:
//      - Children cannot be AggregateExpr: aggregates have their own combining logic, so its not safe to send a subquery with additional aggregations
//      - offsets within the subtree must match: if they don't then we'll get mismatched data, so we wait until we are far enough down the tree that they converge
//      - Don't reduce accuracy/granularity: the intention of this is to get the correct data faster, meaning correctness overrules speed.
func (p *ProxyStorage) NodeReplacer(ctx context.Context, s *promql.EvalStmt, node promql.Node) (promql.Node, error) {
	// If there is a child that is an aggregator we cannot do anything (as they have their own
	// rules around combining). We'll skip this node and let a lower layer take this on
	aggFinder := &BooleanFinder{Func: func(node promql.Node) bool {
		_, ok := node.(*promql.AggregateExpr)
		return ok
	}}

	if _, err := promql.Walk(ctx, aggFinder, s, node, nil, nil); err != nil {
		return nil, err
	}

	if aggFinder.Found {
		return nil, nil
	}

	// If the tree below us is not all the same offset, then we can't do anything below -- we'll need
	// to wait until further in execution where they all match
	var offset time.Duration

	visitor := &OffsetFinder{}
	if _, err := promql.Walk(ctx, visitor, s, node, nil, nil); err != nil {
		return nil, err
	}
	// If we couldn't find an offset, then something is wrong-- lets skip
	// Also if there was an error, skip
	if !visitor.Found || visitor.Error != nil {
		return nil, nil
	}
	offset = visitor.Offset

	// Function to recursivelt remove offset. This is needed as we're using
	// the node API to String() the query to downstreams. Promql's iterators require
	// that the time be the absolute time, whereas the API returns them based on the
	// range you ask for (with the offset being implicit)
	// TODO: rename
	removeOffset := func() error {
		_, err := promql.Walk(ctx, &OffsetRemover{}, s, node, nil, nil)
		return err
	}

	state := p.GetState()
	serverGroups := servergroup.ServerGroups(state.serverGroups)
	switch n := node.(type) {
	// Some AggregateExprs can be composed (meaning they are "reentrant". If the aggregation op
	// is reentrant/composable then we'll do so, otherwise we let it fall through to normal query mechanisms
	case *promql.AggregateExpr:
		logrus.Debugf("AggregateExpr %v", n)

		var result model.Value
		var err error

		// Not all Aggregation functions are composable, so we'll do what we can
		switch n.Op.String() {
		// All "reentrant" cases (meaning they can be done repeatedly and the outcome doesn't change)
		case "sum", "min", "max", "topk", "bottomk":
			var urlBase string
			values := url.Values{}
			removeOffset()
			values.Add("query", n.String())

			if s.Interval > 0 {
				values.Add("start", model.Time(timestamp.FromTime(s.Start.Add(-offset-promql.LookbackDelta))).String())
				values.Add("end", model.Time(timestamp.FromTime(s.End.Add(-offset))).String())
				values.Add("step", strconv.FormatFloat(s.Interval.Seconds(), 'f', -1, 64))
				urlBase = "/api/v1/query_range"
			} else {
				values.Add("time", model.Time(timestamp.FromTime(s.Start.Add(-offset))).String())
				urlBase = "/api/v1/query"
			}

			result, err = serverGroups.GetData(ctx, urlBase, values)
			if err != nil {
				return nil, err
			}

		// Convert avg into sum() / count()
		case "avg":
			// Replace with sum() / count()
			return &promql.BinaryExpr{
				Op: 24, // Divide  TODO
				LHS: &promql.AggregateExpr{
					Op:       41, // sum() TODO
					Expr:     n.Expr,
					Param:    n.Param,
					Grouping: n.Grouping,
					Without:  n.Without,
				},

				RHS: &promql.AggregateExpr{
					Op:       40, // count() TODO
					Expr:     n.Expr,
					Param:    n.Param,
					Grouping: n.Grouping,
					Without:  n.Without,
				},
				VectorMatching: &promql.VectorMatching{Card: promql.CardOneToOne},
			}, nil

		// For count we simply need to change this to a sum over the data we get back
		case "count":
			var urlBase string
			values := url.Values{}
			removeOffset()
			values.Add("query", n.String())

			if s.Interval > 0 {
				values.Add("start", model.Time(timestamp.FromTime(s.Start.Add(-offset-promql.LookbackDelta))).String())
				values.Add("end", model.Time(timestamp.FromTime(s.End.Add(-offset))).String())
				values.Add("step", strconv.FormatFloat(s.Interval.Seconds(), 'f', -1, 64))
				urlBase = "/api/v1/query_range"
			} else {
				values.Add("time", model.Time(timestamp.FromTime(s.Start.Add(-offset))).String())
				urlBase = "/api/v1/query"
			}

			result, err = serverGroups.GetData(ctx, urlBase, values)
			if err != nil {
				return nil, err
			}
			// TODO: have a reverse method in promql/lex.go
			n.Op = 41 // SUM

		case "stddev": // TODO: something?
		case "stdvar": // TODO: something?

			// Do nothing, we want to allow the VectorSelector to fall through to do a query_range.
			// Unless we find another way to decompose the query, this is all we can do
		case "count_values":
			// DO NOTHING

		case "quantile": // TODO: something?

		}

		if result != nil {
			iterators := promclient.IteratorsForValue(result)

			series := make([]storage.Series, len(iterators))
			for i, iterator := range iterators {
				series[i] = &proxyquerier.Series{iterator}
			}

			ret := &promql.VectorSelector{Offset: offset}
			ret.SetSeries(series)
			n.Expr = ret
			return n, nil
		}

	// Call is for things such as rate() etc. This can be sent directly to the
	// prometheus node to answer
	case *promql.Call:
		logrus.Debugf("call %v %v", n, n.Type())
		var urlBase string
		values := url.Values{}
		removeOffset()
		values.Add("query", n.String())

		if s.Interval > 0 {
			values.Add("start", model.Time(timestamp.FromTime(s.Start.Add(-offset-promql.LookbackDelta))).String())
			values.Add("end", model.Time(timestamp.FromTime(s.End.Add(-offset))).String())
			values.Add("step", strconv.FormatFloat(s.Interval.Seconds(), 'f', -1, 64))
			urlBase = "/api/v1/query_range"
		} else {
			values.Add("time", model.Time(timestamp.FromTime(s.Start.Add(-offset))).String())
			urlBase = "/api/v1/query"
		}

		result, err := serverGroups.GetData(ctx, urlBase, values)
		if err != nil {
			return nil, err
		}
		iterators := promclient.IteratorsForValue(result)
		series := make([]storage.Series, len(iterators))
		for i, iterator := range iterators {
			series[i] = &proxyquerier.Series{iterator}
		}

		ret := &promql.VectorSelector{Offset: offset}
		ret.SetSeries(series)
		return ret, nil

		// If we are simply fetching a Vector then we can fetch the data using the same step that
		// the query came in as (reducing the amount of data we need to fetch)
	// If we are simply fetching data, lets do that
	case *promql.VectorSelector:
		// Don't attempt to fetch internally replaced things
		if n.HasSeries() {
			return nil, nil
		}
		logrus.Debugf("vectorSelector %v", n)
		var urlBase string
		values := url.Values{}
		removeOffset()
		values.Add("query", n.String())
		n.Offset = offset
		if s.Interval > 0 {
			values.Add("start", model.Time(timestamp.FromTime(s.Start.Add(-offset-promql.LookbackDelta))).String())
			values.Add("end", model.Time(timestamp.FromTime(s.End.Add(-offset))).String())
			values.Add("step", strconv.FormatFloat(s.Interval.Seconds(), 'f', -1, 64))
			urlBase = "/api/v1/query_range"
		} else {
			values.Add("time", model.Time(timestamp.FromTime(s.Start.Add(-offset))).String())
			urlBase = "/api/v1/query"
		}

		result, err := serverGroups.GetData(ctx, urlBase, values)
		if err != nil {
			return nil, err
		}
		iterators := promclient.IteratorsForValue(result)
		series := make([]storage.Series, len(iterators))
		for i, iterator := range iterators {
			series[i] = &proxyquerier.Series{iterator}
		}

		n.SetSeries(series)
		return n, nil

	// If we hit this someone is asking for a matrix directly, if so then we don't
	// have anyway to ask for less-- since this is exactly what they are asking for
	case *promql.MatrixSelector:
		// DO NOTHING

	default:
		logrus.Debugf("default %v %s", n, reflect.TypeOf(n))

	}
	return nil, nil

}
<commit_msg>Pass VectorSelectors in the NodeReplacer layer<commit_after>package proxystorage

import (
	"context"
	"fmt"
	"net/url"
	"reflect"
	"strconv"
	"sync/atomic"
	"time"

	"github.com/jacksontj/promxy/config"
	"github.com/jacksontj/promxy/promclient"
	"github.com/jacksontj/promxy/proxyquerier"
	"github.com/jacksontj/promxy/servergroup"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/pkg/timestamp"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/storage"
	"github.com/sirupsen/logrus"
)

type proxyStorageState struct {
	serverGroups []*servergroup.ServerGroup
	cfg          *proxyconfig.PromxyConfig
}

func (p *proxyStorageState) Ready() {
	for _, sg := range p.serverGroups {
		<-sg.Ready
	}
}

func (p *proxyStorageState) Cancel() {
	if p.serverGroups != nil {
		for _, sg := range p.serverGroups {
			sg.Cancel()
		}
	}
}

func NewProxyStorage() (*ProxyStorage, error) {
	return &ProxyStorage{}, nil
}

// TODO: rename?
type ProxyStorage struct {
	state atomic.Value
}

func (p *ProxyStorage) GetState() *proxyStorageState {
	tmp := p.state.Load()
	if sg, ok := tmp.(*proxyStorageState); ok {
		return sg
	} else {
		return nil
	}
}

func (p *ProxyStorage) ApplyConfig(c *proxyconfig.Config) error {
	failed := false

	newState := &proxyStorageState{
		serverGroups: make([]*servergroup.ServerGroup, len(c.ServerGroups)),
		cfg:          &c.PromxyConfig,
	}
	for i, sgCfg := range c.ServerGroups {
		tmp := servergroup.New()
		if err := tmp.ApplyConfig(sgCfg); err != nil {
			failed = true
			logrus.Errorf("Error applying config to server group: %s", err)
		}
		newState.serverGroups[i] = tmp
	}

	if failed {
		for _, sg := range newState.serverGroups {
			sg.Cancel()
		}
		return fmt.Errorf("Error Applying Config to one or more server group(s)")
	}

	newState.Ready()         // Wait for the newstate to be ready
	oldState := p.GetState() // Fetch the old state
	p.state.Store(newState)  // Store the new state
	if oldState != nil {
		oldState.Cancel() // Cancel the old one
	}

	return nil
}

func (p *ProxyStorage) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) {
	state := p.GetState()
	return &proxyquerier.ProxyQuerier{
		ctx,
		timestamp.Time(mint),
		timestamp.Time(maxt),
		state.serverGroups,

		state.cfg,
	}, nil
}

func (p *ProxyStorage) StartTime() (int64, error) {
	return 0, nil
}

// TODO: remove?
type appenderStub struct{}

func (a *appenderStub) Add(l labels.Labels, t int64, v float64) (uint64, error) { return 0, nil }

func (a *appenderStub) AddFast(l labels.Labels, ref uint64, t int64, v float64) error { return nil }

// Commit submits the collected samples and purges the batch.
func (a *appenderStub) Commit() error { return nil }

func (a *appenderStub) Rollback() error { return nil }

func (p *ProxyStorage) Appender() (storage.Appender, error) {
	return &appenderStub{}, nil
}

// TODO: actually close things?
func (p *ProxyStorage) Close() error { return nil }

// This replaces promql Nodes with more efficient-to-fetch ones. This works by taking lower-layer
// chunks of the query, farming them out to prometheus hosts, then stitching the results back together.
// An example would be a sum, we can sum multiple sums and come up with the same result -- so we do.
// There are a few ground rules for this:
//      - Children cannot be AggregateExpr: aggregates have their own combining logic, so its not safe to send a subquery with additional aggregations
//      - offsets within the subtree must match: if they don't then we'll get mismatched data, so we wait until we are far enough down the tree that they converge
//      - Don't reduce accuracy/granularity: the intention of this is to get the correct data faster, meaning correctness overrules speed.
func (p *ProxyStorage) NodeReplacer(ctx context.Context, s *promql.EvalStmt, node promql.Node) (promql.Node, error) {
	// If there is a child that is an aggregator we cannot do anything (as they have their own
	// rules around combining). We'll skip this node and let a lower layer take this on
	aggFinder := &BooleanFinder{Func: func(node promql.Node) bool {
		_, ok := node.(*promql.AggregateExpr)
		return ok
	}}

	if _, err := promql.Walk(ctx, aggFinder, s, node, nil, nil); err != nil {
		return nil, err
	}

	if aggFinder.Found {
		return nil, nil
	}

	// If the tree below us is not all the same offset, then we can't do anything below -- we'll need
	// to wait until further in execution where they all match
	var offset time.Duration

	visitor := &OffsetFinder{}
	if _, err := promql.Walk(ctx, visitor, s, node, nil, nil); err != nil {
		return nil, err
	}
	// If we couldn't find an offset, then something is wrong-- lets skip
	// Also if there was an error, skip
	if !visitor.Found || visitor.Error != nil {
		return nil, nil
	}
	offset = visitor.Offset

	// Function to recursivelt remove offset. This is needed as we're using
	// the node API to String() the query to downstreams. Promql's iterators require
	// that the time be the absolute time, whereas the API returns them based on the
	// range you ask for (with the offset being implicit)
	// TODO: rename
	removeOffset := func() error {
		_, err := promql.Walk(ctx, &OffsetRemover{}, s, node, nil, nil)
		return err
	}

	state := p.GetState()
	serverGroups := servergroup.ServerGroups(state.serverGroups)
	switch n := node.(type) {
	// Some AggregateExprs can be composed (meaning they are "reentrant". If the aggregation op
	// is reentrant/composable then we'll do so, otherwise we let it fall through to normal query mechanisms
	case *promql.AggregateExpr:
		logrus.Debugf("AggregateExpr %v", n)

		var result model.Value
		var err error

		// Not all Aggregation functions are composable, so we'll do what we can
		switch n.Op.String() {
		// All "reentrant" cases (meaning they can be done repeatedly and the outcome doesn't change)
		case "sum", "min", "max", "topk", "bottomk":
			var urlBase string
			values := url.Values{}
			removeOffset()
			values.Add("query", n.String())

			if s.Interval > 0 {
				values.Add("start", model.Time(timestamp.FromTime(s.Start.Add(-offset-promql.LookbackDelta))).String())
				values.Add("end", model.Time(timestamp.FromTime(s.End.Add(-offset))).String())
				values.Add("step", strconv.FormatFloat(s.Interval.Seconds(), 'f', -1, 64))
				urlBase = "/api/v1/query_range"
			} else {
				values.Add("time", model.Time(timestamp.FromTime(s.Start.Add(-offset))).String())
				urlBase = "/api/v1/query"
			}

			result, err = serverGroups.GetData(ctx, urlBase, values)
			if err != nil {
				return nil, err
			}

		// Convert avg into sum() / count()
		case "avg":
			// Replace with sum() / count()
			return &promql.BinaryExpr{
				Op: 24, // Divide  TODO
				LHS: &promql.AggregateExpr{
					Op:       41, // sum() TODO
					Expr:     n.Expr,
					Param:    n.Param,
					Grouping: n.Grouping,
					Without:  n.Without,
				},

				RHS: &promql.AggregateExpr{
					Op:       40, // count() TODO
					Expr:     n.Expr,
					Param:    n.Param,
					Grouping: n.Grouping,
					Without:  n.Without,
				},
				VectorMatching: &promql.VectorMatching{Card: promql.CardOneToOne},
			}, nil

		// For count we simply need to change this to a sum over the data we get back
		case "count":
			var urlBase string
			values := url.Values{}
			removeOffset()
			values.Add("query", n.String())

			if s.Interval > 0 {
				values.Add("start", model.Time(timestamp.FromTime(s.Start.Add(-offset-promql.LookbackDelta))).String())
				values.Add("end", model.Time(timestamp.FromTime(s.End.Add(-offset))).String())
				values.Add("step", strconv.FormatFloat(s.Interval.Seconds(), 'f', -1, 64))
				urlBase = "/api/v1/query_range"
			} else {
				values.Add("time", model.Time(timestamp.FromTime(s.Start.Add(-offset))).String())
				urlBase = "/api/v1/query"
			}

			result, err = serverGroups.GetData(ctx, urlBase, values)
			if err != nil {
				return nil, err
			}
			// TODO: have a reverse method in promql/lex.go
			n.Op = 41 // SUM

		case "stddev": // TODO: something?
		case "stdvar": // TODO: something?

			// Do nothing, we want to allow the VectorSelector to fall through to do a query_range.
			// Unless we find another way to decompose the query, this is all we can do
		case "count_values":
			// DO NOTHING

		case "quantile": // TODO: something?

		}

		if result != nil {
			iterators := promclient.IteratorsForValue(result)

			series := make([]storage.Series, len(iterators))
			for i, iterator := range iterators {
				series[i] = &proxyquerier.Series{iterator}
			}

			ret := &promql.VectorSelector{Offset: offset}
			ret.SetSeries(series)
			n.Expr = ret
			return n, nil
		}

	// Call is for things such as rate() etc. This can be sent directly to the
	// prometheus node to answer
	case *promql.Call:
		logrus.Debugf("call %v %v", n, n.Type())
		var urlBase string
		values := url.Values{}
		removeOffset()
		values.Add("query", n.String())

		if s.Interval > 0 {
			values.Add("start", model.Time(timestamp.FromTime(s.Start.Add(-offset-promql.LookbackDelta))).String())
			values.Add("end", model.Time(timestamp.FromTime(s.End.Add(-offset))).String())
			values.Add("step", strconv.FormatFloat(s.Interval.Seconds(), 'f', -1, 64))
			urlBase = "/api/v1/query_range"
		} else {
			values.Add("time", model.Time(timestamp.FromTime(s.Start.Add(-offset))).String())
			urlBase = "/api/v1/query"
		}

		result, err := serverGroups.GetData(ctx, urlBase, values)
		if err != nil {
			return nil, err
		}
		iterators := promclient.IteratorsForValue(result)
		series := make([]storage.Series, len(iterators))
		for i, iterator := range iterators {
			series[i] = &proxyquerier.Series{iterator}
		}

		ret := &promql.VectorSelector{Offset: offset}
		ret.SetSeries(series)
		return ret, nil

		// If we are simply fetching a Vector then we can fetch the data using the same step that
		// the query came in as (reducing the amount of data we need to fetch)
	// If we are simply fetching data, we skip here to let it fall through to the normal
	// storage API
	case *promql.VectorSelector:
		// Do Nothing
		return nil, nil

	// If we hit this someone is asking for a matrix directly, if so then we don't
	// have anyway to ask for less-- since this is exactly what they are asking for
	case *promql.MatrixSelector:
		// DO NOTHING

	default:
		logrus.Debugf("default %v %s", n, reflect.TypeOf(n))

	}
	return nil, nil

}
<|endoftext|>