<commit_before>package main

import (
	"time"

	"github.com/MustWin/baremtlclient"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

// ResourceIdentityUser exposes a IdentityUser Resource
func ResourceIdentityUser() *schema.Resource {
	return &schema.Resource{
		Create: resourceIdentityUserCreate,
		Read:   resourceIdentityUserRead,
		Update: resourceIdentityUserUpdate,
		Delete: resourceIdentityUserDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"description": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"compartment_id": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"state": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"time_created": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"time_modified": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceIdentityUserCreate(d *schema.ResourceData, m interface{}) (e error) {
	client := m.(BareMetalClient)

	name := d.Get("name").(string)
	description := d.Get("description").(string)

	var user *baremtlsdk.Resource
	if user, e = client.CreateUser(name, description); e != nil {
		return
	}

	// Set the id and set any fields that were returned by the API.
	d.SetId(user.ID)
	setResourceData(d, user)

	if user.State != baremtlsdk.ResourceCreated {
		user, e = waitForUserStateRefresh(d, client)
	}

	return
}

func resourceIdentityUserRead(d *schema.ResourceData, m interface{}) (e error) {
	client := m.(BareMetalClient)

	var user *baremtlsdk.Resource
	if user, e = client.GetUser(d.Id()); e != nil {
		return
	}

	setResourceData(d, user)

	return
}

func resourceIdentityUserUpdate(d *schema.ResourceData, m interface{}) (e error) {
	client := m.(BareMetalClient)

	desc := d.Get("description").(string)

	var user *baremtlsdk.Resource
	if user, e = client.UpdateUser(d.Id(), desc); e != nil {
		return
	}

	// Capture any upstream changes, like time_modified.
	setResourceData(d, user)

	return
}

func resourceIdentityUserDelete(d *schema.ResourceData, m interface{}) (e error) {
	client := m.(BareMetalClient)

	if e = client.DeleteUser(d.Id()); e != nil {
		return
	}

	return
}

func userStateRefreshFunc(client BareMetalClient, d *schema.ResourceData) resource.StateRefreshFunc {
	return func() (user interface{}, s string, e error) {
		if user, e = client.GetUser(d.Id()); e != nil {
			return nil, "", e
		}
		s = user.(*baremtlsdk.Resource).State
		return
	}
}

func setResourceData(d *schema.ResourceData, user *baremtlsdk.Resource) {
	d.Set("name", user.Name)
	d.Set("description", user.Description)
	d.Set("compartment_id", user.CompartmentID)
	d.Set("state", user.State)
	d.Set("time_modified", user.TimeModified.String())
	d.Set("time_created", user.TimeCreated.String())
}

func waitForUserStateRefresh(d *schema.ResourceData, c BareMetalClient) (user *baremtlsdk.Resource, e error) {
	stateConf := &resource.StateChangeConf{
		Pending: []string{baremtlsdk.ResourceCreating},
		Target:  []string{baremtlsdk.ResourceCreated},
		Refresh: userStateRefreshFunc(c, d),
		Timeout: 5 * time.Minute,
	}

	rawu, err := stateConf.WaitForState()
	user = rawu.(*baremtlsdk.Resource)
	if e = err; e != nil {
		return
	}

	// Fields may have changed during polling, set them again.
	setResourceData(d, user)

	return
}
<commit_msg>Partial state for description so it is not saved if update fails. There is no way to test this, but I've opened a ticket: https://github.com/hashicorp/terraform/issues/7950<commit_after>package main

import (
	"time"

	"github.com/MustWin/baremtlclient"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

// ResourceIdentityUser exposes a IdentityUser Resource
func ResourceIdentityUser() *schema.Resource {
	return &schema.Resource{
		Create: resourceIdentityUserCreate,
		Read:   resourceIdentityUserRead,
		Update: resourceIdentityUserUpdate,
		Delete: resourceIdentityUserDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"description": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"compartment_id": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"state": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"time_created": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"time_modified": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceIdentityUserCreate(d *schema.ResourceData, m interface{}) (e error) {
	client := m.(BareMetalClient)

	name := d.Get("name").(string)
	description := d.Get("description").(string)

	var user *baremtlsdk.Resource
	if user, e = client.CreateUser(name, description); e != nil {
		return
	}

	// Set the id and set any fields that were returned by the API.
	d.SetId(user.ID)
	setResourceData(d, user)

	if user.State != baremtlsdk.ResourceCreated {
		user, e = waitForUserStateRefresh(d, client)
	}

	return
}

func resourceIdentityUserRead(d *schema.ResourceData, m interface{}) (e error) {
	client := m.(BareMetalClient)

	var user *baremtlsdk.Resource
	if user, e = client.GetUser(d.Id()); e != nil {
		return
	}

	setResourceData(d, user)

	return
}

func resourceIdentityUserUpdate(d *schema.ResourceData, m interface{}) (e error) {
	client := m.(BareMetalClient)

	desc := d.Get("description").(string)

	d.Partial(true)
	var user *baremtlsdk.Resource
	if user, e = client.UpdateUser(d.Id(), desc); e != nil {
		return
	}
	d.Partial(false)

	// Capture any upstream changes, like time_modified.
	setResourceData(d, user)

	return
}

func resourceIdentityUserDelete(d *schema.ResourceData, m interface{}) (e error) {
	client := m.(BareMetalClient)

	if e = client.DeleteUser(d.Id()); e != nil {
		return
	}

	return
}

func userStateRefreshFunc(client BareMetalClient, d *schema.ResourceData) resource.StateRefreshFunc {
	return func() (user interface{}, s string, e error) {
		if user, e = client.GetUser(d.Id()); e != nil {
			return nil, "", e
		}
		s = user.(*baremtlsdk.Resource).State
		return
	}
}

func setResourceData(d *schema.ResourceData, user *baremtlsdk.Resource) {
	d.Set("name", user.Name)
	d.Set("description", user.Description)
	d.Set("compartment_id", user.CompartmentID)
	d.Set("state", user.State)
	d.Set("time_modified", user.TimeModified.String())
	d.Set("time_created", user.TimeCreated.String())
}

func waitForUserStateRefresh(d *schema.ResourceData, c BareMetalClient) (user *baremtlsdk.Resource, e error) {
	stateConf := &resource.StateChangeConf{
		Pending: []string{baremtlsdk.ResourceCreating},
		Target:  []string{baremtlsdk.ResourceCreated},
		Refresh: userStateRefreshFunc(c, d),
		Timeout: 5 * time.Minute,
	}

	rawu, err := stateConf.WaitForState()
	user = rawu.(*baremtlsdk.Resource)
	if e = err; e != nil {
		return
	}

	// Fields may have changed during polling, set them again.
	setResourceData(d, user)

	return
}
<|endoftext|>


<commit_before>package s3

import (
	"bytes"
	"errors"
	"io"
	"strings"
	"sync"

	"github.com/mitchellh/goamz/aws"
	as3 "github.com/mitchellh/goamz/s3"

	"github.com/restic/restic/backend"
)

var ErrWrongData = errors.New("wrong data returned by backend, checksum does not match")

func s3path(t backend.Type, name string) string {
	if t == backend.Config {
		return string(t)
	}
	return string(t) + "/" + name
}

type S3DB struct {
	bucket *as3.Bucket
	mput   sync.Mutex
	path   string
}

// Open opens the local backend at bucket and region.
func Open(regionname, bucketname string) (*S3DB, error) {
	auth, err := aws.EnvAuth()
	if err != nil {
		return nil, err
	}

	client := as3.New(auth, aws.Regions[regionname])

	return &S3DB{bucket: client.Bucket(bucketname), path: bucketname}, nil
}

// Location returns this backend's location (the directory name).
func (b *S3DB) Location() string {
	return b.path
}

type s3Blob struct {
	b     *S3DB
	buf   *bytes.Buffer
	final bool
}

func (bb *s3Blob) Write(p []byte) (int, error) {
	if bb.final {
		return 0, errors.New("blob already closed")
	}

	n, err := bb.buf.Write(p)
	return n, err
}

func (bb *s3Blob) Read(p []byte) (int, error) {
	return bb.buf.Read(p)
}

func (bb *s3Blob) Close() error {
	bb.final = true
	bb.buf.Reset()
	return nil
}

func (bb *s3Blob) Size() uint {
	return uint(bb.buf.Len())
}

func (bb *s3Blob) Finalize(t backend.Type, name string) error {
	if bb.final {
		return errors.New("Already finalized")
	}

	bb.final = true

	path := s3path(t, name)
	bb.b.mput.Lock()
	err := bb.b.bucket.Put(path, bb.buf.Bytes(), "binary/octet-stream", "private")
	bb.b.mput.Unlock()
	bb.buf.Reset()
	return err
}

// Create creates a new Blob. The data is available only after Finalize()
// has been called on the returned Blob.
func (b *S3DB) Create() (backend.Blob, error) {
	blob := s3Blob{
		b:   b,
		buf: &bytes.Buffer{},
	}

	return &blob, nil
}

func (b *S3DB) get(t backend.Type, name string) (*s3Blob, error) {
	blob := &s3Blob{
		b: b,
	}

	path := s3path(t, name)
	data, err := b.bucket.Get(path)
	blob.buf = bytes.NewBuffer(data)
	return blob, err
}

// Get returns a reader that yields the content stored under the given
// name. The reader should be closed after draining it.
func (b *S3DB) Get(t backend.Type, name string) (io.ReadCloser, error) {
	return b.get(t, name)
}

// GetReader returns an io.ReadCloser for the Blob with the given name of
// type t at offset and length. If length is 0, the reader reads until EOF.
func (b *S3DB) GetReader(t backend.Type, name string, offset, length uint) (io.ReadCloser, error) {
	blob, err := b.get(t, name)
	if err != nil {
		return nil, err
	}

	blob.buf.Next(int(offset))

	if length == 0 {
		return blob, nil
	}

	return backend.LimitReadCloser(blob, int64(length)), nil
}

// Test returns true if a blob of the given type and name exists in the backend.
func (b *S3DB) Test(t backend.Type, name string) (bool, error) {
	found := false
	path := s3path(t, name)
	key, err := b.bucket.GetKey(path)
	if err == nil && key.Key == path {
		found = true
	}

	// If error, then not found
	return found, nil
}

// Remove removes the blob with the given name and type.
func (b *S3DB) Remove(t backend.Type, name string) error {
	path := s3path(t, name)
	return b.bucket.Del(path)
}

// List returns a channel that yields all names of blobs of type t. A
// goroutine is started for this. If the channel done is closed, sending
// stops.
func (b *S3DB) List(t backend.Type, done <-chan struct{}) <-chan string {
	ch := make(chan string)

	prefix := string(t) + "/"

	listresp, err := b.bucket.List(prefix, "/", "", 10000)

	if err != nil {
		close(ch)
		return ch
	}

	matches := make([]string, len(listresp.Contents))
	for idx, key := range listresp.Contents {
		matches[idx] = strings.TrimPrefix(key.Key, prefix)
	}

	go func() {
		defer close(ch)
		for _, m := range matches {
			if m == "" {
				continue
			}

			select {
			case ch <- m:
			case <-done:
				return
			}
		}
	}()

	return ch
}

// Delete removes the repository and all files.
func (b *S3DB) Delete() error {
	return b.bucket.DelBucket()
}

// Close does nothing
func (b *S3DB) Close() error { return nil }
<commit_msg>Handle long list responses from s3.<commit_after>package s3

import (
	"bytes"
	"errors"
	"io"
	"strings"
	"sync"

	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/s3"

	"github.com/restic/restic/backend"
)

const maxKeysInList = 1000

func s3path(t backend.Type, name string) string {
	if t == backend.Config {
		return string(t)
	}
	return string(t) + "/" + name
}

type S3 struct {
	bucket *s3.Bucket
	mput   sync.Mutex
	path   string
}

// Open opens the s3 backend at bucket and region.
func Open(regionname, bucketname string) (*S3, error) {
	auth, err := aws.EnvAuth()
	if err != nil {
		return nil, err
	}

	client := s3.New(auth, aws.Regions[regionname])

	return &S3{bucket: client.Bucket(bucketname), path: bucketname}, nil
}

// Location returns this backend's location (the bucket name).
func (b *S3) Location() string {
	return b.path
}

type s3Blob struct {
	b     *S3
	buf   *bytes.Buffer
	final bool
}

func (bb *s3Blob) Write(p []byte) (int, error) {
	if bb.final {
		return 0, errors.New("blob already closed")
	}

	n, err := bb.buf.Write(p)
	return n, err
}

func (bb *s3Blob) Read(p []byte) (int, error) {
	return bb.buf.Read(p)
}

func (bb *s3Blob) Close() error {
	bb.final = true
	bb.buf.Reset()
	return nil
}

func (bb *s3Blob) Size() uint {
	return uint(bb.buf.Len())
}

func (bb *s3Blob) Finalize(t backend.Type, name string) error {
	if bb.final {
		return errors.New("Already finalized")
	}

	bb.final = true

	path := s3path(t, name)
	bb.b.mput.Lock()
	err := bb.b.bucket.Put(path, bb.buf.Bytes(), "binary/octet-stream", "private")
	bb.b.mput.Unlock()
	bb.buf.Reset()
	return err
}

// Create creates a new Blob. The data is available only after Finalize()
// has been called on the returned Blob.
func (b *S3) Create() (backend.Blob, error) {
	blob := s3Blob{
		b:   b,
		buf: &bytes.Buffer{},
	}

	return &blob, nil
}

func (b *S3) get(t backend.Type, name string) (*s3Blob, error) {
	blob := &s3Blob{
		b: b,
	}

	path := s3path(t, name)
	data, err := b.bucket.Get(path)
	blob.buf = bytes.NewBuffer(data)
	return blob, err
}

// Get returns a reader that yields the content stored under the given
// name. The reader should be closed after draining it.
func (b *S3) Get(t backend.Type, name string) (io.ReadCloser, error) {
	return b.get(t, name)
}

// GetReader returns an io.ReadCloser for the Blob with the given name of
// type t at offset and length. If length is 0, the reader reads until EOF.
func (b *S3) GetReader(t backend.Type, name string, offset, length uint) (io.ReadCloser, error) {
	blob, err := b.get(t, name)
	if err != nil {
		return nil, err
	}

	blob.buf.Next(int(offset))

	if length == 0 {
		return blob, nil
	}

	return backend.LimitReadCloser(blob, int64(length)), nil
}

// Test returns true if a blob of the given type and name exists in the backend.
func (b *S3) Test(t backend.Type, name string) (bool, error) {
	found := false
	path := s3path(t, name)
	key, err := b.bucket.GetKey(path)
	if err == nil && key.Key == path {
		found = true
	}

	// If error, then not found
	return found, nil
}

// Remove removes the blob with the given name and type.
func (b *S3) Remove(t backend.Type, name string) error {
	path := s3path(t, name)
	return b.bucket.Del(path)
}

// List returns a channel that yields all names of blobs of type t. A
// goroutine is started for this. If the channel done is closed, sending
// stops.
func (b *S3) List(t backend.Type, done <-chan struct{}) <-chan string {
	ch := make(chan string)

	prefix := string(t) + "/"

	listresp, err := b.bucket.List(prefix, "/", "", maxKeysInList)

	if err != nil {
		close(ch)
		return ch
	}

	matches := make([]string, len(listresp.Contents))
	for idx, key := range listresp.Contents {
		matches[idx] = strings.TrimPrefix(key.Key, prefix)
	}

	// Continue making requests to get full list.
	for listresp.IsTruncated {
		listresp, err = b.bucket.List(prefix, "/", listresp.NextMarker, maxKeysInList)
		if err != nil {
			close(ch)
			return ch
		}

		for _, key := range listresp.Contents {
			matches = append(matches, strings.TrimPrefix(key.Key, prefix))
		}
	}

	go func() {
		defer close(ch)
		for _, m := range matches {
			if m == "" {
				continue
			}

			select {
			case ch <- m:
			case <-done:
				return
			}
		}
	}()

	return ch
}

// Delete removes the repository and all files.
func (b *S3) Delete() error {
	return b.bucket.DelBucket()
}

// Close does nothing
func (b *S3) Close() error { return nil }
<|endoftext|>


<commit_before>/*
 Copyright 2016 Padduck, LLC

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

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

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

package main

import (
	"flag"
	"io/ioutil"
	"os"
	"path/filepath"

	"fmt"

	"net/http"
	"strings"

	"github.com/braintree/manners"
	"github.com/gin-gonic/gin"
	"github.com/pufferpanel/apufferi/config"
	"github.com/pufferpanel/apufferi/logging"
	"github.com/pufferpanel/pufferd/data"
	"github.com/pufferpanel/pufferd/data/templates"
	"github.com/pufferpanel/pufferd/install"
	"github.com/pufferpanel/pufferd/migration"
	"github.com/pufferpanel/pufferd/programs"
	"github.com/pufferpanel/pufferd/routing"
	"github.com/pufferpanel/pufferd/sftp"
	"github.com/pufferpanel/pufferd/shutdown"
	"github.com/pufferpanel/pufferd/uninstaller"
)

var (
	VERSION      = "nightly"
	MAJORVERSION = "nightly"
	BUILDDATE    = "unknown"
	GITHASH      = "unknown"
)

func main() {
	var loggingLevel string
	var authRoot string
	var authToken string
	var runInstaller bool
	var version bool
	var license bool
	var regenerate bool
	var migrate bool
	var uninstall bool
	var configPath string
	var pid int
	var installService bool
	flag.StringVar(&loggingLevel, "logging", "INFO", "Lowest logging level to display")
	flag.StringVar(&authRoot, "auth", "", "Base URL to the authorization server")
	flag.StringVar(&authToken, "token", "", "Authorization token")
	flag.BoolVar(&runInstaller, "install", false, "If installing instead of running")
	flag.BoolVar(&version, "version", false, "Get the version")
	flag.BoolVar(&license, "license", false, "View license")
	flag.BoolVar(&regenerate, "regenerate", false, "Regenerate pufferd templates")
	flag.BoolVar(&migrate, "migrate", false, "Migrate Scales data to pufferd")
	flag.BoolVar(&uninstall, "uninstall", false, "Uninstall pufferd")
	flag.StringVar(&configPath, "config", "config.json", "Path to pufferd config.json")
	flag.IntVar(&pid, "shutdown", 0, "PID to shut down")
	flag.BoolVar(&installService, "installService", false, "Installs the pufferd service file")
	flag.Parse()

	versionString := fmt.Sprintf("pufferd %s (%s)", VERSION, GITHASH)

	if pid != 0 {
		logging.Info("Shutting down")
		shutdown.Command(pid)
	}

	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		if _, err := os.Stat("/etc/pufferd/config.json"); err == nil {
			logging.Info("No config passed, defaulting to /etc/pufferd/config.json")
			configPath = "/etc/pufferd/config.json"
		} else {
			logging.Error("Cannot find a config file!")
			Shutdown.CompleteShutdown()
		}
	}

	if uninstall {
		fmt.Println("This option will UNINSTALL pufferd, are you sure? Please enter \"yes\" to proceed [no]")
		var response string
		fmt.Scanln(&response)
		if strings.ToLower(response) == "yes" || strings.ToLower(response) == "y" {
			if os.Geteuid() != 0 {
				logging.Error("To uninstall pufferd you need to have sudo or root privileges")
			} else {
				config.Load(configPath)
				uninstaller.StartProcess()
				logging.Info("pufferd is now uninstalled.")
			}
		} else {
			logging.Info("Uninstall process aborted")
			logging.Info("Exiting")
		}
		return
	}

	if version {
		os.Stdout.WriteString(versionString + "\r\n")
	}

	if license {
		os.Stdout.WriteString(data.LICENSE + "\r\n")
	}

	if regenerate {
		config.Load(configPath)
		programs.Initialize()

		if _, err := os.Stat(programs.TemplateFolder); os.IsNotExist(err) {
			logging.Info("No template directory found, creating")
			err = os.MkdirAll(programs.TemplateFolder, 0755)
			if err != nil {
				logging.Error("Error creating template folder", err)
			}
		}
		// Overwrite existing templates
		templates.CopyTemplates()
		logging.Info("Templates regenerated")
	}

	if migrate {
		config.Load(configPath)
		migration.MigrateFromScales()
	}

	if installService {
		install.InstallService()
	}

	if license || version || regenerate || migrate || pid != 0 {
		return
	}

	config.Load(configPath)

	logging.SetLevelByString(loggingLevel)
	var logPath = config.GetOrDefault("logPath", "logs")
	logging.SetLogFolder(logPath)
	logging.Init()
	gin.SetMode(gin.ReleaseMode)

	logging.Info(versionString)
	logging.Info("Logging set to " + loggingLevel)

	if runInstaller {
		install.Install(configPath, authRoot, authToken)
	}

	if runInstaller || installService {
		return
	}

	programs.Initialize()

	if _, err := os.Stat(programs.TemplateFolder); os.IsNotExist(err) {
		logging.Info("No template directory found, creating")
		err = os.MkdirAll(programs.TemplateFolder, 0755)
		if err != nil {
			logging.Error("Error creating template folder", err)
		}

	}
	if files, _ := ioutil.ReadDir(programs.TemplateFolder); len(files) == 0 {
		logging.Info("Templates being copied to " + programs.TemplateFolder)
		templates.CopyTemplates()
	}

	if _, err := os.Stat(programs.ServerFolder); os.IsNotExist(err) {
		logging.Info("No server directory found, creating")
		os.MkdirAll(programs.ServerFolder, 0755)
	}

	programs.LoadFromFolder()

	for _, element := range programs.GetAll() {
		if element.IsEnabled() && element.IsAutoStart() {
			logging.Info("Starting server " + element.Id())
			element.Start()
		}
	}

	r := routing.ConfigureWeb()

	var useHttps bool
	useHttps = false

	dataFolder := config.GetOrDefault("datafolder", "data")
	httpsPem := filepath.Join(dataFolder, "https.pem")
	httpsKey := filepath.Join(dataFolder, "https.key")

	if _, err := os.Stat(httpsPem); os.IsNotExist(err) {
		logging.Warn("No HTTPS.PEM found in data folder, will use http instead")
	} else if _, err := os.Stat(httpsKey); os.IsNotExist(err) {
		logging.Warn("No HTTPS.KEY found in data folder, will use http instead")
	} else {
		useHttps = true
	}

	sftp.Run()

	//check if there's an update
	if config.GetOrDefault("update-check", "true") == "true" {
		go func() {
			url := "https://dl.pufferpanel.com/pufferd/" + MAJORVERSION + "/version.txt"
			logging.Debug("Checking for updates using " + url)
			resp, err := http.Get(url)
			if err != nil {
				return
			}
			defer resp.Body.Close()
			onlineVersion, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				return
			}
			if string(onlineVersion) != GITHASH {
				logging.Infof("DL server reports a different hash than this version, an update may be available")
				logging.Infof("Installed: %s", GITHASH)
				logging.Infof("Online: %s", onlineVersion)
			}
		}()
	}

	web := config.GetOrDefault("web", config.GetOrDefault("webhost", "0.0.0.0")+":"+config.GetOrDefault("webport", "5656"))

	shutdown.CreateHook()

	logging.Infof("Starting web access on %s", web)
	var err error
	if useHttps {
		err = manners.ListenAndServeTLS(web, httpsPem, httpsKey, r)
	} else {
		err = manners.ListenAndServe(web, r)
	}
	if err != nil {
		logging.Error("Error starting web service", err)
	}
	shutdown.Shutdown()
}
<commit_msg>Fix shutdown if no config is found<commit_after>/*
 Copyright 2016 Padduck, LLC

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

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

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

package main

import (
	"flag"
	"io/ioutil"
	"os"
	"path/filepath"

	"fmt"

	"net/http"
	"strings"

	"github.com/braintree/manners"
	"github.com/gin-gonic/gin"
	"github.com/pufferpanel/apufferi/config"
	"github.com/pufferpanel/apufferi/logging"
	"github.com/pufferpanel/pufferd/data"
	"github.com/pufferpanel/pufferd/data/templates"
	"github.com/pufferpanel/pufferd/install"
	"github.com/pufferpanel/pufferd/migration"
	"github.com/pufferpanel/pufferd/programs"
	"github.com/pufferpanel/pufferd/routing"
	"github.com/pufferpanel/pufferd/sftp"
	"github.com/pufferpanel/pufferd/shutdown"
	"github.com/pufferpanel/pufferd/uninstaller"
)

var (
	VERSION      = "nightly"
	MAJORVERSION = "nightly"
	BUILDDATE    = "unknown"
	GITHASH      = "unknown"
)

func main() {
	var loggingLevel string
	var authRoot string
	var authToken string
	var runInstaller bool
	var version bool
	var license bool
	var regenerate bool
	var migrate bool
	var uninstall bool
	var configPath string
	var pid int
	var installService bool
	flag.StringVar(&loggingLevel, "logging", "INFO", "Lowest logging level to display")
	flag.StringVar(&authRoot, "auth", "", "Base URL to the authorization server")
	flag.StringVar(&authToken, "token", "", "Authorization token")
	flag.BoolVar(&runInstaller, "install", false, "If installing instead of running")
	flag.BoolVar(&version, "version", false, "Get the version")
	flag.BoolVar(&license, "license", false, "View license")
	flag.BoolVar(&regenerate, "regenerate", false, "Regenerate pufferd templates")
	flag.BoolVar(&migrate, "migrate", false, "Migrate Scales data to pufferd")
	flag.BoolVar(&uninstall, "uninstall", false, "Uninstall pufferd")
	flag.StringVar(&configPath, "config", "config.json", "Path to pufferd config.json")
	flag.IntVar(&pid, "shutdown", 0, "PID to shut down")
	flag.BoolVar(&installService, "installService", false, "Installs the pufferd service file")
	flag.Parse()

	versionString := fmt.Sprintf("pufferd %s (%s)", VERSION, GITHASH)

	if pid != 0 {
		logging.Info("Shutting down")
		shutdown.Command(pid)
	}

	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		if _, err := os.Stat("/etc/pufferd/config.json"); err == nil {
			logging.Info("No config passed, defaulting to /etc/pufferd/config.json")
			configPath = "/etc/pufferd/config.json"
		} else {
			logging.Error("Cannot find a config file!")
			shutdown.CompleteShutdown()
		}
	}

	if uninstall {
		fmt.Println("This option will UNINSTALL pufferd, are you sure? Please enter \"yes\" to proceed [no]")
		var response string
		fmt.Scanln(&response)
		if strings.ToLower(response) == "yes" || strings.ToLower(response) == "y" {
			if os.Geteuid() != 0 {
				logging.Error("To uninstall pufferd you need to have sudo or root privileges")
			} else {
				config.Load(configPath)
				uninstaller.StartProcess()
				logging.Info("pufferd is now uninstalled.")
			}
		} else {
			logging.Info("Uninstall process aborted")
			logging.Info("Exiting")
		}
		return
	}

	if version {
		os.Stdout.WriteString(versionString + "\r\n")
	}

	if license {
		os.Stdout.WriteString(data.LICENSE + "\r\n")
	}

	if regenerate {
		config.Load(configPath)
		programs.Initialize()

		if _, err := os.Stat(programs.TemplateFolder); os.IsNotExist(err) {
			logging.Info("No template directory found, creating")
			err = os.MkdirAll(programs.TemplateFolder, 0755)
			if err != nil {
				logging.Error("Error creating template folder", err)
			}
		}
		// Overwrite existing templates
		templates.CopyTemplates()
		logging.Info("Templates regenerated")
	}

	if migrate {
		config.Load(configPath)
		migration.MigrateFromScales()
	}

	if installService {
		install.InstallService()
	}

	if license || version || regenerate || migrate || pid != 0 {
		return
	}

	config.Load(configPath)

	logging.SetLevelByString(loggingLevel)
	var logPath = config.GetOrDefault("logPath", "logs")
	logging.SetLogFolder(logPath)
	logging.Init()
	gin.SetMode(gin.ReleaseMode)

	logging.Info(versionString)
	logging.Info("Logging set to " + loggingLevel)

	if runInstaller {
		install.Install(configPath, authRoot, authToken)
	}

	if runInstaller || installService {
		return
	}

	programs.Initialize()

	if _, err := os.Stat(programs.TemplateFolder); os.IsNotExist(err) {
		logging.Info("No template directory found, creating")
		err = os.MkdirAll(programs.TemplateFolder, 0755)
		if err != nil {
			logging.Error("Error creating template folder", err)
		}

	}
	if files, _ := ioutil.ReadDir(programs.TemplateFolder); len(files) == 0 {
		logging.Info("Templates being copied to " + programs.TemplateFolder)
		templates.CopyTemplates()
	}

	if _, err := os.Stat(programs.ServerFolder); os.IsNotExist(err) {
		logging.Info("No server directory found, creating")
		os.MkdirAll(programs.ServerFolder, 0755)
	}

	programs.LoadFromFolder()

	for _, element := range programs.GetAll() {
		if element.IsEnabled() && element.IsAutoStart() {
			logging.Info("Starting server " + element.Id())
			element.Start()
		}
	}

	r := routing.ConfigureWeb()

	var useHttps bool
	useHttps = false

	dataFolder := config.GetOrDefault("datafolder", "data")
	httpsPem := filepath.Join(dataFolder, "https.pem")
	httpsKey := filepath.Join(dataFolder, "https.key")

	if _, err := os.Stat(httpsPem); os.IsNotExist(err) {
		logging.Warn("No HTTPS.PEM found in data folder, will use http instead")
	} else if _, err := os.Stat(httpsKey); os.IsNotExist(err) {
		logging.Warn("No HTTPS.KEY found in data folder, will use http instead")
	} else {
		useHttps = true
	}

	sftp.Run()

	//check if there's an update
	if config.GetOrDefault("update-check", "true") == "true" {
		go func() {
			url := "https://dl.pufferpanel.com/pufferd/" + MAJORVERSION + "/version.txt"
			logging.Debug("Checking for updates using " + url)
			resp, err := http.Get(url)
			if err != nil {
				return
			}
			defer resp.Body.Close()
			onlineVersion, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				return
			}
			if string(onlineVersion) != GITHASH {
				logging.Infof("DL server reports a different hash than this version, an update may be available")
				logging.Infof("Installed: %s", GITHASH)
				logging.Infof("Online: %s", onlineVersion)
			}
		}()
	}

	web := config.GetOrDefault("web", config.GetOrDefault("webhost", "0.0.0.0")+":"+config.GetOrDefault("webport", "5656"))

	shutdown.CreateHook()

	logging.Infof("Starting web access on %s", web)
	var err error
	if useHttps {
		err = manners.ListenAndServeTLS(web, httpsPem, httpsKey, r)
	} else {
		err = manners.ListenAndServe(web, r)
	}
	if err != nil {
		logging.Error("Error starting web service", err)
	}
	shutdown.Shutdown()
}
<|endoftext|>


<commit_before>// Package fastcgi has middleware that acts as a FastCGI client. Requests
// that get forwarded to FastCGI stop the middleware execution chain.
// The most common use for this package is to serve PHP websites via php-fpm.
package fastcgi

import (
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/mholt/caddy/middleware"
)

// Handler is a middleware type that can handle requests as a FastCGI client.
type Handler struct {
	Next    middleware.Handler
	Rules   []Rule
	Root    string
	AbsRoot string // same as root, but absolute path
	FileSys http.FileSystem

	// These are sent to CGI scripts in env variables
	SoftwareName    string
	SoftwareVersion string
	ServerName      string
	ServerPort      string
}

// ServeHTTP satisfies the middleware.Handler interface.
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	for _, rule := range h.Rules {

		// First requirement: Base path must match
		if !middleware.Path(r.URL.Path).Matches(rule.Path) {
			continue
		}

		// In addition to matching the path, a request must meet some
		// other criteria before being proxied as FastCGI. For example,
		// we probably want to exclude static assets (CSS, JS, images...)
		// but we also want to be flexible for the script we proxy to.

		fpath := r.URL.Path
		if idx, ok := middleware.IndexFile(h.FileSys, fpath, rule.IndexFiles); ok {
			fpath = idx
		}

		// These criteria work well in this order for PHP sites
		if fpath[len(fpath)-1] == '/' || strings.HasSuffix(fpath, rule.Ext) || !h.exists(fpath) {

			// Create environment for CGI script
			env, err := h.buildEnv(r, rule, fpath)
			if err != nil {
				return http.StatusInternalServerError, err
			}

			// Connect to FastCGI gateway
			var fcgi *FCGIClient

			// check if unix socket or tcp
			if strings.HasPrefix(rule.Address, "/") || strings.HasPrefix(rule.Address, "unix:") {
				if strings.HasPrefix(rule.Address, "unix:") {
					rule.Address = rule.Address[len("unix:"):]
				}
				fcgi, err = Dial("unix", rule.Address)
			} else {
				fcgi, err = Dial("tcp", rule.Address)
			}
			if err != nil {
				return http.StatusBadGateway, err
			}

			var resp *http.Response
			contentLength, _ := strconv.Atoi(r.Header.Get("Content-Length"))
			switch r.Method {
			case "HEAD":
				resp, err = fcgi.Head(env)
			case "GET":
				resp, err = fcgi.Get(env)
			case "OPTIONS":
				resp, err = fcgi.Options(env)
			case "POST":
				resp, err = fcgi.Post(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			case "PUT":
				resp, err = fcgi.Put(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			case "PATCH":
				resp, err = fcgi.Patch(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			case "DELETE":
				resp, err = fcgi.Delete(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			default:
				return http.StatusMethodNotAllowed, nil
			}

			if resp.Body != nil {
				defer resp.Body.Close()
			}

			if err != nil && err != io.EOF {
				return http.StatusBadGateway, err
			}

			// Write the response header
			for key, vals := range resp.Header {
				for _, val := range vals {
					w.Header().Add(key, val)
				}
			}
			w.WriteHeader(resp.StatusCode)

			// Write the response body
			// TODO: If this has an error, the response will already be
			// partly written. We should copy out of resp.Body into a buffer
			// first, then write it to the response...
			_, err = io.Copy(w, resp.Body)
			if err != nil {
				return http.StatusBadGateway, err
			}

			return 0, nil
		}
	}

	return h.Next.ServeHTTP(w, r)
}

func (h Handler) exists(path string) bool {
	if _, err := os.Stat(h.Root + path); err == nil {
		return true
	}
	return false
}

// buildEnv returns a set of CGI environment variables for the request.
func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string]string, error) {
	var env map[string]string

	// Get absolute path of requested resource
	absPath := filepath.Join(h.AbsRoot, fpath)

	// Separate remote IP and port; more lenient than net.SplitHostPort
	var ip, port string
	if idx := strings.Index(r.RemoteAddr, ":"); idx > -1 {
		ip = r.RemoteAddr[:idx]
		port = r.RemoteAddr[idx+1:]
	} else {
		ip = r.RemoteAddr
	}

	// Split path in preparation for env variables
	splitPos := strings.Index(fpath, rule.SplitPath)
	var docURI, scriptName, scriptFilename, pathInfo string
	if splitPos == -1 {
		// Request doesn't have the extension, so assume index file in root
		docURI = "/" + rule.IndexFiles[0]
		scriptName = "/" + rule.IndexFiles[0]
		scriptFilename = filepath.Join(h.AbsRoot, rule.IndexFiles[0])
		pathInfo = fpath
	} else {
		// Request has the extension; path was split successfully
		docURI = fpath[:splitPos+len(rule.SplitPath)]
		pathInfo = fpath[splitPos+len(rule.SplitPath):]
		scriptName = fpath
		scriptFilename = absPath
	}

	// Some variables are unused but cleared explicitly to prevent
	// the parent environment from interfering.
	env = map[string]string{

		// Variables defined in CGI 1.1 spec
		"AUTH_TYPE":         "", // Not used
		"CONTENT_LENGTH":    r.Header.Get("Content-Length"),
		"CONTENT_TYPE":      r.Header.Get("Content-Type"),
		"GATEWAY_INTERFACE": "CGI/1.1",
		"PATH_INFO":         pathInfo,
		"QUERY_STRING":      r.URL.RawQuery,
		"REMOTE_ADDR":       ip,
		"REMOTE_HOST":       ip, // For speed, remote host lookups disabled
		"REMOTE_PORT":       port,
		"REMOTE_IDENT":      "", // Not used
		"REMOTE_USER":       "", // Not used
		"REQUEST_METHOD":    r.Method,
		"SERVER_NAME":       h.ServerName,
		"SERVER_PORT":       h.ServerPort,
		"SERVER_PROTOCOL":   r.Proto,
		"SERVER_SOFTWARE":   h.SoftwareName + "/" + h.SoftwareVersion,

		// Other variables
		"DOCUMENT_ROOT":   h.AbsRoot,
		"DOCUMENT_URI":    docURI,
		"HTTP_HOST":       r.Host, // added here, since not always part of headers
		"REQUEST_URI":     r.URL.RequestURI(),
		"SCRIPT_FILENAME": scriptFilename,
		"SCRIPT_NAME":     scriptName,
	}

	// compliance with the CGI specification that PATH_TRANSLATED
	// should only exist if PATH_INFO is defined.
	// Info: https://www.ietf.org/rfc/rfc3875 Page 14
	if env["PATH_INFO"] != "" {
		env["PATH_TRANSLATED"] = filepath.Join(h.AbsRoot, pathInfo) // Info: http://www.oreilly.com/openbook/cgi/ch02_04.html
	}

	// Add env variables from config
	for _, envVar := range rule.EnvVars {
		env[envVar[0]] = envVar[1]
	}

	// Add all HTTP headers to env variables
	for field, val := range r.Header {
		header := strings.ToUpper(field)
		header = headerNameReplacer.Replace(header)
		env["HTTP_"+header] = strings.Join(val, ", ")
	}

	return env, nil
}

// Rule represents a FastCGI handling rule.
type Rule struct {
	// The base path to match. Required.
	Path string

	// The address of the FastCGI server. Required.
	Address string

	// Always process files with this extension with fastcgi.
	Ext string

	// The path in the URL will be split into two, with the first piece ending
	// with the value of SplitPath. The first piece will be assumed as the
	// actual resource (CGI script) name, and the second piece will be set to
	// PATH_INFO for the CGI script to use.
	SplitPath string

	// If the URL ends with '/' (which indicates a directory), these index
	// files will be tried instead.
	IndexFiles []string

	// Environment Variables
	EnvVars [][2]string
}

var headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_")
<commit_msg>fastcgi: Add HTTPS env variable on HTTPS connections<commit_after>// Package fastcgi has middleware that acts as a FastCGI client. Requests
// that get forwarded to FastCGI stop the middleware execution chain.
// The most common use for this package is to serve PHP websites via php-fpm.
package fastcgi

import (
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/mholt/caddy/middleware"
)

// Handler is a middleware type that can handle requests as a FastCGI client.
type Handler struct {
	Next    middleware.Handler
	Rules   []Rule
	Root    string
	AbsRoot string // same as root, but absolute path
	FileSys http.FileSystem

	// These are sent to CGI scripts in env variables
	SoftwareName    string
	SoftwareVersion string
	ServerName      string
	ServerPort      string
}

// ServeHTTP satisfies the middleware.Handler interface.
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	for _, rule := range h.Rules {

		// First requirement: Base path must match
		if !middleware.Path(r.URL.Path).Matches(rule.Path) {
			continue
		}

		// In addition to matching the path, a request must meet some
		// other criteria before being proxied as FastCGI. For example,
		// we probably want to exclude static assets (CSS, JS, images...)
		// but we also want to be flexible for the script we proxy to.

		fpath := r.URL.Path
		if idx, ok := middleware.IndexFile(h.FileSys, fpath, rule.IndexFiles); ok {
			fpath = idx
		}

		// These criteria work well in this order for PHP sites
		if fpath[len(fpath)-1] == '/' || strings.HasSuffix(fpath, rule.Ext) || !h.exists(fpath) {

			// Create environment for CGI script
			env, err := h.buildEnv(r, rule, fpath)
			if err != nil {
				return http.StatusInternalServerError, err
			}

			// Connect to FastCGI gateway
			var fcgi *FCGIClient

			// check if unix socket or tcp
			if strings.HasPrefix(rule.Address, "/") || strings.HasPrefix(rule.Address, "unix:") {
				if strings.HasPrefix(rule.Address, "unix:") {
					rule.Address = rule.Address[len("unix:"):]
				}
				fcgi, err = Dial("unix", rule.Address)
			} else {
				fcgi, err = Dial("tcp", rule.Address)
			}
			if err != nil {
				return http.StatusBadGateway, err
			}

			var resp *http.Response
			contentLength, _ := strconv.Atoi(r.Header.Get("Content-Length"))
			switch r.Method {
			case "HEAD":
				resp, err = fcgi.Head(env)
			case "GET":
				resp, err = fcgi.Get(env)
			case "OPTIONS":
				resp, err = fcgi.Options(env)
			case "POST":
				resp, err = fcgi.Post(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			case "PUT":
				resp, err = fcgi.Put(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			case "PATCH":
				resp, err = fcgi.Patch(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			case "DELETE":
				resp, err = fcgi.Delete(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			default:
				return http.StatusMethodNotAllowed, nil
			}

			if resp.Body != nil {
				defer resp.Body.Close()
			}

			if err != nil && err != io.EOF {
				return http.StatusBadGateway, err
			}

			// Write the response header
			for key, vals := range resp.Header {
				for _, val := range vals {
					w.Header().Add(key, val)
				}
			}
			w.WriteHeader(resp.StatusCode)

			// Write the response body
			// TODO: If this has an error, the response will already be
			// partly written. We should copy out of resp.Body into a buffer
			// first, then write it to the response...
			_, err = io.Copy(w, resp.Body)
			if err != nil {
				return http.StatusBadGateway, err
			}

			return 0, nil
		}
	}

	return h.Next.ServeHTTP(w, r)
}

func (h Handler) exists(path string) bool {
	if _, err := os.Stat(h.Root + path); err == nil {
		return true
	}
	return false
}

// buildEnv returns a set of CGI environment variables for the request.
func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string]string, error) {
	var env map[string]string

	// Get absolute path of requested resource
	absPath := filepath.Join(h.AbsRoot, fpath)

	// Separate remote IP and port; more lenient than net.SplitHostPort
	var ip, port string
	if idx := strings.Index(r.RemoteAddr, ":"); idx > -1 {
		ip = r.RemoteAddr[:idx]
		port = r.RemoteAddr[idx+1:]
	} else {
		ip = r.RemoteAddr
	}

	// Split path in preparation for env variables
	splitPos := strings.Index(fpath, rule.SplitPath)
	var docURI, scriptName, scriptFilename, pathInfo string
	if splitPos == -1 {
		// Request doesn't have the extension, so assume index file in root
		docURI = "/" + rule.IndexFiles[0]
		scriptName = "/" + rule.IndexFiles[0]
		scriptFilename = filepath.Join(h.AbsRoot, rule.IndexFiles[0])
		pathInfo = fpath
	} else {
		// Request has the extension; path was split successfully
		docURI = fpath[:splitPos+len(rule.SplitPath)]
		pathInfo = fpath[splitPos+len(rule.SplitPath):]
		scriptName = fpath
		scriptFilename = absPath
	}

	// Some variables are unused but cleared explicitly to prevent
	// the parent environment from interfering.
	env = map[string]string{

		// Variables defined in CGI 1.1 spec
		"AUTH_TYPE":         "", // Not used
		"CONTENT_LENGTH":    r.Header.Get("Content-Length"),
		"CONTENT_TYPE":      r.Header.Get("Content-Type"),
		"GATEWAY_INTERFACE": "CGI/1.1",
		"PATH_INFO":         pathInfo,
		"QUERY_STRING":      r.URL.RawQuery,
		"REMOTE_ADDR":       ip,
		"REMOTE_HOST":       ip, // For speed, remote host lookups disabled
		"REMOTE_PORT":       port,
		"REMOTE_IDENT":      "", // Not used
		"REMOTE_USER":       "", // Not used
		"REQUEST_METHOD":    r.Method,
		"SERVER_NAME":       h.ServerName,
		"SERVER_PORT":       h.ServerPort,
		"SERVER_PROTOCOL":   r.Proto,
		"SERVER_SOFTWARE":   h.SoftwareName + "/" + h.SoftwareVersion,

		// Other variables
		"DOCUMENT_ROOT":   h.AbsRoot,
		"DOCUMENT_URI":    docURI,
		"HTTP_HOST":       r.Host, // added here, since not always part of headers
		"REQUEST_URI":     r.URL.RequestURI(),
		"SCRIPT_FILENAME": scriptFilename,
		"SCRIPT_NAME":     scriptName,
	}

	// compliance with the CGI specification that PATH_TRANSLATED
	// should only exist if PATH_INFO is defined.
	// Info: https://www.ietf.org/rfc/rfc3875 Page 14
	if env["PATH_INFO"] != "" {
		env["PATH_TRANSLATED"] = filepath.Join(h.AbsRoot, pathInfo) // Info: http://www.oreilly.com/openbook/cgi/ch02_04.html
	}

	// Some web apps rely on knowing HTTPS or not
	if r.TLS != nil {
		env["HTTPS"] = "on"
	}

	// Add env variables from config
	for _, envVar := range rule.EnvVars {
		env[envVar[0]] = envVar[1]
	}

	// Add all HTTP headers to env variables
	for field, val := range r.Header {
		header := strings.ToUpper(field)
		header = headerNameReplacer.Replace(header)
		env["HTTP_"+header] = strings.Join(val, ", ")
	}

	return env, nil
}

// Rule represents a FastCGI handling rule.
type Rule struct {
	// The base path to match. Required.
	Path string

	// The address of the FastCGI server. Required.
	Address string

	// Always process files with this extension with fastcgi.
	Ext string

	// The path in the URL will be split into two, with the first piece ending
	// with the value of SplitPath. The first piece will be assumed as the
	// actual resource (CGI script) name, and the second piece will be set to
	// PATH_INFO for the CGI script to use.
	SplitPath string

	// If the URL ends with '/' (which indicates a directory), these index
	// files will be tried instead.
	IndexFiles []string

	// Environment Variables
	EnvVars [][2]string
}

var headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_")
<|endoftext|>


<commit_before>// Package fastcgi has middleware that acts as a FastCGI client. Requests
// that get forwarded to FastCGI stop the middleware execution chain.
// The most common use for this package is to serve PHP websites via php-fpm.
package fastcgi

import (
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/mholt/caddy/middleware"
)

// Handler is a middleware type that can handle requests as a FastCGI client.
type Handler struct {
	Next    middleware.Handler
	Rules   []Rule
	Root    string
	AbsRoot string // same as root, but absolute path
	FileSys http.FileSystem

	// These are sent to CGI scripts in env variables
	SoftwareName    string
	SoftwareVersion string
	ServerName      string
	ServerPort      string
}

// ServeHTTP satisfies the middleware.Handler interface.
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	for _, rule := range h.Rules {

		// First requirement: Base path must match
		if !middleware.Path(r.URL.Path).Matches(rule.Path) {
			continue
		}

		// In addition to matching the path, a request must meet some
		// other criteria before being proxied as FastCGI. For example,
		// we probably want to exclude static assets (CSS, JS, images...)
		// but we also want to be flexible for the script we proxy to.

		fpath := r.URL.Path
		if idx, ok := middleware.IndexFile(h.FileSys, fpath, rule.IndexFiles); ok {
			fpath = idx
		}

		// These criteria work well in this order for PHP sites
		if fpath[len(fpath)-1] == '/' || strings.HasSuffix(fpath, rule.Ext) || !h.exists(fpath) {

			// Create environment for CGI script
			env, err := h.buildEnv(r, rule, fpath)
			if err != nil {
				return http.StatusInternalServerError, err
			}

			// Connect to FastCGI gateway
			fcgi, err := Dial("tcp", rule.Address)
			if err != nil {
				return http.StatusBadGateway, err
			}

			var resp *http.Response
			contentLength, _ := strconv.Atoi(r.Header.Get("Content-Length"))
			switch r.Method {
			case "HEAD":
				resp, err = fcgi.Head(env)
			case "GET":
				resp, err = fcgi.Get(env)
			case "OPTIONS":
				resp, err = fcgi.Options(env)
			case "POST":
				resp, err = fcgi.Post(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			case "PUT":
				resp, err = fcgi.Put(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			case "PATCH":
				resp, err = fcgi.Patch(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			case "DELETE":
				resp, err = fcgi.Delete(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			default:
				return http.StatusMethodNotAllowed, nil
			}
			defer resp.Body.Close()

			if err != nil && err != io.EOF {
				return http.StatusBadGateway, err
			}

			// Write the response header
			for key, vals := range resp.Header {
				for _, val := range vals {
					w.Header().Add(key, val)
				}
			}
			w.WriteHeader(resp.StatusCode)

			// Write the response body
			// TODO: If this has an error, the response will already be
			// partly written. We should copy out of resp.Body into a buffer
			// first, then write it to the response...
			_, err = io.Copy(w, resp.Body)
			if err != nil {
				return http.StatusBadGateway, err
			}

			return 0, nil
		}
	}

	return h.Next.ServeHTTP(w, r)
}

func (h Handler) exists(path string) bool {
	if _, err := os.Stat(h.Root + path); err == nil {
		return true
	}
	return false
}

// buildEnv returns a set of CGI environment variables for the request.
func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string]string, error) {
	var env map[string]string

	// Get absolute path of requested resource
	absPath := filepath.Join(h.AbsRoot, fpath)

	// Separate remote IP and port; more lenient than net.SplitHostPort
	var ip, port string
	if idx := strings.Index(r.RemoteAddr, ":"); idx > -1 {
		ip = r.RemoteAddr[:idx]
		port = r.RemoteAddr[idx+1:]
	} else {
		ip = r.RemoteAddr
	}

	// Split path in preparation for env variables
	splitPos := strings.Index(fpath, rule.SplitPath)
	var docURI, scriptName, scriptFilename, pathInfo string
	if splitPos == -1 {
		// Request doesn't have the extension, so assume index file in root
		docURI = "/" + rule.IndexFiles[0]
		scriptName = "/" + rule.IndexFiles[0]
		scriptFilename = filepath.Join(h.AbsRoot, rule.IndexFiles[0])
		pathInfo = fpath
	} else {
		// Request has the extension; path was split successfully
		docURI = fpath[:splitPos+len(rule.SplitPath)]
		pathInfo = fpath[splitPos+len(rule.SplitPath):]
		scriptName = fpath
		scriptFilename = absPath
	}

	// Some variables are unused but cleared explicitly to prevent
	// the parent environment from interfering.
	env = map[string]string{

		// Variables defined in CGI 1.1 spec
		"AUTH_TYPE":         "", // Not used
		"CONTENT_LENGTH":    r.Header.Get("Content-Length"),
		"CONTENT_TYPE":      r.Header.Get("Content-Type"),
		"GATEWAY_INTERFACE": "CGI/1.1",
		"PATH_INFO":         pathInfo,
		"PATH_TRANSLATED":   filepath.Join(h.AbsRoot, pathInfo), // Info: http://www.oreilly.com/openbook/cgi/ch02_04.html
		"QUERY_STRING":      r.URL.RawQuery,
		"REMOTE_ADDR":       ip,
		"REMOTE_HOST":       ip, // For speed, remote host lookups disabled
		"REMOTE_PORT":       port,
		"REMOTE_IDENT":      "", // Not used
		"REMOTE_USER":       "", // Not used
		"REQUEST_METHOD":    r.Method,
		"SERVER_NAME":       h.ServerName,
		"SERVER_PORT":       h.ServerPort,
		"SERVER_PROTOCOL":   r.Proto,
		"SERVER_SOFTWARE":   h.SoftwareName + "/" + h.SoftwareVersion,

		// Other variables
		"DOCUMENT_ROOT":   h.AbsRoot,
		"DOCUMENT_URI":    docURI,
		"HTTP_HOST":       r.Host, // added here, since not always part of headers
		"REQUEST_URI":     r.URL.RequestURI(),
		"SCRIPT_FILENAME": scriptFilename,
		"SCRIPT_NAME":     scriptName,
	}

	// Add env variables from config
	for _, envVar := range rule.EnvVars {
		env[envVar[0]] = envVar[1]
	}

	// Add all HTTP headers to env variables
	for field, val := range r.Header {
		header := strings.ToUpper(field)
		header = headerNameReplacer.Replace(header)
		env["HTTP_"+header] = strings.Join(val, ", ")
	}

	return env, nil
}

// Rule represents a FastCGI handling rule.
type Rule struct {
	// The base path to match. Required.
	Path string

	// The address of the FastCGI server. Required.
	Address string

	// Always process files with this extension with fastcgi.
	Ext string

	// The path in the URL will be split into two, with the first piece ending
	// with the value of SplitPath. The first piece will be assumed as the
	// actual resource (CGI script) name, and the second piece will be set to
	// PATH_INFO for the CGI script to use.
	SplitPath string

	// If the URL ends with '/' (which indicates a directory), these index
	// files will be tried instead.
	IndexFiles []string

	// Environment Variables
	EnvVars [][2]string
}

var headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_")
<commit_msg>FastCGI: support for unix sockets.<commit_after>// Package fastcgi has middleware that acts as a FastCGI client. Requests
// that get forwarded to FastCGI stop the middleware execution chain.
// The most common use for this package is to serve PHP websites via php-fpm.
package fastcgi

import (
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/mholt/caddy/middleware"
)

// Handler is a middleware type that can handle requests as a FastCGI client.
type Handler struct {
	Next    middleware.Handler
	Rules   []Rule
	Root    string
	AbsRoot string // same as root, but absolute path
	FileSys http.FileSystem

	// These are sent to CGI scripts in env variables
	SoftwareName    string
	SoftwareVersion string
	ServerName      string
	ServerPort      string
}

// ServeHTTP satisfies the middleware.Handler interface.
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	for _, rule := range h.Rules {

		// First requirement: Base path must match
		if !middleware.Path(r.URL.Path).Matches(rule.Path) {
			continue
		}

		// In addition to matching the path, a request must meet some
		// other criteria before being proxied as FastCGI. For example,
		// we probably want to exclude static assets (CSS, JS, images...)
		// but we also want to be flexible for the script we proxy to.

		fpath := r.URL.Path
		if idx, ok := middleware.IndexFile(h.FileSys, fpath, rule.IndexFiles); ok {
			fpath = idx
		}

		// These criteria work well in this order for PHP sites
		if fpath[len(fpath)-1] == '/' || strings.HasSuffix(fpath, rule.Ext) || !h.exists(fpath) {

			// Create environment for CGI script
			env, err := h.buildEnv(r, rule, fpath)
			if err != nil {
				return http.StatusInternalServerError, err
			}

			// Connect to FastCGI gateway
			var fcgi *FCGIClient

			// check if unix socket or tcp
			if strings.HasPrefix(rule.Address, "/") {
				fcgi, err = Dial("unix", rule.Address)
			} else {
				fcgi, err = Dial("tcp", rule.Address)
			}
			if err != nil {
				return http.StatusBadGateway, err
			}

			var resp *http.Response
			contentLength, _ := strconv.Atoi(r.Header.Get("Content-Length"))
			switch r.Method {
			case "HEAD":
				resp, err = fcgi.Head(env)
			case "GET":
				resp, err = fcgi.Get(env)
			case "OPTIONS":
				resp, err = fcgi.Options(env)
			case "POST":
				resp, err = fcgi.Post(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			case "PUT":
				resp, err = fcgi.Put(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			case "PATCH":
				resp, err = fcgi.Patch(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			case "DELETE":
				resp, err = fcgi.Delete(env, r.Header.Get("Content-Type"), r.Body, contentLength)
			default:
				return http.StatusMethodNotAllowed, nil
			}

			if resp.Body != nil {
				defer resp.Body.Close()
			}

			if err != nil && err != io.EOF {
				return http.StatusBadGateway, err
			}

			// Write the response header
			for key, vals := range resp.Header {
				for _, val := range vals {
					w.Header().Add(key, val)
				}
			}
			w.WriteHeader(resp.StatusCode)

			// Write the response body
			// TODO: If this has an error, the response will already be
			// partly written. We should copy out of resp.Body into a buffer
			// first, then write it to the response...
			_, err = io.Copy(w, resp.Body)
			if err != nil {
				return http.StatusBadGateway, err
			}

			return 0, nil
		}
	}

	return h.Next.ServeHTTP(w, r)
}

func (h Handler) exists(path string) bool {
	if _, err := os.Stat(h.Root + path); err == nil {
		return true
	}
	return false
}

// buildEnv returns a set of CGI environment variables for the request.
func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string]string, error) {
	var env map[string]string

	// Get absolute path of requested resource
	absPath := filepath.Join(h.AbsRoot, fpath)

	// Separate remote IP and port; more lenient than net.SplitHostPort
	var ip, port string
	if idx := strings.Index(r.RemoteAddr, ":"); idx > -1 {
		ip = r.RemoteAddr[:idx]
		port = r.RemoteAddr[idx+1:]
	} else {
		ip = r.RemoteAddr
	}

	// Split path in preparation for env variables
	splitPos := strings.Index(fpath, rule.SplitPath)
	var docURI, scriptName, scriptFilename, pathInfo string
	if splitPos == -1 {
		// Request doesn't have the extension, so assume index file in root
		docURI = "/" + rule.IndexFiles[0]
		scriptName = "/" + rule.IndexFiles[0]
		scriptFilename = filepath.Join(h.AbsRoot, rule.IndexFiles[0])
		pathInfo = fpath
	} else {
		// Request has the extension; path was split successfully
		docURI = fpath[:splitPos+len(rule.SplitPath)]
		pathInfo = fpath[splitPos+len(rule.SplitPath):]
		scriptName = fpath
		scriptFilename = absPath
	}

	// Some variables are unused but cleared explicitly to prevent
	// the parent environment from interfering.
	env = map[string]string{

		// Variables defined in CGI 1.1 spec
		"AUTH_TYPE":         "", // Not used
		"CONTENT_LENGTH":    r.Header.Get("Content-Length"),
		"CONTENT_TYPE":      r.Header.Get("Content-Type"),
		"GATEWAY_INTERFACE": "CGI/1.1",
		"PATH_INFO":         pathInfo,
		"PATH_TRANSLATED":   filepath.Join(h.AbsRoot, pathInfo), // Info: http://www.oreilly.com/openbook/cgi/ch02_04.html
		"QUERY_STRING":      r.URL.RawQuery,
		"REMOTE_ADDR":       ip,
		"REMOTE_HOST":       ip, // For speed, remote host lookups disabled
		"REMOTE_PORT":       port,
		"REMOTE_IDENT":      "", // Not used
		"REMOTE_USER":       "", // Not used
		"REQUEST_METHOD":    r.Method,
		"SERVER_NAME":       h.ServerName,
		"SERVER_PORT":       h.ServerPort,
		"SERVER_PROTOCOL":   r.Proto,
		"SERVER_SOFTWARE":   h.SoftwareName + "/" + h.SoftwareVersion,

		// Other variables
		"DOCUMENT_ROOT":   h.AbsRoot,
		"DOCUMENT_URI":    docURI,
		"HTTP_HOST":       r.Host, // added here, since not always part of headers
		"REQUEST_URI":     r.URL.RequestURI(),
		"SCRIPT_FILENAME": scriptFilename,
		"SCRIPT_NAME":     scriptName,
	}

	// Add env variables from config
	for _, envVar := range rule.EnvVars {
		env[envVar[0]] = envVar[1]
	}

	// Add all HTTP headers to env variables
	for field, val := range r.Header {
		header := strings.ToUpper(field)
		header = headerNameReplacer.Replace(header)
		env["HTTP_"+header] = strings.Join(val, ", ")
	}

	return env, nil
}

// Rule represents a FastCGI handling rule.
type Rule struct {
	// The base path to match. Required.
	Path string

	// The address of the FastCGI server. Required.
	Address string

	// Always process files with this extension with fastcgi.
	Ext string

	// The path in the URL will be split into two, with the first piece ending
	// with the value of SplitPath. The first piece will be assumed as the
	// actual resource (CGI script) name, and the second piece will be set to
	// PATH_INFO for the CGI script to use.
	SplitPath string

	// If the URL ends with '/' (which indicates a directory), these index
	// files will be tried instead.
	IndexFiles []string

	// Environment Variables
	EnvVars [][2]string
}

var headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_")
<|endoftext|>


<commit_before>package balancer

import "fmt"

type Pool []*Worker

func NewPool(nWorkers int) Pool {
	return make([]*Worker, 0, nWorkers)
}

func (p Pool) getStats() []int {
	stats := make([]int, len(p))
	for i := 0; i < len(p); i++ {
		stats[i] = p[i].QueueSize()
	}
	return stats
}

func (p Pool) String() string {
	stats := p.getStats()
	return fmt.Sprintf("pool: %+v, load: {avg: %.1f, mean: %.1f, stddev: %.1f}",
		stats, avg(stats), mean(stats), stddev(stats))
}

func (p Pool) Cap() int {
	return cap(p)
}

// Implement heap.Interface
func (p Pool) Len() int           { return len(p) }
func (p Pool) Less(i, j int) bool { return p[i].QueueSize() < p[j].QueueSize() }
func (p Pool) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
	p[i].index = i
	p[j].index = j
}

func (p *Pool) Push(x interface{}) {
	// log.Println("Adding new element to the heap")
	w := x.(*Worker)
	w.index = len(*p)
	*p = append(*p, w)
	// log.Println(p)
}

func (p *Pool) Pop() interface{} {
	old := *p
	n := len(old) - 1
	x := old[n]
	x.index = -1 // for safety
	*p = old[0:n]
	return x
}
<commit_msg>No need for another copy of the slice<commit_after>package balancer

import "fmt"

type Pool []*Worker

func NewPool(nWorkers int) Pool {
	return make([]*Worker, 0, nWorkers)
}

func (p Pool) getStats() []int {
	stats := make([]int, len(p))
	for i := 0; i < len(p); i++ {
		stats[i] = p[i].QueueSize()
	}
	return stats
}

func (p Pool) String() string {
	stats := p.getStats()
	return fmt.Sprintf("pool: %+v, load: {avg: %.1f, mean: %.1f, stddev: %.1f}",
		stats, avg(stats), mean(stats), stddev(stats))
}

func (p Pool) Cap() int {
	return cap(p)
}

// Implement heap.Interface
func (p Pool) Len() int           { return len(p) }
func (p Pool) Less(i, j int) bool { return p[i].QueueSize() < p[j].QueueSize() }
func (p Pool) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
	p[i].index = i
	p[j].index = j
}

func (p *Pool) Push(x interface{}) {
	// log.Println("Adding new element to the heap")
	w := x.(*Worker)
	w.index = len(*p)
	*p = append(*p, w)
	// log.Println(p)
}

func (p *Pool) Pop() interface{} {
	n := len(*p) - 1
	x := (*p)[n]
	x.index = -1 // for safety
	*p = (*p)[0:n]
	return x
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"log"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	plugin "github.com/hashicorp/go-plugin"
	tfplugin "github.com/hashicorp/terraform/plugin"
	"github.com/hashicorp/terraform/plugin/discovery"
	"github.com/hashicorp/terraform/terraform"
	"github.com/kardianos/osext"
)

// multiVersionProviderResolver is an implementation of
// terraform.ResourceProviderResolver that matches the given version constraints
// against a set of versioned provider plugins to find the newest version of
// each that satisfies the given constraints.
type multiVersionProviderResolver struct {
	Available discovery.PluginMetaSet
}

func (r *multiVersionProviderResolver) ResolveProviders(
	reqd discovery.PluginRequirements,
) (map[string]terraform.ResourceProviderFactory, []error) {
	factories := make(map[string]terraform.ResourceProviderFactory, len(reqd))
	var errs []error

	candidates := r.Available.ConstrainVersions(reqd)
	for name := range reqd {
		if metas := candidates[name]; metas != nil {
			newest := metas.Newest()
			client := tfplugin.Client(newest)
			factories[name] = providerFactory(client)
		} else {
			errs = append(errs, fmt.Errorf("provider.%s: no suitable version installed", name))
		}
	}

	return factories, errs
}

// the default location for automatically installed plugins
func (m *Meta) pluginDir() string {
	return filepath.Join(m.DataDir(), "plugins", fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH))
}

// pluginDirs return a list of directories to search for plugins.
//
// Earlier entries in this slice get priority over later when multiple copies
// of the same plugin version are found, but newer versions always override
// older versions where both satisfy the provider version constraints.
func (m *Meta) pluginDirs() []string {

	// When searching the following directories, earlier entries get precedence
	// if the same plugin version is found twice, but newer versions will
	// always get preference below regardless of where they are coming from.
	// TODO: Add auto-install dir, default vendor dir and optional override
	// vendor dir(s).
	dirs := []string{"."}

	// Look in the same directory as the Terraform executable.
	// If found, this replaces what we found in the config path.
	exePath, err := osext.Executable()
	if err != nil {
		log.Printf("[ERROR] Error discovering exe directory: %s", err)
	} else {
		dirs = append(dirs, filepath.Dir(exePath))
	}

	dirs = append(dirs, m.pluginDir())
	dirs = append(dirs, m.GlobalPluginDirs...)
	return dirs
}

// providerPluginSet returns the set of valid providers that were discovered in
// the defined search paths.
func (m *Meta) providerPluginSet() discovery.PluginMetaSet {
	plugins := discovery.FindPlugins("provider", m.pluginDirs())
	plugins, _ = plugins.ValidateVersions()

	return plugins
}

func (m *Meta) providerResolver() terraform.ResourceProviderResolver {
	return &multiVersionProviderResolver{
		Available: m.providerPluginSet(),
	}
}

// filter the requirements returning only the providers that we can't resolve
func (m *Meta) missingProviders(reqd discovery.PluginRequirements) discovery.PluginRequirements {
	missing := make(discovery.PluginRequirements)

	candidates := m.providerPluginSet().ConstrainVersions(reqd)

	for name, versionSet := range reqd {
		if metas := candidates[name]; metas == nil {
			missing[name] = versionSet
		}
	}

	return missing
}

func (m *Meta) provisionerFactories() map[string]terraform.ResourceProvisionerFactory {
	dirs := m.pluginDirs()
	plugins := discovery.FindPlugins("provisioner", dirs)
	plugins, _ = plugins.ValidateVersions()

	// For now our goal is to just find the latest version of each plugin
	// we have on the system. All provisioners should be at version 0.0.0
	// currently, so there should actually only be one instance of each plugin
	// name here, even though the discovery interface forces us to pretend
	// that might not be true.

	factories := make(map[string]terraform.ResourceProvisionerFactory)

	// Wire up the internal provisioners first. These might be overridden
	// by discovered provisioners below.
	for name := range InternalProvisioners {
		client, err := internalPluginClient("provisioner", name)
		if err != nil {
			log.Printf("[WARN] failed to build command line for internal plugin %q: %s", name, err)
			continue
		}
		factories[name] = provisionerFactory(client)
	}

	byName := plugins.ByName()
	for name, metas := range byName {
		// Since we validated versions above and we partitioned the sets
		// by name, we're guaranteed that the metas in our set all have
		// valid versions and that there's at least one meta.
		newest := metas.Newest()
		client := tfplugin.Client(newest)
		factories[name] = provisionerFactory(client)
	}

	return factories
}

func internalPluginClient(kind, name string) (*plugin.Client, error) {
	cmdLine, err := BuildPluginCommandString(kind, name)
	if err != nil {
		return nil, err
	}

	// See the docstring for BuildPluginCommandString for why we need to do
	// this split here.
	cmdArgv := strings.Split(cmdLine, TFSPACE)

	cfg := &plugin.ClientConfig{
		Cmd:             exec.Command(cmdArgv[0], cmdArgv[1:]...),
		HandshakeConfig: tfplugin.Handshake,
		Managed:         true,
		Plugins:         tfplugin.PluginMap,
	}

	return plugin.NewClient(cfg), nil
}

func providerFactory(client *plugin.Client) terraform.ResourceProviderFactory {
	return func() (terraform.ResourceProvider, error) {
		// Request the RPC client so we can get the provider
		// so we can build the actual RPC-implemented provider.
		rpcClient, err := client.Client()
		if err != nil {
			return nil, err
		}

		raw, err := rpcClient.Dispense(tfplugin.ProviderPluginName)
		if err != nil {
			return nil, err
		}

		return raw.(terraform.ResourceProvider), nil
	}
}

func provisionerFactory(client *plugin.Client) terraform.ResourceProvisionerFactory {
	return func() (terraform.ResourceProvisioner, error) {
		// Request the RPC client so we can get the provisioner
		// so we can build the actual RPC-implemented provisioner.
		rpcClient, err := client.Client()
		if err != nil {
			return nil, err
		}

		raw, err := rpcClient.Dispense(tfplugin.ProvisionerPluginName)
		if err != nil {
			return nil, err
		}

		return raw.(terraform.ResourceProvisioner), nil
	}
}
<commit_msg>command: provider resolver to also check SHA256 constraints when set<commit_after>package command

import (
	"fmt"
	"log"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	plugin "github.com/hashicorp/go-plugin"
	tfplugin "github.com/hashicorp/terraform/plugin"
	"github.com/hashicorp/terraform/plugin/discovery"
	"github.com/hashicorp/terraform/terraform"
	"github.com/kardianos/osext"
)

// multiVersionProviderResolver is an implementation of
// terraform.ResourceProviderResolver that matches the given version constraints
// against a set of versioned provider plugins to find the newest version of
// each that satisfies the given constraints.
type multiVersionProviderResolver struct {
	Available discovery.PluginMetaSet
}

func (r *multiVersionProviderResolver) ResolveProviders(
	reqd discovery.PluginRequirements,
) (map[string]terraform.ResourceProviderFactory, []error) {
	factories := make(map[string]terraform.ResourceProviderFactory, len(reqd))
	var errs []error

	candidates := r.Available.ConstrainVersions(reqd)
	for name := range reqd {
		if metas := candidates[name]; metas != nil {
			newest := metas.Newest()

			digest, err := newest.SHA256()
			if err != nil {
				errs = append(errs, fmt.Errorf("provider.%s: failed to load plugin to verify its signature: %s", name, err))
				continue
			}
			if !reqd[name].AcceptsSHA256(digest) {
				// This generic error message is intended to avoid troubling
				// users with implementation details. The main useful point
				// here is that they need to run "terraform init" to
				// fix this, which is covered by the UI code reporting these
				// error messages.
				errs = append(errs, fmt.Errorf("provider.%s: not yet initialized", name))
				continue
			}

			client := tfplugin.Client(newest)
			factories[name] = providerFactory(client)
		} else {
			errs = append(errs, fmt.Errorf("provider.%s: no suitable version installed", name))
		}
	}

	return factories, errs
}

// the default location for automatically installed plugins
func (m *Meta) pluginDir() string {
	return filepath.Join(m.DataDir(), "plugins", fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH))
}

// pluginDirs return a list of directories to search for plugins.
//
// Earlier entries in this slice get priority over later when multiple copies
// of the same plugin version are found, but newer versions always override
// older versions where both satisfy the provider version constraints.
func (m *Meta) pluginDirs() []string {

	// When searching the following directories, earlier entries get precedence
	// if the same plugin version is found twice, but newer versions will
	// always get preference below regardless of where they are coming from.
	// TODO: Add auto-install dir, default vendor dir and optional override
	// vendor dir(s).
	dirs := []string{"."}

	// Look in the same directory as the Terraform executable.
	// If found, this replaces what we found in the config path.
	exePath, err := osext.Executable()
	if err != nil {
		log.Printf("[ERROR] Error discovering exe directory: %s", err)
	} else {
		dirs = append(dirs, filepath.Dir(exePath))
	}

	dirs = append(dirs, m.pluginDir())
	dirs = append(dirs, m.GlobalPluginDirs...)
	return dirs
}

// providerPluginSet returns the set of valid providers that were discovered in
// the defined search paths.
func (m *Meta) providerPluginSet() discovery.PluginMetaSet {
	plugins := discovery.FindPlugins("provider", m.pluginDirs())
	plugins, _ = plugins.ValidateVersions()

	return plugins
}

func (m *Meta) providerResolver() terraform.ResourceProviderResolver {
	return &multiVersionProviderResolver{
		Available: m.providerPluginSet(),
	}
}

// filter the requirements returning only the providers that we can't resolve
func (m *Meta) missingProviders(reqd discovery.PluginRequirements) discovery.PluginRequirements {
	missing := make(discovery.PluginRequirements)

	candidates := m.providerPluginSet().ConstrainVersions(reqd)

	for name, versionSet := range reqd {
		if metas := candidates[name]; metas == nil {
			missing[name] = versionSet
		}
	}

	return missing
}

func (m *Meta) provisionerFactories() map[string]terraform.ResourceProvisionerFactory {
	dirs := m.pluginDirs()
	plugins := discovery.FindPlugins("provisioner", dirs)
	plugins, _ = plugins.ValidateVersions()

	// For now our goal is to just find the latest version of each plugin
	// we have on the system. All provisioners should be at version 0.0.0
	// currently, so there should actually only be one instance of each plugin
	// name here, even though the discovery interface forces us to pretend
	// that might not be true.

	factories := make(map[string]terraform.ResourceProvisionerFactory)

	// Wire up the internal provisioners first. These might be overridden
	// by discovered provisioners below.
	for name := range InternalProvisioners {
		client, err := internalPluginClient("provisioner", name)
		if err != nil {
			log.Printf("[WARN] failed to build command line for internal plugin %q: %s", name, err)
			continue
		}
		factories[name] = provisionerFactory(client)
	}

	byName := plugins.ByName()
	for name, metas := range byName {
		// Since we validated versions above and we partitioned the sets
		// by name, we're guaranteed that the metas in our set all have
		// valid versions and that there's at least one meta.
		newest := metas.Newest()
		client := tfplugin.Client(newest)
		factories[name] = provisionerFactory(client)
	}

	return factories
}

func internalPluginClient(kind, name string) (*plugin.Client, error) {
	cmdLine, err := BuildPluginCommandString(kind, name)
	if err != nil {
		return nil, err
	}

	// See the docstring for BuildPluginCommandString for why we need to do
	// this split here.
	cmdArgv := strings.Split(cmdLine, TFSPACE)

	cfg := &plugin.ClientConfig{
		Cmd:             exec.Command(cmdArgv[0], cmdArgv[1:]...),
		HandshakeConfig: tfplugin.Handshake,
		Managed:         true,
		Plugins:         tfplugin.PluginMap,
	}

	return plugin.NewClient(cfg), nil
}

func providerFactory(client *plugin.Client) terraform.ResourceProviderFactory {
	return func() (terraform.ResourceProvider, error) {
		// Request the RPC client so we can get the provider
		// so we can build the actual RPC-implemented provider.
		rpcClient, err := client.Client()
		if err != nil {
			return nil, err
		}

		raw, err := rpcClient.Dispense(tfplugin.ProviderPluginName)
		if err != nil {
			return nil, err
		}

		return raw.(terraform.ResourceProvider), nil
	}
}

func provisionerFactory(client *plugin.Client) terraform.ResourceProvisionerFactory {
	return func() (terraform.ResourceProvisioner, error) {
		// Request the RPC client so we can get the provisioner
		// so we can build the actual RPC-implemented provisioner.
		rpcClient, err := client.Client()
		if err != nil {
			return nil, err
		}

		raw, err := rpcClient.Dispense(tfplugin.ProvisionerPluginName)
		if err != nil {
			return nil, err
		}

		return raw.(terraform.ResourceProvisioner), nil
	}
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"

	"github.com/spf13/cobra"
)

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Print the version number of Pact Go",
	Long:  `All software has versions. This is Pact Go's`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("Pact Go CLI v0.0.11")
	},
}

func init() {
	RootCmd.AddCommand(versionCmd)
}
<commit_msg>chore(version): updated to v0.0.13-beta<commit_after>package command

import (
	"fmt"

	"github.com/spf13/cobra"
)

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Print the version number of Pact Go",
	Long:  `All software has versions. This is Pact Go's`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("Pact Go CLI v0.0.13-beta")
	},
}

func init() {
	RootCmd.AddCommand(versionCmd)
}
<|endoftext|>


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

package analysis

import (
	"fmt"
	"path/filepath"
	"sort"
	"testing"

	"github.com/go-swagger/go-swagger/loads"
	"github.com/go-swagger/go-swagger/spec"
	"github.com/stretchr/testify/assert"
)

func schemeNames(schemes []SecurityRequirement) []string {
	var names []string
	for _, v := range schemes {
		names = append(names, v.Name)
	}
	sort.Sort(sort.StringSlice(names))
	return names
}

func TestAnalyzer(t *testing.T) {
	formatParam := spec.QueryParam("format").Typed("string", "")

	limitParam := spec.QueryParam("limit").Typed("integer", "int32")
	limitParam.Extensions = spec.Extensions(map[string]interface{}{})
	limitParam.Extensions.Add("go-name", "Limit")

	skipParam := spec.QueryParam("skip").Typed("integer", "int32")
	pi := spec.PathItem{}
	pi.Parameters = []spec.Parameter{*limitParam}

	op := &spec.Operation{}
	op.Consumes = []string{"application/x-yaml"}
	op.Produces = []string{"application/x-yaml"}
	op.Security = []map[string][]string{
		map[string][]string{"oauth2": []string{}},
		map[string][]string{"basic": nil},
	}
	op.ID = "someOperation"
	op.Parameters = []spec.Parameter{*skipParam}
	pi.Get = op

	pi2 := spec.PathItem{}
	pi2.Parameters = []spec.Parameter{*limitParam}
	op2 := &spec.Operation{}
	op2.ID = "anotherOperation"
	op2.Parameters = []spec.Parameter{*skipParam}
	pi2.Get = op2

	spec := &spec.Swagger{
		SwaggerProps: spec.SwaggerProps{
			Consumes: []string{"application/json"},
			Produces: []string{"application/json"},
			Security: []map[string][]string{
				map[string][]string{"apikey": nil},
			},
			SecurityDefinitions: map[string]*spec.SecurityScheme{
				"basic":  spec.BasicAuth(),
				"apiKey": spec.APIKeyAuth("api_key", "query"),
				"oauth2": spec.OAuth2AccessToken("http://authorize.com", "http://token.com"),
			},
			Parameters: map[string]spec.Parameter{"format": *formatParam},
			Paths: &spec.Paths{
				Paths: map[string]spec.PathItem{
					"/":      pi,
					"/items": pi2,
				},
			},
		},
	}
	analyzer := New(spec)

	assert.Len(t, analyzer.consumes, 2)
	assert.Len(t, analyzer.produces, 2)
	assert.Len(t, analyzer.operations, 1)
	assert.Equal(t, analyzer.operations["GET"]["/"], spec.Paths.Paths["/"].Get)

	expected := []string{"application/x-yaml"}
	sort.Sort(sort.StringSlice(expected))
	consumes := analyzer.ConsumesFor(spec.Paths.Paths["/"].Get)
	sort.Sort(sort.StringSlice(consumes))
	assert.Equal(t, expected, consumes)

	produces := analyzer.ProducesFor(spec.Paths.Paths["/"].Get)
	sort.Sort(sort.StringSlice(produces))
	assert.Equal(t, expected, produces)

	expected = []string{"application/json"}
	sort.Sort(sort.StringSlice(expected))
	consumes = analyzer.ConsumesFor(spec.Paths.Paths["/items"].Get)
	sort.Sort(sort.StringSlice(consumes))
	assert.Equal(t, expected, consumes)

	produces = analyzer.ProducesFor(spec.Paths.Paths["/items"].Get)
	sort.Sort(sort.StringSlice(produces))
	assert.Equal(t, expected, produces)

	expectedSchemes := []SecurityRequirement{SecurityRequirement{"oauth2", []string{}}, SecurityRequirement{"basic", nil}}
	schemes := analyzer.SecurityRequirementsFor(spec.Paths.Paths["/"].Get)
	assert.Equal(t, schemeNames(expectedSchemes), schemeNames(schemes))

	securityDefinitions := analyzer.SecurityDefinitionsFor(spec.Paths.Paths["/"].Get)
	assert.Equal(t, securityDefinitions["basic"], *spec.SecurityDefinitions["basic"])
	assert.Equal(t, securityDefinitions["oauth2"], *spec.SecurityDefinitions["oauth2"])

	parameters := analyzer.ParamsFor("GET", "/")
	assert.Len(t, parameters, 2)

	operations := analyzer.OperationIDs()
	assert.Len(t, operations, 2)

	producers := analyzer.RequiredProduces()
	assert.Len(t, producers, 2)
	consumers := analyzer.RequiredConsumes()
	assert.Len(t, consumers, 2)
	authSchemes := analyzer.RequiredSecuritySchemes()
	assert.Len(t, authSchemes, 3)

	ops := analyzer.Operations()
	assert.Len(t, ops, 1)
	assert.Len(t, ops["GET"], 2)

	op, ok := analyzer.OperationFor("get", "/")
	assert.True(t, ok)
	assert.NotNil(t, op)

	op, ok = analyzer.OperationFor("delete", "/")
	assert.False(t, ok)
	assert.Nil(t, op)
}

func TestDefinitionAnalysis(t *testing.T) {
	doc, err := loads.Spec(filepath.Join("..", "fixtures", "analysis", "definitions.yml"))
	if assert.NoError(t, err) {
		analyzer := New(doc.Spec())
		definitions := analyzer.allSchemas
		// parameters
		assertSchemaRefExists(t, definitions, "#/parameters/someParam/schema")
		assertSchemaRefExists(t, definitions, "#/paths/~1some~1where~1{id}/parameters/1/schema")
		assertSchemaRefExists(t, definitions, "#/paths/~1some~1where~1{id}/get/parameters/1/schema")
		// responses
		assertSchemaRefExists(t, definitions, "#/responses/someResponse/schema")
		assertSchemaRefExists(t, definitions, "#/paths/~1some~1where~1{id}/get/responses/default/schema")
		assertSchemaRefExists(t, definitions, "#/paths/~1some~1where~1{id}/get/responses/200/schema")
		// definitions
		assertSchemaRefExists(t, definitions, "#/definitions/tag")
		assertSchemaRefExists(t, definitions, "#/definitions/tag/properties/id")
		assertSchemaRefExists(t, definitions, "#/definitions/tag/properties/value")
		assertSchemaRefExists(t, definitions, "#/definitions/tag/definitions/category")
		assertSchemaRefExists(t, definitions, "#/definitions/tag/definitions/category/properties/id")
		assertSchemaRefExists(t, definitions, "#/definitions/tag/definitions/category/properties/value")
		assertSchemaRefExists(t, definitions, "#/definitions/withAdditionalProps")
		assertSchemaRefExists(t, definitions, "#/definitions/withAdditionalProps/additionalProperties")
		assertSchemaRefExists(t, definitions, "#/definitions/withAdditionalItems")
		assertSchemaRefExists(t, definitions, "#/definitions/withAdditionalItems/items/0")
		assertSchemaRefExists(t, definitions, "#/definitions/withAdditionalItems/items/1")
		assertSchemaRefExists(t, definitions, "#/definitions/withAdditionalItems/additionalItems")
		assertSchemaRefExists(t, definitions, "#/definitions/withNot")
		assertSchemaRefExists(t, definitions, "#/definitions/withNot/not")
		assertSchemaRefExists(t, definitions, "#/definitions/withAnyOf")
		assertSchemaRefExists(t, definitions, "#/definitions/withAnyOf/anyOf/0")
		assertSchemaRefExists(t, definitions, "#/definitions/withAnyOf/anyOf/1")
		assertSchemaRefExists(t, definitions, "#/definitions/withAllOf")
		assertSchemaRefExists(t, definitions, "#/definitions/withAllOf/allOf/0")
		assertSchemaRefExists(t, definitions, "#/definitions/withAllOf/allOf/1")
		allOfs := analyzer.allOfs
		assert.Len(t, allOfs, 1)
		_, hasAllOf := allOfs["#/definitions/withAllOf"]
		assert.True(t, hasAllOf)
	}
}

func TestReferenceAnalysis(t *testing.T) {
	doc, err := loads.Spec(filepath.Join("..", "fixtures", "analysis", "references.yml"))
	if assert.NoError(t, err) {
		definitions := New(doc.Spec()).references

		// parameters
		assertRefExists(t, definitions.parameters, "#/paths/~1some~1where~1{id}/parameters/0")
		assertRefExists(t, definitions.parameters, "#/paths/~1some~1where~1{id}/get/parameters/0")

		// responses
		assertRefExists(t, definitions.responses, "#/paths/~1some~1where~1{id}/get/responses/404")

		// definitions
		assertRefExists(t, definitions.schemas, "#/responses/notFound/schema")
		assertRefExists(t, definitions.schemas, "#/paths/~1some~1where~1{id}/get/responses/200/schema")
		assertRefExists(t, definitions.schemas, "#/definitions/tag/properties/audit")

		// items
		assertRefExists(t, definitions.allRefs, "#/paths/~1some~1where~1{id}/get/parameters/1/items")
	}
}

func assertRefExists(t testing.TB, data map[string]spec.Ref, key string) bool {
	if _, ok := data[key]; !ok {
		return assert.Fail(t, fmt.Sprintf("expected %q to exist in the ref bag", key))
	}
	return true
}

func assertSchemaRefExists(t testing.TB, data map[string]SchemaRef, key string) bool {
	if _, ok := data[key]; !ok {
		return assert.Fail(t, fmt.Sprintf("expected %q to exist in schema ref bag", key))
	}
	return true
}
<commit_msg>remove circular reference from analzyer test to loads<commit_after>// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package analysis

import (
	"encoding/json"
	"fmt"
	"path/filepath"
	"sort"
	"testing"

	"github.com/go-swagger/go-swagger/spec"
	"github.com/go-swagger/go-swagger/swag"
	"github.com/stretchr/testify/assert"
)

func schemeNames(schemes []SecurityRequirement) []string {
	var names []string
	for _, v := range schemes {
		names = append(names, v.Name)
	}
	sort.Sort(sort.StringSlice(names))
	return names
}

func TestAnalyzer(t *testing.T) {
	formatParam := spec.QueryParam("format").Typed("string", "")

	limitParam := spec.QueryParam("limit").Typed("integer", "int32")
	limitParam.Extensions = spec.Extensions(map[string]interface{}{})
	limitParam.Extensions.Add("go-name", "Limit")

	skipParam := spec.QueryParam("skip").Typed("integer", "int32")
	pi := spec.PathItem{}
	pi.Parameters = []spec.Parameter{*limitParam}

	op := &spec.Operation{}
	op.Consumes = []string{"application/x-yaml"}
	op.Produces = []string{"application/x-yaml"}
	op.Security = []map[string][]string{
		map[string][]string{"oauth2": []string{}},
		map[string][]string{"basic": nil},
	}
	op.ID = "someOperation"
	op.Parameters = []spec.Parameter{*skipParam}
	pi.Get = op

	pi2 := spec.PathItem{}
	pi2.Parameters = []spec.Parameter{*limitParam}
	op2 := &spec.Operation{}
	op2.ID = "anotherOperation"
	op2.Parameters = []spec.Parameter{*skipParam}
	pi2.Get = op2

	spec := &spec.Swagger{
		SwaggerProps: spec.SwaggerProps{
			Consumes: []string{"application/json"},
			Produces: []string{"application/json"},
			Security: []map[string][]string{
				map[string][]string{"apikey": nil},
			},
			SecurityDefinitions: map[string]*spec.SecurityScheme{
				"basic":  spec.BasicAuth(),
				"apiKey": spec.APIKeyAuth("api_key", "query"),
				"oauth2": spec.OAuth2AccessToken("http://authorize.com", "http://token.com"),
			},
			Parameters: map[string]spec.Parameter{"format": *formatParam},
			Paths: &spec.Paths{
				Paths: map[string]spec.PathItem{
					"/":      pi,
					"/items": pi2,
				},
			},
		},
	}
	analyzer := New(spec)

	assert.Len(t, analyzer.consumes, 2)
	assert.Len(t, analyzer.produces, 2)
	assert.Len(t, analyzer.operations, 1)
	assert.Equal(t, analyzer.operations["GET"]["/"], spec.Paths.Paths["/"].Get)

	expected := []string{"application/x-yaml"}
	sort.Sort(sort.StringSlice(expected))
	consumes := analyzer.ConsumesFor(spec.Paths.Paths["/"].Get)
	sort.Sort(sort.StringSlice(consumes))
	assert.Equal(t, expected, consumes)

	produces := analyzer.ProducesFor(spec.Paths.Paths["/"].Get)
	sort.Sort(sort.StringSlice(produces))
	assert.Equal(t, expected, produces)

	expected = []string{"application/json"}
	sort.Sort(sort.StringSlice(expected))
	consumes = analyzer.ConsumesFor(spec.Paths.Paths["/items"].Get)
	sort.Sort(sort.StringSlice(consumes))
	assert.Equal(t, expected, consumes)

	produces = analyzer.ProducesFor(spec.Paths.Paths["/items"].Get)
	sort.Sort(sort.StringSlice(produces))
	assert.Equal(t, expected, produces)

	expectedSchemes := []SecurityRequirement{SecurityRequirement{"oauth2", []string{}}, SecurityRequirement{"basic", nil}}
	schemes := analyzer.SecurityRequirementsFor(spec.Paths.Paths["/"].Get)
	assert.Equal(t, schemeNames(expectedSchemes), schemeNames(schemes))

	securityDefinitions := analyzer.SecurityDefinitionsFor(spec.Paths.Paths["/"].Get)
	assert.Equal(t, securityDefinitions["basic"], *spec.SecurityDefinitions["basic"])
	assert.Equal(t, securityDefinitions["oauth2"], *spec.SecurityDefinitions["oauth2"])

	parameters := analyzer.ParamsFor("GET", "/")
	assert.Len(t, parameters, 2)

	operations := analyzer.OperationIDs()
	assert.Len(t, operations, 2)

	producers := analyzer.RequiredProduces()
	assert.Len(t, producers, 2)
	consumers := analyzer.RequiredConsumes()
	assert.Len(t, consumers, 2)
	authSchemes := analyzer.RequiredSecuritySchemes()
	assert.Len(t, authSchemes, 3)

	ops := analyzer.Operations()
	assert.Len(t, ops, 1)
	assert.Len(t, ops["GET"], 2)

	op, ok := analyzer.OperationFor("get", "/")
	assert.True(t, ok)
	assert.NotNil(t, op)

	op, ok = analyzer.OperationFor("delete", "/")
	assert.False(t, ok)
	assert.Nil(t, op)
}

func TestDefinitionAnalysis(t *testing.T) {
	doc, err := loadSpec(filepath.Join("..", "fixtures", "analysis", "definitions.yml"))
	if assert.NoError(t, err) {
		analyzer := New(doc)
		definitions := analyzer.allSchemas
		// parameters
		assertSchemaRefExists(t, definitions, "#/parameters/someParam/schema")
		assertSchemaRefExists(t, definitions, "#/paths/~1some~1where~1{id}/parameters/1/schema")
		assertSchemaRefExists(t, definitions, "#/paths/~1some~1where~1{id}/get/parameters/1/schema")
		// responses
		assertSchemaRefExists(t, definitions, "#/responses/someResponse/schema")
		assertSchemaRefExists(t, definitions, "#/paths/~1some~1where~1{id}/get/responses/default/schema")
		assertSchemaRefExists(t, definitions, "#/paths/~1some~1where~1{id}/get/responses/200/schema")
		// definitions
		assertSchemaRefExists(t, definitions, "#/definitions/tag")
		assertSchemaRefExists(t, definitions, "#/definitions/tag/properties/id")
		assertSchemaRefExists(t, definitions, "#/definitions/tag/properties/value")
		assertSchemaRefExists(t, definitions, "#/definitions/tag/definitions/category")
		assertSchemaRefExists(t, definitions, "#/definitions/tag/definitions/category/properties/id")
		assertSchemaRefExists(t, definitions, "#/definitions/tag/definitions/category/properties/value")
		assertSchemaRefExists(t, definitions, "#/definitions/withAdditionalProps")
		assertSchemaRefExists(t, definitions, "#/definitions/withAdditionalProps/additionalProperties")
		assertSchemaRefExists(t, definitions, "#/definitions/withAdditionalItems")
		assertSchemaRefExists(t, definitions, "#/definitions/withAdditionalItems/items/0")
		assertSchemaRefExists(t, definitions, "#/definitions/withAdditionalItems/items/1")
		assertSchemaRefExists(t, definitions, "#/definitions/withAdditionalItems/additionalItems")
		assertSchemaRefExists(t, definitions, "#/definitions/withNot")
		assertSchemaRefExists(t, definitions, "#/definitions/withNot/not")
		assertSchemaRefExists(t, definitions, "#/definitions/withAnyOf")
		assertSchemaRefExists(t, definitions, "#/definitions/withAnyOf/anyOf/0")
		assertSchemaRefExists(t, definitions, "#/definitions/withAnyOf/anyOf/1")
		assertSchemaRefExists(t, definitions, "#/definitions/withAllOf")
		assertSchemaRefExists(t, definitions, "#/definitions/withAllOf/allOf/0")
		assertSchemaRefExists(t, definitions, "#/definitions/withAllOf/allOf/1")
		allOfs := analyzer.allOfs
		assert.Len(t, allOfs, 1)
		_, hasAllOf := allOfs["#/definitions/withAllOf"]
		assert.True(t, hasAllOf)
	}
}

func loadSpec(path string) (*spec.Swagger, error) {
	data, err := swag.YAMLDoc(path)
	if err != nil {
		return nil, err
	}

	var sw spec.Swagger
	if err := json.Unmarshal(data, &sw); err != nil {
		return nil, err
	}
	return &sw, nil
}

func TestReferenceAnalysis(t *testing.T) {
	doc, err := loadSpec(filepath.Join("..", "fixtures", "analysis", "references.yml"))
	if assert.NoError(t, err) {
		definitions := New(doc).references

		// parameters
		assertRefExists(t, definitions.parameters, "#/paths/~1some~1where~1{id}/parameters/0")
		assertRefExists(t, definitions.parameters, "#/paths/~1some~1where~1{id}/get/parameters/0")

		// responses
		assertRefExists(t, definitions.responses, "#/paths/~1some~1where~1{id}/get/responses/404")

		// definitions
		assertRefExists(t, definitions.schemas, "#/responses/notFound/schema")
		assertRefExists(t, definitions.schemas, "#/paths/~1some~1where~1{id}/get/responses/200/schema")
		assertRefExists(t, definitions.schemas, "#/definitions/tag/properties/audit")

		// items
		assertRefExists(t, definitions.allRefs, "#/paths/~1some~1where~1{id}/get/parameters/1/items")
	}
}

func assertRefExists(t testing.TB, data map[string]spec.Ref, key string) bool {
	if _, ok := data[key]; !ok {
		return assert.Fail(t, fmt.Sprintf("expected %q to exist in the ref bag", key))
	}
	return true
}

func assertSchemaRefExists(t testing.TB, data map[string]SchemaRef, key string) bool {
	if _, ok := data[key]; !ok {
		return assert.Fail(t, fmt.Sprintf("expected %q to exist in schema ref bag", key))
	}
	return true
}
<|endoftext|>


<commit_before>package anonuuid

import (
	"fmt"
	"testing"
)

const exampleInput string = `VOLUMES_0_SERVER_ID=15573749-c89d-41dd-a655-16e79bed52e0
VOLUMES_0_SERVER_NAME=hello
VOLUMES_0_ID=c245c3cb-3336-4567-ada1-70cb1fe4eefe
VOLUMES_0_SIZE=50000000000
ORGANIZATION=fe1e54e8-d69d-4f7c-a9f1-42069e03da31
TEST=15573749-c89d-41dd-a655-16e79bed52e0`

func ExampleAnonUUID_Sanitize() {
	anonuuid := New()
	fmt.Println(anonuuid.Sanitize(exampleInput))
	// Output:
	// VOLUMES_0_SERVER_ID=00000000-0000-0000-0000-000000000000
	// VOLUMES_0_SERVER_NAME=hello
	// VOLUMES_0_ID=11111111-1111-1111-1111-111111111111
	// VOLUMES_0_SIZE=50000000000
	// ORGANIZATION=22222222-2222-2222-2222-222222222222
	// TEST=00000000-0000-0000-0000-000000000000
}

func TestAnonUUID_Cache(t *testing.T) {
	anonuuid := New()
	if len(anonuuid.cache) != 0 {
		t.Fatalf("anonuuid.cache should be empty")
	}

	anonuuid.Sanitize("hello")
	if len(anonuuid.cache) != 0 {
		t.Fatalf("anonuuid.cache should be empty")
	}

	anonuuid.Sanitize("hello 15573749-c89d-41dd-a655-16e79bed52e0")
	if len(anonuuid.cache) != 1 {
		t.Fatalf("anonuuid.cache should contain 1 entry")
	}

	anonuuid.Sanitize("hello 15573749-c89d-41dd-a655-16e79bed52e0")
	if len(anonuuid.cache) != 1 {
		t.Fatalf("anonuuid.cache should contain 1 entry")
	}

	anonuuid.Sanitize("hello c245c3cb-3336-4567-ada1-70cb1fe4eefe")
	if len(anonuuid.cache) != 2 {
		t.Fatalf("anonuuid.cache should contain 2 entries")
	}

	anonuuid.Sanitize("hello c245c3cb-3336-4567-ada1-70cb1fe4eefe")
	if len(anonuuid.cache) != 2 {
		t.Fatalf("anonuuid.cache should contain 2 entries")
	}

	anonuuid.Sanitize("hello 15573749-c89d-41dd-a655-16e79bed52e0")
	if len(anonuuid.cache) != 2 {
		t.Fatalf("anonuuid.cache should contain 2 entries")
	}
}

func ExampleAnonUUID_FakeUUID() {
	anonuuid := New()
	fmt.Println(anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0"))
	fmt.Println(anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0"))
	fmt.Println(anonuuid.FakeUUID("c245c3cb-3336-4567-ada1-70cb1fe4eefe"))
	fmt.Println(anonuuid.FakeUUID("c245c3cb-3336-4567-ada1-70cb1fe4eefe"))
	fmt.Println(anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0"))
	fmt.Println(anonuuid.FakeUUID("fe1e54e8-d69d-4f7c-a9f1-42069e03da31"))
	// Output:
	// 00000000-0000-0000-0000-000000000000
	// 00000000-0000-0000-0000-000000000000
	// 11111111-1111-1111-1111-111111111111
	// 11111111-1111-1111-1111-111111111111
	// 00000000-0000-0000-0000-000000000000
	// 22222222-2222-2222-2222-222222222222
}

func BenchmarkNew(b *testing.B) {
	for i := 0; i < b.N; i++ {
		New()
	}
}

func BenchmarkAnonUUID_FakeUUID(b *testing.B) {
	anonuuid := New()
	for i := 0; i < b.N; i++ {
		anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0")
	}
}

func BenchmarkAnonUUID_Sanitize(b *testing.B) {
	anonuuid := New()
	for i := 0; i < b.N; i++ {
		anonuuid.Sanitize("A: 15573749-c89d-41dd-a655-16e79bed52e0, B: c245c3cb-3336-4567-ada1-70cb1fe4eefe, A: 15573749-c89d-41dd-a655-16e79bed52e0")
	}
}
<commit_msg>Fixed tests<commit_after>package anonuuid

import (
	"fmt"
	"testing"
)

const exampleInput string = `VOLUMES_0_SERVER_ID=15573749-c89d-41dd-a655-16e79bed52e0
VOLUMES_0_SERVER_NAME=hello
VOLUMES_0_ID=c245c3cb-3336-4567-ada1-70cb1fe4eefe
VOLUMES_0_SIZE=50000000000
ORGANIZATION=fe1e54e8-d69d-4f7c-a9f1-42069e03da31
TEST=15573749-c89d-41dd-a655-16e79bed52e0`

func ExampleAnonUUID_Sanitize() {
	anonuuid := New()
	fmt.Println(anonuuid.Sanitize(exampleInput))
	// Output:
	// VOLUMES_0_SERVER_ID=00000000-0000-1000-0000-000000000000
	// VOLUMES_0_SERVER_NAME=hello
	// VOLUMES_0_ID=11111111-1111-1111-1111-111111111111
	// VOLUMES_0_SIZE=50000000000
	// ORGANIZATION=22222222-2222-1222-2222-222222222222
	// TEST=00000000-0000-1000-0000-000000000000
}

func TestAnonUUID_Cache(t *testing.T) {
	anonuuid := New()
	if len(anonuuid.cache) != 0 {
		t.Fatalf("anonuuid.cache should be empty")
	}

	anonuuid.Sanitize("hello")
	if len(anonuuid.cache) != 0 {
		t.Fatalf("anonuuid.cache should be empty")
	}

	anonuuid.Sanitize("hello 15573749-c89d-41dd-a655-16e79bed52e0")
	if len(anonuuid.cache) != 1 {
		t.Fatalf("anonuuid.cache should contain 1 entry")
	}

	anonuuid.Sanitize("hello 15573749-c89d-41dd-a655-16e79bed52e0")
	if len(anonuuid.cache) != 1 {
		t.Fatalf("anonuuid.cache should contain 1 entry")
	}

	anonuuid.Sanitize("hello c245c3cb-3336-4567-ada1-70cb1fe4eefe")
	if len(anonuuid.cache) != 2 {
		t.Fatalf("anonuuid.cache should contain 2 entries")
	}

	anonuuid.Sanitize("hello c245c3cb-3336-4567-ada1-70cb1fe4eefe")
	if len(anonuuid.cache) != 2 {
		t.Fatalf("anonuuid.cache should contain 2 entries")
	}

	anonuuid.Sanitize("hello 15573749-c89d-41dd-a655-16e79bed52e0")
	if len(anonuuid.cache) != 2 {
		t.Fatalf("anonuuid.cache should contain 2 entries")
	}
}

func ExampleAnonUUID_FakeUUID() {
	anonuuid := New()
	fmt.Println(anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0"))
	fmt.Println(anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0"))
	fmt.Println(anonuuid.FakeUUID("c245c3cb-3336-4567-ada1-70cb1fe4eefe"))
	fmt.Println(anonuuid.FakeUUID("c245c3cb-3336-4567-ada1-70cb1fe4eefe"))
	fmt.Println(anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0"))
	fmt.Println(anonuuid.FakeUUID("fe1e54e8-d69d-4f7c-a9f1-42069e03da31"))
	// Output:
	// 00000000-0000-1000-0000-000000000000
	// 00000000-0000-1000-0000-000000000000
	// 11111111-1111-1111-1111-111111111111
	// 11111111-1111-1111-1111-111111111111
	// 00000000-0000-1000-0000-000000000000
	// 22222222-2222-1222-2222-222222222222
}

func BenchmarkNew(b *testing.B) {
	for i := 0; i < b.N; i++ {
		New()
	}
}

func BenchmarkAnonUUID_FakeUUID(b *testing.B) {
	anonuuid := New()
	for i := 0; i < b.N; i++ {
		anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0")
	}
}

func BenchmarkAnonUUID_Sanitize(b *testing.B) {
	anonuuid := New()
	for i := 0; i < b.N; i++ {
		anonuuid.Sanitize("A: 15573749-c89d-41dd-a655-16e79bed52e0, B: c245c3cb-3336-4567-ada1-70cb1fe4eefe, A: 15573749-c89d-41dd-a655-16e79bed52e0")
	}
}
<|endoftext|>