<commit_before>package integration

import (
	"os"

	. "code.cloudfoundry.org/cli-acceptance-tests/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("unbind-service command", func() {
	var (
		org             string
		space           string
		service         string
		servicePlan     string
		serviceInstance string
		appName         string
		broker          ServiceBroker
	)

	BeforeEach(func() {
		org = PrefixedRandomName("ORG")
		space = PrefixedRandomName("SPACE")
		service = PrefixedRandomName("SERVICE")
		servicePlan = PrefixedRandomName("SERVICE-PLAN")
		serviceInstance = "service-instance"
		appName = PrefixedRandomName("app")

		setupCF(org, space)
	})

	AfterEach(func() {
		Eventually(CF("delete-org", "-f", org), CFLongTimeout).Should(Exit(0))
	})

	Context("when the environment is not setup correctly", func() {
		Context("when no API endpoint is set", func() {
			BeforeEach(func() {
				unsetAPI()
			})

			AfterEach(func() {
				setAPI()
				loginCF()
			})

			It("fails with no API endpoint set message", func() {
				Eventually(CF("unbind-service", "some-app", "some-service")).Should(SatisfyAll(
					Exit(1),
					Say("FAILED\nNo API endpoint set. Use 'cf login' or 'cf api' to target an endpoint.")),
				)
			})
		})

		Context("when not logged in", func() {
			BeforeEach(func() {
				logoutCF()
			})

			AfterEach(func() {
				loginCF()
			})

			It("fails with not logged in message", func() {
				Eventually(CF("unbind-service", "some-app", "some-service")).Should(SatisfyAll(
					Exit(1),
					Say("FAILED\nNot logged in. Use 'cf login' to log in.")),
				)
			})
		})

		Context("when there no space set", func() {
			BeforeEach(func() {
				logoutCF()
				loginCF()
			})

			It("fails with no targeted space error message", func() {
				Eventually(CF("unbind-service", "some-app", "some-service")).Should(SatisfyAll(
					Exit(1),
					Say("FAILED\nServer error, status code: 404, error code: 40004, message: The app space could not be found: apps")),
				)
			})
		})
	})

	Context("when the environment is setup correctly", func() {
		BeforeEach(func() {
			broker = NewServiceBroker(PrefixedRandomName("SERVICE-BROKER"), NewAssets().ServiceBroker, "bosh-lite.com", service, servicePlan)
			broker.Push()
			broker.Configure()
			broker.Create()

			Eventually(CF("enable-service-access", service)).Should(Exit(0))
		})

		AfterEach(func() {
			broker.Destroy()
		})

		Context("when the service is bound to an app", func() {
			BeforeEach(func() {
				Eventually(CF("create-service", service, servicePlan, serviceInstance)).Should(Exit(0))
				Eventually(CF("push", appName, "--no-start", "-p", os.TempDir()), CFLongTimeout).Should(Exit(0))
				Eventually(CF("bind-service", appName, serviceInstance)).Should(Exit(0))
			})

			It("unbinds the service", func() {
				Eventually(CF("services")).Should(SatisfyAll(
					Exit(0),
					Say("%s.*%s", serviceInstance, appName)),
				)
				Eventually(CF("unbind-service", appName, serviceInstance), CFLongTimeout).Should(Exit(0))
				Eventually(CF("services")).Should(SatisfyAll(
					Exit(0),
					Not(Say("%s.*%s", serviceInstance, appName)),
				))
			})
		})

		Context("when the service is not bound to an app", func() {
			BeforeEach(func() {
				Eventually(CF("create-service", service, servicePlan, serviceInstance)).Should(Exit(0))
				Eventually(CF("push", appName, "--no-start", "-p", os.TempDir()), CFLongTimeout).Should(Exit(0))
			})

			It("fails to unbind the service", func() {
				Eventually(CF("unbind-service", appName, serviceInstance), CFLongTimeout).Should(SatisfyAll(
					Exit(0),
					Say("Binding between service-instance and %s did not exist", appName),
				))
			})
		})

		Context("when the service does not exist", func() {
			BeforeEach(func() {
				Eventually(CF("push", appName, "--no-start", "-p", os.TempDir()), CFLongTimeout).Should(Exit(0))
			})

			It("fails to unbind the service", func() {
				Eventually(CF("unbind-service", appName, serviceInstance), CFLongTimeout).Should(SatisfyAll(
					Exit(1),
					Say("Service instance %s not found", serviceInstance),
				))
			})
		})

		Context("when the app does not exist", func() {
			BeforeEach(func() {
				Eventually(CF("create-service", service, servicePlan, serviceInstance)).Should(Exit(0))
			})

			It("fails to unbind the service", func() {
				Eventually(CF("unbind-service", appName, serviceInstance), CFLongTimeout).Should(SatisfyAll(
					Exit(1),
					Say("App %s not found", appName),
				))
			})
		})
	})
})
<commit_msg>Skipping until rewrite has completed<commit_after>package integration

import (
	"os"

	. "code.cloudfoundry.org/cli-acceptance-tests/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("unbind-service command", func() {
	var (
		org             string
		space           string
		service         string
		servicePlan     string
		serviceInstance string
		appName         string
		broker          ServiceBroker
	)

	BeforeEach(func() {
		Skip("until #129631341")
		org = PrefixedRandomName("ORG")
		space = PrefixedRandomName("SPACE")
		service = PrefixedRandomName("SERVICE")
		servicePlan = PrefixedRandomName("SERVICE-PLAN")
		serviceInstance = "service-instance"
		appName = PrefixedRandomName("app")

		setupCF(org, space)
	})

	AfterEach(func() {
		Eventually(CF("delete-org", "-f", org), CFLongTimeout).Should(Exit(0))
	})

	Context("when the environment is not setup correctly", func() {
		Context("when no API endpoint is set", func() {
			BeforeEach(func() {
				unsetAPI()
			})

			AfterEach(func() {
				setAPI()
				loginCF()
			})

			It("fails with no API endpoint set message", func() {
				Eventually(CF("unbind-service", "some-app", "some-service")).Should(SatisfyAll(
					Exit(1),
					Say("FAILED\nNo API endpoint set. Use 'cf login' or 'cf api' to target an endpoint.")),
				)
			})
		})

		Context("when not logged in", func() {
			BeforeEach(func() {
				logoutCF()
			})

			AfterEach(func() {
				loginCF()
			})

			It("fails with not logged in message", func() {
				Eventually(CF("unbind-service", "some-app", "some-service")).Should(SatisfyAll(
					Exit(1),
					Say("FAILED\nNot logged in. Use 'cf login' to log in.")),
				)
			})
		})

		Context("when there no space set", func() {
			BeforeEach(func() {
				logoutCF()
				loginCF()
			})

			It("fails with no targeted space error message", func() {
				Eventually(CF("unbind-service", "some-app", "some-service")).Should(SatisfyAll(
					Exit(1),
					Say("FAILED\nServer error, status code: 404, error code: 40004, message: The app space could not be found: apps")),
				)
			})
		})
	})

	Context("when the environment is setup correctly", func() {
		BeforeEach(func() {
			broker = NewServiceBroker(PrefixedRandomName("SERVICE-BROKER"), NewAssets().ServiceBroker, "bosh-lite.com", service, servicePlan)
			broker.Push()
			broker.Configure()
			broker.Create()

			Eventually(CF("enable-service-access", service)).Should(Exit(0))
		})

		AfterEach(func() {
			broker.Destroy()
		})

		Context("when the service is bound to an app", func() {
			BeforeEach(func() {
				Eventually(CF("create-service", service, servicePlan, serviceInstance)).Should(Exit(0))
				Eventually(CF("push", appName, "--no-start", "-p", os.TempDir()), CFLongTimeout).Should(Exit(0))
				Eventually(CF("bind-service", appName, serviceInstance)).Should(Exit(0))
			})

			It("unbinds the service", func() {
				Eventually(CF("services")).Should(SatisfyAll(
					Exit(0),
					Say("%s.*%s", serviceInstance, appName)),
				)
				Eventually(CF("unbind-service", appName, serviceInstance), CFLongTimeout).Should(Exit(0))
				Eventually(CF("services")).Should(SatisfyAll(
					Exit(0),
					Not(Say("%s.*%s", serviceInstance, appName)),
				))
			})
		})

		Context("when the service is not bound to an app", func() {
			BeforeEach(func() {
				Eventually(CF("create-service", service, servicePlan, serviceInstance)).Should(Exit(0))
				Eventually(CF("push", appName, "--no-start", "-p", os.TempDir()), CFLongTimeout).Should(Exit(0))
			})

			It("fails to unbind the service", func() {
				Eventually(CF("unbind-service", appName, serviceInstance), CFLongTimeout).Should(SatisfyAll(
					Exit(0),
					Say("Binding between service-instance and %s did not exist", appName),
				))
			})
		})

		Context("when the service does not exist", func() {
			BeforeEach(func() {
				Eventually(CF("push", appName, "--no-start", "-p", os.TempDir()), CFLongTimeout).Should(Exit(0))
			})

			It("fails to unbind the service", func() {
				Eventually(CF("unbind-service", appName, serviceInstance), CFLongTimeout).Should(SatisfyAll(
					Exit(1),
					Say("Service instance %s not found", serviceInstance),
				))
			})
		})

		Context("when the app does not exist", func() {
			BeforeEach(func() {
				Eventually(CF("create-service", service, servicePlan, serviceInstance)).Should(Exit(0))
			})

			It("fails to unbind the service", func() {
				Eventually(CF("unbind-service", appName, serviceInstance), CFLongTimeout).Should(SatisfyAll(
					Exit(1),
					Say("App %s not found", appName),
				))
			})
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      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 generator

import (
	"strings"
	"testing"
)

var allowedReleaseLevels = map[string]bool{
	"alpha": true,
	"beta":  true,
	"ga":    true,
}

var apivExceptions = map[string]bool{
	"cloud.google.com/go/longrunning/autogen":   true,
	"cloud.google.com/go/firestore/apiv1/admin": true,
	"cloud.google.com/go/cloudbuild/apiv1/v2":   true,
	"cloud.google.com/go/monitoring/apiv3/v2":   true,
}

// TestMicrogenConfigs validates config entries.
// We expect entries here to have reasonable production settings, whereas
// the low level tooling is more permissive in the face of ambiguity.
//
// TODO: we should be able to do more substantial validation of config entries
// given sufficient setup.  Consider fetching https://github.com/googleapis/googleapis
// to ensure referenced entries are present.
func TestMicrogenConfigs(t *testing.T) {
	for k, entry := range microgenGapicConfigs {
		if entry.importPath == "" {
			t.Errorf("config %q (#%d) expected non-empty importPath", entry.inputDirectoryPath, k)
		}
		importParts := strings.Split(entry.importPath, "/")
		v := importParts[len(importParts)-1]
		if !strings.HasPrefix(v, "apiv") && !apivExceptions[entry.importPath] {
			t.Errorf("config %q (#%d) expected the last part of import path %q to start with \"apiv\"", entry.inputDirectoryPath, k, entry.importPath)
		}
		if entry.inputDirectoryPath == "" {
			t.Errorf("config %q (#%d) expected non-empty inputDirectoryPath field", entry.importPath, k)
		}
		if entry.pkg == "" {
			t.Errorf("config %q (#%d) expected non-empty pkg field", entry.importPath, k)
		}
		if entry.apiServiceConfigPath == "" {
			t.Errorf("config %q (#%d) expected non-empty apiServiceConfigPath", entry.importPath, k)
		}
		if p := entry.apiServiceConfigPath; p != "" && strings.HasSuffix(p, "gapic.yaml") {
			t.Errorf("config %q (#%d) should not use GAPIC yaml for apiServiceConfigPath", entry.importPath, k)
		}
		// Internally, an empty release level means "ga" to the underlying tool, but we
		// want to be explicit in this configuration.
		if entry.releaseLevel == "" {
			t.Errorf("config %q (#%d) expected non-empty releaseLevel field", entry.importPath, k)
		} else if !allowedReleaseLevels[entry.releaseLevel] {
			t.Errorf("config %q (#%d) invalid releaseLevel: %q", entry.importPath, k, entry.releaseLevel)
		}
	}
}
<commit_msg>test(internal): add test for gapic pkg path (#3590)<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      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 generator

import (
	"strings"
	"testing"
)

var allowedReleaseLevels = map[string]bool{
	"alpha": true,
	"beta":  true,
	"ga":    true,
}

var apivExceptions = map[string]bool{
	"cloud.google.com/go/longrunning/autogen":   true,
	"cloud.google.com/go/firestore/apiv1/admin": true,
	"cloud.google.com/go/cloudbuild/apiv1/v2":   true,
	"cloud.google.com/go/monitoring/apiv3/v2":   true,
}

var packagePathExceptions = map[string]bool{
	"cloud.google.com/go/longrunning/autogen":   true,
	"cloud.google.com/go/firestore/apiv1/admin": true,
}

// TestMicrogenConfigs validates config entries.
// We expect entries here to have reasonable production settings, whereas
// the low level tooling is more permissive in the face of ambiguity.
//
// TODO: we should be able to do more substantial validation of config entries
// given sufficient setup.  Consider fetching https://github.com/googleapis/googleapis
// to ensure referenced entries are present.
func TestMicrogenConfigs(t *testing.T) {
	for k, entry := range microgenGapicConfigs {
		if entry.importPath == "" {
			t.Errorf("config %q (#%d) expected non-empty importPath", entry.inputDirectoryPath, k)
		}
		importParts := strings.Split(entry.importPath, "/")
		v := importParts[len(importParts)-1]
		if !strings.HasPrefix(v, "apiv") && !apivExceptions[entry.importPath] {
			t.Errorf("config %q (#%d) expected the last part of import path %q to start with \"apiv\"", entry.inputDirectoryPath, k, entry.importPath)
		}
		if want := entry.pkg + "/apiv"; !strings.Contains(entry.importPath, want) && !packagePathExceptions[entry.importPath] {
			t.Errorf("config %q (#%d) want import path to contain %q", entry.importPath, k, want)
		}
		if entry.inputDirectoryPath == "" {
			t.Errorf("config %q (#%d) expected non-empty inputDirectoryPath field", entry.importPath, k)
		}
		if entry.pkg == "" {
			t.Errorf("config %q (#%d) expected non-empty pkg field", entry.importPath, k)
		}
		if entry.apiServiceConfigPath == "" {
			t.Errorf("config %q (#%d) expected non-empty apiServiceConfigPath", entry.importPath, k)
		}
		if p := entry.apiServiceConfigPath; p != "" && strings.HasSuffix(p, "gapic.yaml") {
			t.Errorf("config %q (#%d) should not use GAPIC yaml for apiServiceConfigPath", entry.importPath, k)
		}
		// Internally, an empty release level means "ga" to the underlying tool, but we
		// want to be explicit in this configuration.
		if entry.releaseLevel == "" {
			t.Errorf("config %q (#%d) expected non-empty releaseLevel field", entry.importPath, k)
		} else if !allowedReleaseLevels[entry.releaseLevel] {
			t.Errorf("config %q (#%d) invalid releaseLevel: %q", entry.importPath, k, entry.releaseLevel)
		}
	}
}
<|endoftext|>


<commit_before>package metric

import (
	"time"

	"github.com/bigdatadev/goryman"
	"github.com/pivotal-golang/lager"
)

type Event interface {
	Emit(lager.Logger)
}

type SchedulingFullDuration struct {
	PipelineName string
	Duration     time.Duration
}

func (event SchedulingFullDuration) Emit(logger lager.Logger) {
	state := "ok"
	if event.Duration > time.Second {
		state = "warning"
	} else if event.Duration > 5*time.Second {
		state = "critical"
	}

	emit(eventEmission{
		event: goryman.Event{
			Service: "scheduling: full duration (ms)",
			Metric:  ms(event.Duration),
			State:   state,
			Tags:    []string{"pipeline:" + event.PipelineName},
		},

		logger: logger.Session("full-scheduling-duration", lager.Data{
			"pipeline": event.PipelineName,
			"duration": event.Duration.String(),
		}),
	})
}

type SchedulingLoadVersionsDuration struct {
	PipelineName string
	Duration     time.Duration
}

func (event SchedulingLoadVersionsDuration) Emit(logger lager.Logger) {
	state := "ok"
	if event.Duration > time.Second {
		state = "warning"
	} else if event.Duration > 5*time.Second {
		state = "critical"
	}

	emit(eventEmission{
		event: goryman.Event{
			Service: "scheduling: loading versions duration (ms)",
			Metric:  ms(event.Duration),
			State:   state,
			Tags:    []string{"pipeline:" + event.PipelineName},
		},

		logger: logger.Session("loading-versions-duration", lager.Data{
			"pipeline": event.PipelineName,
			"duration": event.Duration.String(),
		}),
	})
}

type SchedulingJobDuration struct {
	PipelineName string
	JobName      string
	Duration     time.Duration
}

func (event SchedulingJobDuration) Emit(logger lager.Logger) {
	state := "ok"
	if event.Duration > time.Second {
		state = "warning"
	} else if event.Duration > 5*time.Second {
		state = "critical"
	}

	emit(eventEmission{
		event: goryman.Event{
			Service: "scheduling: job duration (ms)",
			Metric:  ms(event.Duration),
			State:   state,
			Tags:    []string{"pipeline:" + event.PipelineName, "job:" + event.JobName},
		},

		logger: logger.Session("job-scheduling-duration", lager.Data{
			"pipeline": event.PipelineName,
			"job":      event.JobName,
			"duration": event.Duration.String(),
		}),
	})
}

type WorkerContainers struct {
	WorkerAddr string
	Containers int
}

func (event WorkerContainers) Emit(logger lager.Logger) {
	emit(eventEmission{
		event: goryman.Event{
			Service: "scheduling: job duration (ms)",
			Metric:  event.Containers,
			State:   "ok",
			Tags:    []string{"worker:" + event.WorkerAddr},
		},

		logger: logger.Session("worker-containers", lager.Data{
			"worker":     event.WorkerAddr,
			"containers": event.Containers,
		}),
	})
}

func ms(duration time.Duration) float64 {
	return float64(duration) / 1000000
}
<commit_msg>derp, fix worker containers metric name<commit_after>package metric

import (
	"time"

	"github.com/bigdatadev/goryman"
	"github.com/pivotal-golang/lager"
)

type Event interface {
	Emit(lager.Logger)
}

type SchedulingFullDuration struct {
	PipelineName string
	Duration     time.Duration
}

func (event SchedulingFullDuration) Emit(logger lager.Logger) {
	state := "ok"
	if event.Duration > time.Second {
		state = "warning"
	} else if event.Duration > 5*time.Second {
		state = "critical"
	}

	emit(eventEmission{
		event: goryman.Event{
			Service: "scheduling: full duration (ms)",
			Metric:  ms(event.Duration),
			State:   state,
			Tags:    []string{"pipeline:" + event.PipelineName},
		},

		logger: logger.Session("full-scheduling-duration", lager.Data{
			"pipeline": event.PipelineName,
			"duration": event.Duration.String(),
		}),
	})
}

type SchedulingLoadVersionsDuration struct {
	PipelineName string
	Duration     time.Duration
}

func (event SchedulingLoadVersionsDuration) Emit(logger lager.Logger) {
	state := "ok"
	if event.Duration > time.Second {
		state = "warning"
	} else if event.Duration > 5*time.Second {
		state = "critical"
	}

	emit(eventEmission{
		event: goryman.Event{
			Service: "scheduling: loading versions duration (ms)",
			Metric:  ms(event.Duration),
			State:   state,
			Tags:    []string{"pipeline:" + event.PipelineName},
		},

		logger: logger.Session("loading-versions-duration", lager.Data{
			"pipeline": event.PipelineName,
			"duration": event.Duration.String(),
		}),
	})
}

type SchedulingJobDuration struct {
	PipelineName string
	JobName      string
	Duration     time.Duration
}

func (event SchedulingJobDuration) Emit(logger lager.Logger) {
	state := "ok"
	if event.Duration > time.Second {
		state = "warning"
	} else if event.Duration > 5*time.Second {
		state = "critical"
	}

	emit(eventEmission{
		event: goryman.Event{
			Service: "scheduling: job duration (ms)",
			Metric:  ms(event.Duration),
			State:   state,
			Tags:    []string{"pipeline:" + event.PipelineName, "job:" + event.JobName},
		},

		logger: logger.Session("job-scheduling-duration", lager.Data{
			"pipeline": event.PipelineName,
			"job":      event.JobName,
			"duration": event.Duration.String(),
		}),
	})
}

type WorkerContainers struct {
	WorkerAddr string
	Containers int
}

func (event WorkerContainers) Emit(logger lager.Logger) {
	emit(eventEmission{
		event: goryman.Event{
			Service: "worker containers",
			Metric:  event.Containers,
			State:   "ok",
			Tags:    []string{"worker:" + event.WorkerAddr},
		},

		logger: logger.Session("worker-containers", lager.Data{
			"worker":     event.WorkerAddr,
			"containers": event.Containers,
		}),
	})
}

func ms(duration time.Duration) float64 {
	return float64(duration) / 1000000
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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 ebiten

import (
	"github.com/hajimehoshi/ebiten/internal/ui"
)

// A GamepadButton represents a gamepad button.
type GamepadButton int

// GamepadButtons
const (
	GamepadButton0  = GamepadButton(ui.GamepadButton0)
	GamepadButton1  = GamepadButton(ui.GamepadButton1)
	GamepadButton2  = GamepadButton(ui.GamepadButton2)
	GamepadButton3  = GamepadButton(ui.GamepadButton3)
	GamepadButton4  = GamepadButton(ui.GamepadButton4)
	GamepadButton5  = GamepadButton(ui.GamepadButton5)
	GamepadButton6  = GamepadButton(ui.GamepadButton6)
	GamepadButton7  = GamepadButton(ui.GamepadButton7)
	GamepadButton8  = GamepadButton(ui.GamepadButton8)
	GamepadButton9  = GamepadButton(ui.GamepadButton9)
	GamepadButton10 = GamepadButton(ui.GamepadButton10)
	GamepadButton11 = GamepadButton(ui.GamepadButton11)
	GamepadButton12 = GamepadButton(ui.GamepadButton12)
	GamepadButton13 = GamepadButton(ui.GamepadButton13)
	GamepadButton14 = GamepadButton(ui.GamepadButton14)
	GamepadButton15 = GamepadButton(ui.GamepadButton15)
)
<commit_msg>input: Bug fix: GamepadButtonN must be GamepadButton type<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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 ebiten

import (
	"github.com/hajimehoshi/ebiten/internal/ui"
)

// A GamepadButton represents a gamepad button.
type GamepadButton int

// GamepadButtons
const (
	GamepadButton0  GamepadButton = GamepadButton(ui.GamepadButton0)
	GamepadButton1                = GamepadButton(ui.GamepadButton1)
	GamepadButton2                = GamepadButton(ui.GamepadButton2)
	GamepadButton3                = GamepadButton(ui.GamepadButton3)
	GamepadButton4                = GamepadButton(ui.GamepadButton4)
	GamepadButton5                = GamepadButton(ui.GamepadButton5)
	GamepadButton6                = GamepadButton(ui.GamepadButton6)
	GamepadButton7                = GamepadButton(ui.GamepadButton7)
	GamepadButton8                = GamepadButton(ui.GamepadButton8)
	GamepadButton9                = GamepadButton(ui.GamepadButton9)
	GamepadButton10               = GamepadButton(ui.GamepadButton10)
	GamepadButton11               = GamepadButton(ui.GamepadButton11)
	GamepadButton12               = GamepadButton(ui.GamepadButton12)
	GamepadButton13               = GamepadButton(ui.GamepadButton13)
	GamepadButton14               = GamepadButton(ui.GamepadButton14)
	GamepadButton15               = GamepadButton(ui.GamepadButton15)
)
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strings"
	"sync"

	docker "github.com/fsouza/go-dockerclient"
)

type Gateway struct {
	Client        *docker.Client
	DefaultDomain string
	SkipNoDomain  bool
	Destinations  map[string]DestinationMap
	*sync.Mutex
}

func NewGateway(client *docker.Client) *Gateway {
	return &Gateway{
		Client:        client,
		DefaultDomain: os.Getenv("GW_DOMAIN"),
		SkipNoDomain:  os.Getenv("GW_SKIP_NO_DOMAIN") != "",
		Destinations:  map[string]DestinationMap{},
		Mutex:         &sync.Mutex{},
	}
}

func (gw *Gateway) fetchDomain(c *docker.Container) string {
	for _, v := range c.Config.Env {
		chunks := strings.Split(v, "=")

		if chunks[0] == "DOMAIN" && len(chunks) > 1 {
			return chunks[1]
		}
	}

	if gw.SkipNoDomain {
		return ""
	}

	return fmt.Sprintf("%s.%s", c.ID[0:12], gw.DefaultDomain)
}

func (gw *Gateway) Add(container *docker.Container) error {
	log.Println("Adding container:", container.ID)

	key := gw.fetchDomain(container)

	if key == "" {
		log.Println("Skipped adding container", container.ID)
		return nil
	}

	if gw.Destinations[key][container.ID] != nil {
		return fmt.Errorf("Destination alreaady exists!")
	}

	if len(container.Config.ExposedPorts) == 0 {
		log.Printf("Container %s does not have any exposed ports\n", container.ID)
		return nil
	}

	dest, err := NewDestination(container)
	if err != nil {
		return err
	}

	gw.Lock()
	defer gw.Unlock()

	if gw.Destinations[key] == nil {
		gw.Destinations[key] = DestinationMap{}
	}

	gw.Destinations[key][container.ID] = dest
	return nil
}

func (gw *Gateway) Remove(container *docker.Container) error {
	log.Println("Removing container:", container.ID)
	key := gw.fetchDomain(container)

	if len(gw.Destinations[key]) == 0 {
		return nil
	}

	gw.Lock()
	defer gw.Unlock()

	delete(gw.Destinations[key], container.ID)
	return nil
}

func (gw *Gateway) Find(reqHost string) *Destination {
	host := strings.ToLower(strings.Split(reqHost, ":")[0])

	if len(gw.Destinations[host]) == 0 {
		return nil
	}

	list := []*Destination{}
	for _, dst := range gw.Destinations[host] {
		list = append(list, dst)
	}

	return list[rand.Intn(len(list))]
}

func (gw *Gateway) Handle(w http.ResponseWriter, r *http.Request) {
	destination := gw.Find(r.Host)

	log.Printf("Request method=%s host=%s path=%s -> %s\n", r.Method, r.Host, r.RequestURI, destination)

	if destination == nil {
		http.Error(w, "Cant find any routes for this host", http.StatusBadGateway)
		return
	}

	destination.proxy.ServeHTTP(w, r)
}

func (gw *Gateway) RenderDestinations(w http.ResponseWriter, r *http.Request) {
	result := map[string][]string{}

	for k, dstMap := range gw.Destinations {
		for _, dst := range dstMap {
			result[k] = append(result[k], dst.String())
		}
	}

	data, _ := json.Marshal(result)
	fmt.Fprintf(w, "%s", data)
}

func (gw *Gateway) RenderLogs(w http.ResponseWriter, r *http.Request) {
	dest := gw.Find(r.Host)

	if dest == nil {
		fmt.Fprintln(w, "Cant find any routes for this host")
		return
	}

	// Determine how many lines of logs we need to fetch
	lines := r.URL.Query().Get("lines")
	if lines == "" {
		lines = "3000"
	}

	buff := bytes.NewBuffer([]byte{})

	err := gw.Client.Logs(docker.LogsOptions{
		Container:    dest.containerId,
		Stdout:       true,
		Stderr:       true,
		OutputStream: buff,
		ErrorStream:  buff,
		RawTerminal:  true,
		Tail:         lines,
	})

	if err != nil {
		fmt.Fprintln(w, "Error while fetching logs:", err)
		return
	}

	r.Header.Set("Content-Type", "text/plain")
	fmt.Fprintf(w, "%s", buff.String())
}

func (gw *Gateway) RenderEnvironment(w http.ResponseWriter, r *http.Request) {
	dest := gw.Find(r.Host)

	if dest == nil {
		fmt.Fprintln(w, "Cant find any routes for this host")
		return
	}

	container, err := gw.Client.InspectContainer(dest.containerId)
	if err != nil {
		fmt.Fprintln(w, "Error while inspecting container:", err)
		return
	}

	env := strings.Join(container.Config.Env, "\n")
	r.Header.Set("Content-Type", "text/plain")
	fmt.Fprintf(w, "%s", env)
}

func (gw *Gateway) Start(bind string) error {
	log.Printf("Starting gateway server on http://%s\n", bind)

	http.HandleFunc("/_routes", gw.RenderDestinations)
	http.HandleFunc("/_destinations", gw.RenderDestinations)
	http.HandleFunc("/_logs", gw.RenderLogs)
	http.HandleFunc("/_env", gw.RenderEnvironment)

	http.HandleFunc("/", gw.Handle)
	return http.ListenAndServe(bind, nil)
}
<commit_msg>Include list of all available urls when requested url cant be routed<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strings"
	"sync"

	docker "github.com/fsouza/go-dockerclient"
)

type Gateway struct {
	Client        *docker.Client
	DefaultDomain string
	SkipNoDomain  bool
	Destinations  map[string]DestinationMap
	*sync.Mutex
}

func NewGateway(client *docker.Client) *Gateway {
	return &Gateway{
		Client:        client,
		DefaultDomain: os.Getenv("GW_DOMAIN"),
		SkipNoDomain:  os.Getenv("GW_SKIP_NO_DOMAIN") != "",
		Destinations:  map[string]DestinationMap{},
		Mutex:         &sync.Mutex{},
	}
}

func (gw *Gateway) fetchDomain(c *docker.Container) string {
	for _, v := range c.Config.Env {
		chunks := strings.Split(v, "=")

		if chunks[0] == "DOMAIN" && len(chunks) > 1 {
			return chunks[1]
		}
	}

	if gw.SkipNoDomain {
		return ""
	}

	return fmt.Sprintf("%s.%s", c.ID[0:12], gw.DefaultDomain)
}

func (gw *Gateway) notFound(w http.ResponseWriter, r *http.Request) {
	routes := []string{}

	for host, _ := range gw.Destinations {
		routes = append(routes, fmt.Sprintf("- http://%s", host))
	}

	msg := []string{
		"Cant find any routes for this host!\n",
		"Check available URLs:",
		strings.Join(routes, "\n"),
	}

	http.Error(w, strings.Join(msg, "\n"), http.StatusBadGateway)
}

func (gw *Gateway) Add(container *docker.Container) error {
	log.Println("Adding container:", container.ID)

	key := gw.fetchDomain(container)

	if key == "" {
		log.Println("Skipped adding container", container.ID)
		return nil
	}

	if gw.Destinations[key][container.ID] != nil {
		return fmt.Errorf("Destination alreaady exists!")
	}

	if len(container.Config.ExposedPorts) == 0 {
		log.Printf("Container %s does not have any exposed ports\n", container.ID)
		return nil
	}

	dest, err := NewDestination(container)
	if err != nil {
		return err
	}

	gw.Lock()
	defer gw.Unlock()

	if gw.Destinations[key] == nil {
		gw.Destinations[key] = DestinationMap{}
	}

	gw.Destinations[key][container.ID] = dest
	return nil
}

func (gw *Gateway) Remove(container *docker.Container) error {
	log.Println("Removing container:", container.ID)
	key := gw.fetchDomain(container)

	if len(gw.Destinations[key]) == 0 {
		return nil
	}

	gw.Lock()
	defer gw.Unlock()

	delete(gw.Destinations[key], container.ID)
	return nil
}

func (gw *Gateway) Find(reqHost string) *Destination {
	host := strings.ToLower(strings.Split(reqHost, ":")[0])

	if len(gw.Destinations[host]) == 0 {
		return nil
	}

	list := []*Destination{}
	for _, dst := range gw.Destinations[host] {
		list = append(list, dst)
	}

	return list[rand.Intn(len(list))]
}

func (gw *Gateway) Handle(w http.ResponseWriter, r *http.Request) {
	destination := gw.Find(r.Host)

	log.Printf("Request method=%s host=%s path=%s -> %s\n", r.Method, r.Host, r.RequestURI, destination)

	if destination == nil {
		gw.notFound(w, r)
		return
	}

	destination.proxy.ServeHTTP(w, r)
}

func (gw *Gateway) RenderDestinations(w http.ResponseWriter, r *http.Request) {
	result := map[string][]string{}

	for k, dstMap := range gw.Destinations {
		for _, dst := range dstMap {
			result[k] = append(result[k], dst.String())
		}
	}

	data, _ := json.Marshal(result)
	fmt.Fprintf(w, "%s", data)
}

func (gw *Gateway) RenderLogs(w http.ResponseWriter, r *http.Request) {
	dest := gw.Find(r.Host)

	if dest == nil {
		gw.notFound(w, r)
		return
	}

	// Determine how many lines of logs we need to fetch
	lines := r.URL.Query().Get("lines")
	if lines == "" {
		lines = "3000"
	}

	buff := bytes.NewBuffer([]byte{})

	err := gw.Client.Logs(docker.LogsOptions{
		Container:    dest.containerId,
		Stdout:       true,
		Stderr:       true,
		OutputStream: buff,
		ErrorStream:  buff,
		RawTerminal:  true,
		Tail:         lines,
	})

	if err != nil {
		fmt.Fprintln(w, "Error while fetching logs:", err)
		return
	}

	r.Header.Set("Content-Type", "text/plain")
	fmt.Fprintf(w, "%s", buff.String())
}

func (gw *Gateway) RenderEnvironment(w http.ResponseWriter, r *http.Request) {
	dest := gw.Find(r.Host)

	if dest == nil {
		gw.notFound(w, r)
		return
	}

	container, err := gw.Client.InspectContainer(dest.containerId)
	if err != nil {
		fmt.Fprintln(w, "Error while inspecting container:", err)
		return
	}

	env := strings.Join(container.Config.Env, "\n")
	r.Header.Set("Content-Type", "text/plain")
	fmt.Fprintf(w, "%s", env)
}

func (gw *Gateway) Start(bind string) error {
	log.Printf("Starting gateway server on http://%s\n", bind)

	http.HandleFunc("/_routes", gw.RenderDestinations)
	http.HandleFunc("/_destinations", gw.RenderDestinations)
	http.HandleFunc("/_logs", gw.RenderLogs)
	http.HandleFunc("/_env", gw.RenderEnvironment)

	http.HandleFunc("/", gw.Handle)
	return http.ListenAndServe(bind, nil)
}
<|endoftext|>


<commit_before>package veneur

import (
	"compress/zlib"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"reflect"
	"time"

	"github.com/stripe/veneur/samplers"
	"github.com/stripe/veneur/trace"
)

type contextHandler func(c context.Context, w http.ResponseWriter, r *http.Request)

func (ch contextHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := context.Background()
	ch(ctx, w, r)
}

// handleImport generates the handler that responds to POST requests submitting
// metrics to the global veneur instance.
func handleImport(s *Server) http.Handler {
	return contextHandler(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {

		//trace := trace.StartTrace("/import")
		//defer trace.Record("veneur.import.trace", []*ssf.SSFTag{})
		span := tracer.StartSpan("/import", trace.NameTag("veneur.import.opentracing")).(*trace.Span)
		defer span.Finish()

		innerLogger := log.WithField("client", r.RemoteAddr)

		var (
			jsonMetrics []samplers.JSONMetric
			body        io.ReadCloser
			err         error
			encoding    = r.Header.Get("Content-Encoding")
		)
		switch encLogger := innerLogger.WithField("encoding", encoding); encoding {
		case "":
			body = r.Body
			encoding = "identity"
		case "deflate":
			body, err = zlib.NewReader(r.Body)
			if err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				//trace.Error(err)
				encLogger.WithError(err).Error("Could not read compressed request body")
				s.statsd.Count("import.request_error_total", 1, []string{"cause:deflate"}, 1.0)
				return
			}
			defer body.Close()
		default:
			http.Error(w, encoding, http.StatusUnsupportedMediaType)
			//trace.Error(errors.New("Could not determine content-encoding of request"))
			encLogger.Error("Could not determine content-encoding of request")
			s.statsd.Count("import.request_error_total", 1, []string{"cause:unknown_content_encoding"}, 1.0)
			return
		}

		if err = json.NewDecoder(body).Decode(&jsonMetrics); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			//trace.Error(err)
			innerLogger.WithError(err).Error("Could not decode /import request")
			s.statsd.Count("import.request_error_total", 1, []string{"cause:json"}, 1.0)
			return
		}

		if len(jsonMetrics) == 0 {
			const msg = "Received empty /import request"
			http.Error(w, msg, http.StatusBadRequest)
			//trace.Error(errors.New(msg))
			innerLogger.WithError(err).Error(msg)
			return
		}

		// We want to make sure we have at least one entry
		// that is not empty (ie, all fields are the zero value)
		// because that is usually the sign that we are unmarshalling
		// into the wrong struct type

		if !s.nonEmpty(span.Attach(ctx), jsonMetrics) {
			const msg = "Received empty or improperly-formed metrics"
			http.Error(w, msg, http.StatusBadRequest)
			//trace.Error(errors.New(msg))
			innerLogger.Error(msg)
			return
		}

		w.WriteHeader(http.StatusAccepted)
		s.statsd.TimeInMilliseconds("import.response_duration_ns",
			float64(time.Since(span.Start).Nanoseconds()),
			[]string{"part:request", fmt.Sprintf("encoding:%s", encoding)},
			1.0)

		// the server usually waits for this to return before finalizing the
		// response, so this part must be done asynchronously
		go s.ImportMetrics(span.Attach(ctx), jsonMetrics)
	})
}

// nonEmpty returns true if there is at least one non-empty
// metric
func (s *Server) nonEmpty(ctx context.Context, jsonMetrics []samplers.JSONMetric) bool {

	span, _ := trace.StartSpanFromContext(ctx, "veneur.import.nonEmpty.opentracing")
	defer span.Finish()

	sentinel := samplers.JSONMetric{}
	for _, metric := range jsonMetrics {
		if !reflect.DeepEqual(sentinel, metric) {
			// we have found at least one entry that is properly formed
			return true

		}
	}
	return false
}
<commit_msg>We can use span.Error directly!<commit_after>package veneur

import (
	"compress/zlib"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"reflect"
	"time"

	"github.com/stripe/veneur/samplers"
	"github.com/stripe/veneur/trace"
)

type contextHandler func(c context.Context, w http.ResponseWriter, r *http.Request)

func (ch contextHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := context.Background()
	ch(ctx, w, r)
}

// handleImport generates the handler that responds to POST requests submitting
// metrics to the global veneur instance.
func handleImport(s *Server) http.Handler {
	return contextHandler(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {

		//trace := trace.StartTrace("/import")
		//defer trace.Record("veneur.import.trace", []*ssf.SSFTag{})
		span := tracer.StartSpan("/import", trace.NameTag("veneur.import.opentracing")).(*trace.Span)
		defer span.Finish()

		innerLogger := log.WithField("client", r.RemoteAddr)

		var (
			jsonMetrics []samplers.JSONMetric
			body        io.ReadCloser
			err         error
			encoding    = r.Header.Get("Content-Encoding")
		)
		switch encLogger := innerLogger.WithField("encoding", encoding); encoding {
		case "":
			body = r.Body
			encoding = "identity"
		case "deflate":
			body, err = zlib.NewReader(r.Body)
			if err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				span.Error(err)
				encLogger.WithError(err).Error("Could not read compressed request body")
				s.statsd.Count("import.request_error_total", 1, []string{"cause:deflate"}, 1.0)
				return
			}
			defer body.Close()
		default:
			http.Error(w, encoding, http.StatusUnsupportedMediaType)
			span.Error(errors.New("Could not determine content-encoding of request"))
			encLogger.Error("Could not determine content-encoding of request")
			s.statsd.Count("import.request_error_total", 1, []string{"cause:unknown_content_encoding"}, 1.0)
			return
		}

		if err = json.NewDecoder(body).Decode(&jsonMetrics); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			span.Error(err)
			innerLogger.WithError(err).Error("Could not decode /import request")
			s.statsd.Count("import.request_error_total", 1, []string{"cause:json"}, 1.0)
			return
		}

		if len(jsonMetrics) == 0 {
			const msg = "Received empty /import request"
			http.Error(w, msg, http.StatusBadRequest)
			span.Error(errors.New(msg))
			innerLogger.WithError(err).Error(msg)
			return
		}

		// We want to make sure we have at least one entry
		// that is not empty (ie, all fields are the zero value)
		// because that is usually the sign that we are unmarshalling
		// into the wrong struct type

		if !s.nonEmpty(span.Attach(ctx), jsonMetrics) {
			const msg = "Received empty or improperly-formed metrics"
			http.Error(w, msg, http.StatusBadRequest)
			span.Error(errors.New(msg))
			innerLogger.Error(msg)
			return
		}

		w.WriteHeader(http.StatusAccepted)
		s.statsd.TimeInMilliseconds("import.response_duration_ns",
			float64(time.Since(span.Start).Nanoseconds()),
			[]string{"part:request", fmt.Sprintf("encoding:%s", encoding)},
			1.0)

		// the server usually waits for this to return before finalizing the
		// response, so this part must be done asynchronously
		go s.ImportMetrics(span.Attach(ctx), jsonMetrics)
	})
}

// nonEmpty returns true if there is at least one non-empty
// metric
func (s *Server) nonEmpty(ctx context.Context, jsonMetrics []samplers.JSONMetric) bool {

	span, _ := trace.StartSpanFromContext(ctx, "veneur.import.nonEmpty.opentracing")
	defer span.Finish()

	sentinel := samplers.JSONMetric{}
	for _, metric := range jsonMetrics {
		if !reflect.DeepEqual(sentinel, metric) {
			// we have found at least one entry that is properly formed
			return true

		}
	}
	return false
}
<|endoftext|>


<commit_before>package gen

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"go/format"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"text/template"
	"time"
)

// dirMetadata is the location of the x-common repository on the filesystem.
// We're making the assumption that the x-common repository has been cloned to
// the same parent directory as the xgo repository.
// E.g.
//
//     $ tree -L 1 .
//     .
//     ├── x-common
//     └── xgo
var dirMetadata string

// dirExercise is the location that the test cases should be generated to.
// This assumes that the generator script lives in the .meta directory within
// the exercise directory. Falls back to the present working directory.
var dirExercise string

// genClient creates an http client with a 10 second timeout so we don't get
// stuck waiting for a response.
var genClient = &http.Client{Timeout: 10 * time.Second}

const (
	// canonicalDataURL is the URL for the raw canonical-data.json data,
	// requires exercise name.
	canonicalDataURL = "https://raw.githubusercontent.com/exercism/x-common/master/exercises/%s/canonical-data.json"
	// commitsURL is the GitHub api endpoint for the canonical-data.json
	// file commit history, requires exercise name.
	commitsURL = "https://api.github.com/repos/exercism/x-common/commits?path=exercises/%s/canonical-data.json"
)

// Header tells how the test data was generated, for display in the header of
// cases_test.go
type Header struct {
	// Ori is a deprecated short name for Origin.
	// TODO: Remove Ori once everything switches to Origin.
	Ori     string
	Origin  string
	Commit  string
	Version string
}

func (h Header) String() string {
	s := fmt.Sprintf("// Source: %s\n", h.Origin)
	if h.Commit != "" {
		s += fmt.Sprintf("// Commit: %s\n", h.Commit)
	}
	if h.Version != "" {
		s += fmt.Sprintf("// x-common version: %s\n", h.Version)
	}
	return s
}

func init() {
	if _, path, _, ok := runtime.Caller(0); ok {
		dirMetadata = filepath.Join(path, "..", "..", "..", "x-common")
	}
	if _, path, _, ok := runtime.Caller(2); ok {
		dirExercise = filepath.Join(path, "..", "..")
	}
	if dirExercise == "" {
		dirExercise = "."
	}
}

// Gen generates the exercise cases_test.go file from the relevant canonical-data.json
func Gen(exercise string, j interface{}, t *template.Template) error {
	if dirMetadata == "" {
		return errors.New("unable to determine current path")
	}
	jFile := filepath.Join("exercises", exercise, "canonical-data.json")
	// try to find and read the local json source file
	log.Printf("[LOCAL] fetching %s test data\n", exercise)
	jPath, jOrigin, jCommit := getLocal(jFile)
	if jPath != "" {
		log.Printf("[LOCAL] source: %s\n", jPath)
	}
	jSrc, err := ioutil.ReadFile(filepath.Join(jPath, jFile))
	if err != nil {
		// fetch json data remotely if there's no local file
		log.Println("[LOCAL] No test data found")
		log.Printf("[REMOTE] fetching %s test data\n", exercise)
		jSrc, jOrigin, jCommit, err = getRemote(exercise)
		if err != nil {
			return err
		}
	}

	// unmarshal the json source to a Go structure
	if err = json.Unmarshal(jSrc, j); err != nil {
		// This error message is usually enough if the problem is a wrong
		// data structure defined here. Sadly it doesn't locate the error well
		// in the case of invalid JSON.  Use a real validator tool if you can't
		// spot the problem right away.
		return fmt.Errorf(`unexpected data structure: %v`, err)
	}

	// These fields are guaranteed to be in every problem
	var commonMetadata struct {
		Version string
	}
	if err := json.Unmarshal(jSrc, &commonMetadata); err != nil {
		return fmt.Errorf(`didn't contain version: %v`, err)
	}

	// package up a little meta data
	d := struct {
		Header
		J interface{}
	}{Header{
		Ori:     jOrigin,
		Origin:  jOrigin,
		Commit:  jCommit,
		Version: commonMetadata.Version,
	}, j}

	// render the Go test cases
	var b bytes.Buffer
	if err := t.Execute(&b, &d); err != nil {
		return err
	}
	// clean it up
	src, err := format.Source(b.Bytes())
	if err != nil {
		return err
	}
	// write output file for the Go test cases.
	return ioutil.WriteFile(filepath.Join(dirExercise, "cases_test.go"), src, 0666)
}

func getLocal(jFile string) (jPath, jOrigin, jCommit string) {
	// Ideally draw from a .json which is pulled from the official x-common
	// repository.  For development however, accept a file in current directory
	// if there is no .json in source control.  Also allow an override in any
	// case by environment variable.
	if jPath := os.Getenv("EXTEST"); jPath > "" {
		return jPath, "local file", "" // override
	}
	c := exec.Command("git", "log", "-1", "--oneline", jFile)
	c.Dir = dirMetadata
	origin, err := c.Output()
	if err != nil {
		return "", "local file", "" // no source control
	}
	if _, err = os.Stat(filepath.Join(c.Dir, jFile)); err != nil {
		return "", "local file", "" // not in source control
	}
	// good.  return source control dir and commit.
	return c.Dir, "exercism/x-common", string(bytes.TrimSpace(origin))
}

func getRemote(exercise string) (body []byte, jOrigin string, jCommit string, err error) {
	url := fmt.Sprintf(canonicalDataURL, exercise)
	resp, err := genClient.Get(url)
	if err != nil {
		return []byte{}, "", "", err
	}
	if resp.StatusCode != http.StatusOK {
		return []byte{}, "", "", fmt.Errorf("error fetching remote data: %s", resp.Status)
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, "", "", err
	}
	c, err := getRemoteCommit(exercise)
	if err != nil {
		// we always expect to have the commit in the cases_test.go
		// file, so return the error if we can't fetch it
		return []byte{}, "", "", err
	}
	log.Printf("[REMOTE] source: %s\n", url)
	return body, "exercism/x-common", c, nil
}

func getRemoteCommit(exercise string) (string, error) {
	type Commits struct {
		Sha    string
		Commit struct {
			Message string
		}
	}
	resp, err := genClient.Get(fmt.Sprintf(commitsURL, exercise))
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	var c []Commits
	err = json.NewDecoder(resp.Body).Decode(&c)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s %s", c[0].Sha[0:7], c[0].Commit.Message), nil
}
<commit_msg>gen.go: be more explicit about local source<commit_after>package gen

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"go/format"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"text/template"
	"time"
)

// dirMetadata is the location of the x-common repository on the filesystem.
// We're making the assumption that the x-common repository has been cloned to
// the same parent directory as the xgo repository.
// E.g.
//
//     $ tree -L 1 .
//     .
//     ├── x-common
//     └── xgo
var dirMetadata string

// dirExercise is the location that the test cases should be generated to.
// This assumes that the generator script lives in the .meta directory within
// the exercise directory. Falls back to the present working directory.
var dirExercise string

// genClient creates an http client with a 10 second timeout so we don't get
// stuck waiting for a response.
var genClient = &http.Client{Timeout: 10 * time.Second}

const (
	// canonicalDataURL is the URL for the raw canonical-data.json data,
	// requires exercise name.
	canonicalDataURL = "https://raw.githubusercontent.com/exercism/x-common/master/exercises/%s/canonical-data.json"
	// commitsURL is the GitHub api endpoint for the canonical-data.json
	// file commit history, requires exercise name.
	commitsURL = "https://api.github.com/repos/exercism/x-common/commits?path=exercises/%s/canonical-data.json"
)

// Header tells how the test data was generated, for display in the header of
// cases_test.go
type Header struct {
	// Ori is a deprecated short name for Origin.
	// TODO: Remove Ori once everything switches to Origin.
	Ori     string
	Origin  string
	Commit  string
	Version string
}

func (h Header) String() string {
	s := fmt.Sprintf("// Source: %s\n", h.Origin)
	if h.Commit != "" {
		s += fmt.Sprintf("// Commit: %s\n", h.Commit)
	}
	if h.Version != "" {
		s += fmt.Sprintf("// x-common version: %s\n", h.Version)
	}
	return s
}

func init() {
	if _, path, _, ok := runtime.Caller(0); ok {
		dirMetadata = filepath.Join(path, "..", "..", "..", "x-common")
	}
	if _, path, _, ok := runtime.Caller(2); ok {
		dirExercise = filepath.Join(path, "..", "..")
	}
	if dirExercise == "" {
		dirExercise = "."
	}
}

// Gen generates the exercise cases_test.go file from the relevant canonical-data.json
func Gen(exercise string, j interface{}, t *template.Template) error {
	if dirMetadata == "" {
		return errors.New("unable to determine current path")
	}
	jFile := filepath.Join("exercises", exercise, "canonical-data.json")
	// try to find and read the local json source file
	log.Printf("[LOCAL] fetching %s test data\n", exercise)
	jPath, jOrigin, jCommit := getLocal(jFile)
	jFilePath := filepath.Join(jPath, jFile)
	if jPath != "" {
		log.Printf("[LOCAL] source: %s\n", jFilePath)
	}
	jSrc, err := ioutil.ReadFile(jFilePath)
	if err != nil {
		// fetch json data remotely if there's no local file
		log.Println("[LOCAL] No test data found")
		log.Printf("[REMOTE] fetching %s test data\n", exercise)
		jSrc, jOrigin, jCommit, err = getRemote(exercise)
		if err != nil {
			return err
		}
	}

	// unmarshal the json source to a Go structure
	if err = json.Unmarshal(jSrc, j); err != nil {
		// This error message is usually enough if the problem is a wrong
		// data structure defined here. Sadly it doesn't locate the error well
		// in the case of invalid JSON.  Use a real validator tool if you can't
		// spot the problem right away.
		return fmt.Errorf(`unexpected data structure: %v`, err)
	}

	// These fields are guaranteed to be in every problem
	var commonMetadata struct {
		Version string
	}
	if err := json.Unmarshal(jSrc, &commonMetadata); err != nil {
		return fmt.Errorf(`didn't contain version: %v`, err)
	}

	// package up a little meta data
	d := struct {
		Header
		J interface{}
	}{Header{
		Ori:     jOrigin,
		Origin:  jOrigin,
		Commit:  jCommit,
		Version: commonMetadata.Version,
	}, j}

	// render the Go test cases
	var b bytes.Buffer
	if err := t.Execute(&b, &d); err != nil {
		return err
	}
	// clean it up
	src, err := format.Source(b.Bytes())
	if err != nil {
		return err
	}
	// write output file for the Go test cases.
	return ioutil.WriteFile(filepath.Join(dirExercise, "cases_test.go"), src, 0666)
}

func getLocal(jFile string) (jPath, jOrigin, jCommit string) {
	// Ideally draw from a .json which is pulled from the official x-common
	// repository.  For development however, accept a file in current directory
	// if there is no .json in source control.  Also allow an override in any
	// case by environment variable.
	if jPath := os.Getenv("EXTEST"); jPath > "" {
		return jPath, "local file", "" // override
	}
	c := exec.Command("git", "log", "-1", "--oneline", jFile)
	c.Dir = dirMetadata
	origin, err := c.Output()
	if err != nil {
		return "", "local file", "" // no source control
	}
	if _, err = os.Stat(filepath.Join(c.Dir, jFile)); err != nil {
		return "", "local file", "" // not in source control
	}
	// good.  return source control dir and commit.
	return c.Dir, "exercism/x-common", string(bytes.TrimSpace(origin))
}

func getRemote(exercise string) (body []byte, jOrigin string, jCommit string, err error) {
	url := fmt.Sprintf(canonicalDataURL, exercise)
	resp, err := genClient.Get(url)
	if err != nil {
		return []byte{}, "", "", err
	}
	if resp.StatusCode != http.StatusOK {
		return []byte{}, "", "", fmt.Errorf("error fetching remote data: %s", resp.Status)
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, "", "", err
	}
	c, err := getRemoteCommit(exercise)
	if err != nil {
		// we always expect to have the commit in the cases_test.go
		// file, so return the error if we can't fetch it
		return []byte{}, "", "", err
	}
	log.Printf("[REMOTE] source: %s\n", url)
	return body, "exercism/x-common", c, nil
}

func getRemoteCommit(exercise string) (string, error) {
	type Commits struct {
		Sha    string
		Commit struct {
			Message string
		}
	}
	resp, err := genClient.Get(fmt.Sprintf(commitsURL, exercise))
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	var c []Commits
	err = json.NewDecoder(resp.Body).Decode(&c)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s %s", c[0].Sha[0:7], c[0].Commit.Message), nil
}
<|endoftext|>


<commit_before>package acceptance

import (
	"testing"

	"github.com/yext/edward/home"
	"github.com/yext/edward/services"

	"github.com/yext/edward/instance"
)

func TestRunSuccess(t *testing.T) {

}

func TestRunFailure(t *testing.T) {
	var tests = []struct {
		name         string
		dataDir      string
		startArgs    []string
		expectedURLs []string
	}{
		{
			name:      "launch failure",
			dataDir:   "testdata/launchfailure",
			startArgs: []string{"run", "broken"},
			expectedURLs: []string{
				"http://127.0.0.1:51234/",
			},
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			workingDir, cleanup, err := createWorkingDir("testStart", test.dataDir)
			defer cleanup()
			if err != nil {
				t.Fatal(err)
			}
			executeCommandExpectFailure(t, workingDir, edwardExecutable, test.startArgs...)
			for _, url := range test.expectedURLs {
				expectErrorFromURL(t, url)
			}
			dirCfg, err := home.NewConfiguration()
			if err != nil {
				t.Fatal(err)
			}
			statuses, err := instance.LoadStatusForService(&services.ServiceConfig{
				Name: "broken",
			}, dirCfg.StateDir)
			if err != nil {
				t.Fatal(err)
			}
			t.Log(statuses)
		})
	}
}
<commit_msg>Remove unnecessary test function.<commit_after>package acceptance

import (
	"testing"

	"github.com/yext/edward/home"
	"github.com/yext/edward/services"

	"github.com/yext/edward/instance"
)

func TestRunFailure(t *testing.T) {
	var tests = []struct {
		name         string
		dataDir      string
		startArgs    []string
		expectedURLs []string
	}{
		{
			name:      "launch failure",
			dataDir:   "testdata/launchfailure",
			startArgs: []string{"run", "broken"},
			expectedURLs: []string{
				"http://127.0.0.1:51234/",
			},
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			workingDir, cleanup, err := createWorkingDir("testStart", test.dataDir)
			defer cleanup()
			if err != nil {
				t.Fatal(err)
			}
			executeCommandExpectFailure(t, workingDir, edwardExecutable, test.startArgs...)
			for _, url := range test.expectedURLs {
				expectErrorFromURL(t, url)
			}
			dirCfg, err := home.NewConfiguration()
			if err != nil {
				t.Fatal(err)
			}
			statuses, err := instance.LoadStatusForService(&services.ServiceConfig{
				Name: "broken",
			}, dirCfg.StateDir)
			if err != nil {
				t.Fatal(err)
			}
			t.Log(statuses)
		})
	}
}
<|endoftext|>


<commit_before>package gen

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"go/format"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"text/template"
	"time"
)

// dirMetadata is the location of the x-common repository on the filesystem.
// We're making the assumption that the x-common repository has been cloned to
// the same parent directory as the xgo repository.
// E.g.
//
//     $ tree -L 1 .
//     .
//     ├── x-common
//     └── xgo
var dirMetadata string

// dirExercise is the location that the test cases should be generated to.
// This assumes that the generator script lives in the .meta directory within
// the exercise directory. Falls back to the present working directory.
var dirExercise string

// genClient creates an http client with a 10 second timeout so we don't get
// stuck waiting for a response.
var genClient = &http.Client{Timeout: 10 * time.Second}

const (
	// canonicalDataURL is the URL for the raw canonical-data.json data,
	// requires exercise name.
	canonicalDataURL = "https://raw.githubusercontent.com/exercism/x-common/master/exercises/%s/canonical-data.json"
	// commitsURL is the GitHub api endpoint for the canonical-data.json
	// file commit history, requires exercise name.
	commitsURL = "https://api.github.com/repos/exercism/x-common/commits?path=exercises/%s/canonical-data.json"
)

// Header tells how the test data was generated, for display in the header of
// cases_test.go
type Header struct {
	// Ori is a deprecated short name for Origin.
	// TODO: Remove Ori once everything switches to Origin.
	Ori     string
	Origin  string
	Commit  string
	Version string
}

func (h Header) String() string {
	s := fmt.Sprintf("// Source: %s\n", h.Origin)
	if h.Commit != "" {
		s += fmt.Sprintf("// Commit: %s\n", h.Commit)
	}
	if h.Version != "" {
		s += fmt.Sprintf("// x-common version: %s\n", h.Version)
	}
	return s
}

func init() {
	if _, path, _, ok := runtime.Caller(0); ok {
		dirMetadata = filepath.Join(path, "..", "..", "..", "x-common")
	}
	if _, path, _, ok := runtime.Caller(2); ok {
		dirExercise = filepath.Join(path, "..", "..")
	}
	if dirExercise == "" {
		dirExercise = "."
	}
}

// Gen generates the exercise cases_test.go file from the relevant canonical-data.json
func Gen(exercise string, j interface{}, t *template.Template) error {
	if dirMetadata == "" {
		return errors.New("unable to determine current path")
	}
	jFile := filepath.Join("exercises", exercise, "canonical-data.json")
	// try to find and read the local json source file
	log.Printf("[LOCAL] fetching %s test data\n", exercise)
	jPath, jOrigin, jCommit := getLocal(jFile)
	jFilePath := filepath.Join(jPath, jFile)
	if jPath != "" {
		log.Printf("[LOCAL] source: %s\n", jFilePath)
	}
	jSrc, err := ioutil.ReadFile(jFilePath)
	if err != nil {
		// fetch json data remotely if there's no local file
		log.Println("[LOCAL] No test data found")
		log.Printf("[REMOTE] fetching %s test data\n", exercise)
		jSrc, jOrigin, jCommit, err = getRemote(exercise)
		if err != nil {
			return err
		}
	}

	// unmarshal the json source to a Go structure
	if err = json.Unmarshal(jSrc, j); err != nil {
		// This error message is usually enough if the problem is a wrong
		// data structure defined here. Sadly it doesn't locate the error well
		// in the case of invalid JSON.  Use a real validator tool if you can't
		// spot the problem right away.
		return fmt.Errorf(`unexpected data structure: %v`, err)
	}

	// These fields are guaranteed to be in every problem
	var commonMetadata struct {
		Version string
	}
	if err := json.Unmarshal(jSrc, &commonMetadata); err != nil {
		return fmt.Errorf(`didn't contain version: %v`, err)
	}

	// package up a little meta data
	d := struct {
		Header
		J interface{}
	}{Header{
		Ori:     jOrigin,
		Origin:  jOrigin,
		Commit:  jCommit,
		Version: commonMetadata.Version,
	}, j}

	// render the Go test cases
	var b bytes.Buffer
	if err := t.Execute(&b, &d); err != nil {
		return err
	}
	// clean it up
	src, err := format.Source(b.Bytes())
	if err != nil {
		return err
	}
	// write output file for the Go test cases.
	return ioutil.WriteFile(filepath.Join(dirExercise, "cases_test.go"), src, 0666)
}

func getLocal(jFile string) (jPath, jOrigin, jCommit string) {
	// Ideally draw from a .json which is pulled from the official x-common
	// repository.  For development however, accept a file in current directory
	// if there is no .json in source control.  Also allow an override in any
	// case by environment variable.
	if jPath := os.Getenv("EXTEST"); jPath > "" {
		return jPath, "local file", "" // override
	}
	c := exec.Command("git", "log", "-1", "--oneline", jFile)
	c.Dir = dirMetadata
	origin, err := c.Output()
	if err != nil {
		return "", "local file", "" // no source control
	}
	if _, err = os.Stat(filepath.Join(c.Dir, jFile)); err != nil {
		return "", "local file", "" // not in source control
	}
	// good.  return source control dir and commit.
	return c.Dir, "exercism/x-common", string(bytes.TrimSpace(origin))
}

func getRemote(exercise string) (body []byte, jOrigin string, jCommit string, err error) {
	url := fmt.Sprintf(canonicalDataURL, exercise)
	resp, err := genClient.Get(url)
	if err != nil {
		return []byte{}, "", "", err
	}
	if resp.StatusCode != http.StatusOK {
		return []byte{}, "", "", fmt.Errorf("error fetching remote data: %s", resp.Status)
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, "", "", err
	}
	c, err := getRemoteCommit(exercise)
	if err != nil {
		// we always expect to have the commit in the cases_test.go
		// file, so return the error if we can't fetch it
		return []byte{}, "", "", err
	}
	log.Printf("[REMOTE] source: %s\n", url)
	return body, "exercism/x-common", c, nil
}

func getRemoteCommit(exercise string) (string, error) {
	type Commits struct {
		Sha    string
		Commit struct {
			Message string
		}
	}
	resp, err := genClient.Get(fmt.Sprintf(commitsURL, exercise))
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	var c []Commits
	err = json.NewDecoder(resp.Body).Decode(&c)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s %s", c[0].Sha[0:7], c[0].Commit.Message), nil
}
<commit_msg>gen.go: delete `Ori` field<commit_after>package gen

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"go/format"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"text/template"
	"time"
)

// dirMetadata is the location of the x-common repository on the filesystem.
// We're making the assumption that the x-common repository has been cloned to
// the same parent directory as the xgo repository.
// E.g.
//
//     $ tree -L 1 .
//     .
//     ├── x-common
//     └── xgo
var dirMetadata string

// dirExercise is the location that the test cases should be generated to.
// This assumes that the generator script lives in the .meta directory within
// the exercise directory. Falls back to the present working directory.
var dirExercise string

// genClient creates an http client with a 10 second timeout so we don't get
// stuck waiting for a response.
var genClient = &http.Client{Timeout: 10 * time.Second}

const (
	// canonicalDataURL is the URL for the raw canonical-data.json data,
	// requires exercise name.
	canonicalDataURL = "https://raw.githubusercontent.com/exercism/x-common/master/exercises/%s/canonical-data.json"
	// commitsURL is the GitHub api endpoint for the canonical-data.json
	// file commit history, requires exercise name.
	commitsURL = "https://api.github.com/repos/exercism/x-common/commits?path=exercises/%s/canonical-data.json"
)

// Header tells how the test data was generated, for display in the header of
// cases_test.go
type Header struct {
	Origin  string
	Commit  string
	Version string
}

func (h Header) String() string {
	s := fmt.Sprintf("// Source: %s\n", h.Origin)
	if h.Commit != "" {
		s += fmt.Sprintf("// Commit: %s\n", h.Commit)
	}
	if h.Version != "" {
		s += fmt.Sprintf("// x-common version: %s\n", h.Version)
	}
	return s
}

func init() {
	if _, path, _, ok := runtime.Caller(0); ok {
		dirMetadata = filepath.Join(path, "..", "..", "..", "x-common")
	}
	if _, path, _, ok := runtime.Caller(2); ok {
		dirExercise = filepath.Join(path, "..", "..")
	}
	if dirExercise == "" {
		dirExercise = "."
	}
}

// Gen generates the exercise cases_test.go file from the relevant canonical-data.json
func Gen(exercise string, j interface{}, t *template.Template) error {
	if dirMetadata == "" {
		return errors.New("unable to determine current path")
	}
	jFile := filepath.Join("exercises", exercise, "canonical-data.json")
	// try to find and read the local json source file
	log.Printf("[LOCAL] fetching %s test data\n", exercise)
	jPath, jOrigin, jCommit := getLocal(jFile)
	jFilePath := filepath.Join(jPath, jFile)
	if jPath != "" {
		log.Printf("[LOCAL] source: %s\n", jFilePath)
	}
	jSrc, err := ioutil.ReadFile(jFilePath)
	if err != nil {
		// fetch json data remotely if there's no local file
		log.Println("[LOCAL] No test data found")
		log.Printf("[REMOTE] fetching %s test data\n", exercise)
		jSrc, jOrigin, jCommit, err = getRemote(exercise)
		if err != nil {
			return err
		}
	}

	// unmarshal the json source to a Go structure
	if err = json.Unmarshal(jSrc, j); err != nil {
		// This error message is usually enough if the problem is a wrong
		// data structure defined here. Sadly it doesn't locate the error well
		// in the case of invalid JSON.  Use a real validator tool if you can't
		// spot the problem right away.
		return fmt.Errorf(`unexpected data structure: %v`, err)
	}

	// These fields are guaranteed to be in every problem
	var commonMetadata struct {
		Version string
	}
	if err := json.Unmarshal(jSrc, &commonMetadata); err != nil {
		return fmt.Errorf(`didn't contain version: %v`, err)
	}

	// package up a little meta data
	d := struct {
		Header
		J interface{}
	}{Header{
		Origin:  jOrigin,
		Commit:  jCommit,
		Version: commonMetadata.Version,
	}, j}

	// render the Go test cases
	var b bytes.Buffer
	if err := t.Execute(&b, &d); err != nil {
		return err
	}
	// clean it up
	src, err := format.Source(b.Bytes())
	if err != nil {
		return err
	}
	// write output file for the Go test cases.
	return ioutil.WriteFile(filepath.Join(dirExercise, "cases_test.go"), src, 0666)
}

func getLocal(jFile string) (jPath, jOrigin, jCommit string) {
	// Ideally draw from a .json which is pulled from the official x-common
	// repository.  For development however, accept a file in current directory
	// if there is no .json in source control.  Also allow an override in any
	// case by environment variable.
	if jPath := os.Getenv("EXTEST"); jPath > "" {
		return jPath, "local file", "" // override
	}
	c := exec.Command("git", "log", "-1", "--oneline", jFile)
	c.Dir = dirMetadata
	origin, err := c.Output()
	if err != nil {
		return "", "local file", "" // no source control
	}
	if _, err = os.Stat(filepath.Join(c.Dir, jFile)); err != nil {
		return "", "local file", "" // not in source control
	}
	// good.  return source control dir and commit.
	return c.Dir, "exercism/x-common", string(bytes.TrimSpace(origin))
}

func getRemote(exercise string) (body []byte, jOrigin string, jCommit string, err error) {
	url := fmt.Sprintf(canonicalDataURL, exercise)
	resp, err := genClient.Get(url)
	if err != nil {
		return []byte{}, "", "", err
	}
	if resp.StatusCode != http.StatusOK {
		return []byte{}, "", "", fmt.Errorf("error fetching remote data: %s", resp.Status)
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, "", "", err
	}
	c, err := getRemoteCommit(exercise)
	if err != nil {
		// we always expect to have the commit in the cases_test.go
		// file, so return the error if we can't fetch it
		return []byte{}, "", "", err
	}
	log.Printf("[REMOTE] source: %s\n", url)
	return body, "exercism/x-common", c, nil
}

func getRemoteCommit(exercise string) (string, error) {
	type Commits struct {
		Sha    string
		Commit struct {
			Message string
		}
	}
	resp, err := genClient.Get(fmt.Sprintf(commitsURL, exercise))
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	var c []Commits
	err = json.NewDecoder(resp.Body).Decode(&c)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s %s", c[0].Sha[0:7], c[0].Commit.Message), nil
}
<|endoftext|>


<commit_before>package client_test

import (
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	merkle "github.com/tendermint/go-merkle"
	rpctest "github.com/tendermint/tendermint/rpc/test"
	"github.com/tendermint/tendermint/types"
)

// Make sure status is correct (we connect properly)
func TestStatus(t *testing.T) {
	c := rpctest.GetClient()
	chainID := rpctest.GetConfig().GetString("chain_id")
	status, err := c.Status()
	if assert.Nil(t, err) {
		assert.Equal(t, chainID, status.NodeInfo.Network)
	}
}

// Make sure info is correct (we connect properly)
func TestInfo(t *testing.T) {
	c := rpctest.GetClient()
	status, err := c.Status()
	require.Nil(t, err)
	info, err := c.ABCIInfo()
	require.Nil(t, err)
	assert.EqualValues(t, status.LatestBlockHeight, info.Response.LastBlockHeight)
	assert.True(t, strings.HasPrefix(info.Response.Data, "size:"))
}

// Make some app checks
func TestAppCalls(t *testing.T) {
	assert, require := assert.New(t), require.New(t)
	c := rpctest.GetClient()
	_, err := c.Block(1)
	assert.NotNil(err) // no block yet
	k, v, tx := MakeTxKV()
	_, err = c.BroadcastTxCommit(tx)
	require.Nil(err)
	// wait before querying
	time.Sleep(time.Second * 2)
	qres, err := c.ABCIQuery("/key", k, false)
	if assert.Nil(err) && assert.True(qres.Response.Code.IsOK()) {
		data := qres.Response
		// assert.Equal(k, data.GetKey())  // only returned for proofs
		assert.Equal(v, data.GetValue())
	}
	// and we can even check the block is added
	block, err := c.Block(3)
	assert.Nil(err) // now it's good :)
	appHash := block.BlockMeta.Header.AppHash
	assert.True(len(appHash) > 0)

	// and get the corresponding commit with the same apphash
	commit, err := c.Commit(3)
	assert.Nil(err) // now it's good :)
	cappHash := commit.Header.AppHash
	assert.Equal(appHash, cappHash)
	assert.NotNil(commit.Commit)

	// compare the commits (note Commit(2) has commit from Block(3))
	commit2, err := c.Commit(2)
	assert.Nil(err) // now it's good :)
	assert.Equal(block.Block.LastCommit, commit2.Commit)

	// and we got a proof that works!
	pres, err := c.ABCIQuery("/key", k, true)
	if assert.Nil(err) && assert.True(pres.Response.Code.IsOK()) {
		proof, err := merkle.ReadProof(pres.Response.GetProof())
		if assert.Nil(err) {
			key := pres.Response.GetKey()
			value := pres.Response.GetValue()
			assert.Equal(appHash, proof.RootHash)
			valid := proof.Verify(key, value, appHash)
			assert.True(valid)
		}
	}
}

// run most calls just to make sure no syntax errors
func TestNoErrors(t *testing.T) {
	assert := assert.New(t)
	c := rpctest.GetClient()
	_, err := c.NetInfo()
	assert.Nil(err)
	_, err = c.BlockchainInfo(0, 4)
	assert.Nil(err)
	// TODO: check with a valid height
	_, err = c.Block(1000)
	assert.NotNil(err)
	// maybe this is an error???
	// _, err = c.DialSeeds([]string{"one", "two"})
	// assert.Nil(err)
	gen, err := c.Genesis()
	if assert.Nil(err) {
		chainID := rpctest.GetConfig().GetString("chain_id")
		assert.Equal(chainID, gen.Genesis.ChainID)
	}
}

func TestSubscriptions(t *testing.T) {
	assert, require := assert.New(t), require.New(t)
	c := rpctest.GetClient()
	err := c.StartWebsocket()
	require.Nil(err)
	defer c.StopWebsocket()

	// subscribe to a transaction event
	_, _, tx := MakeTxKV()
	// this causes a panic in tendermint core!!!
	eventType := types.EventStringTx(types.Tx(tx))
	c.Subscribe(eventType)
	read := 0

	// set up a listener
	r, e := c.GetEventChannels()
	go func() {
		// read one event in the background
		select {
		case <-r:
			// TODO: actually parse this or something
			read += 1
		case err := <-e:
			panic(err)
		}
	}()

	// make sure nothing has happened yet.
	assert.Equal(0, read)

	// send a tx and wait for it to propogate
	_, err = c.BroadcastTxCommit(tx)
	assert.Nil(err, string(tx))
	// wait before querying
	time.Sleep(time.Second)

	// now make sure the event arrived
	assert.Equal(1, read)
}
<commit_msg>Full test coverage for rpc client<commit_after>package client_test

import (
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	merkle "github.com/tendermint/go-merkle"
	rpctest "github.com/tendermint/tendermint/rpc/test"
	"github.com/tendermint/tendermint/types"
)

// Make sure status is correct (we connect properly)
func TestStatus(t *testing.T) {
	c := rpctest.GetClient()
	chainID := rpctest.GetConfig().GetString("chain_id")
	status, err := c.Status()
	require.Nil(t, err, "%+v", err)
	assert.Equal(t, chainID, status.NodeInfo.Network)
}

// Make sure info is correct (we connect properly)
func TestInfo(t *testing.T) {
	c := rpctest.GetClient()
	status, err := c.Status()
	require.Nil(t, err, "%+v", err)
	info, err := c.ABCIInfo()
	require.Nil(t, err, "%+v", err)
	assert.EqualValues(t, status.LatestBlockHeight, info.Response.LastBlockHeight)
	assert.True(t, strings.HasPrefix(info.Response.Data, "size:"))
}

func TestNetInfo(t *testing.T) {
	c := rpctest.GetClient()
	netinfo, err := c.NetInfo()
	require.Nil(t, err, "%+v", err)
	assert.True(t, netinfo.Listening)
	assert.Equal(t, 0, len(netinfo.Peers))
}

func TestDialSeeds(t *testing.T) {
	c := rpctest.GetClient()
	// FIXME: fix server so it doesn't panic on invalid input
	_, err := c.DialSeeds([]string{"12.34.56.78:12345"})
	require.Nil(t, err, "%+v", err)
}

func TestGenesisAndValidators(t *testing.T) {
	c := rpctest.GetClient()
	chainID := rpctest.GetConfig().GetString("chain_id")

	// make sure this is the right genesis file
	gen, err := c.Genesis()
	require.Nil(t, err, "%+v", err)
	assert.Equal(t, chainID, gen.Genesis.ChainID)
	// get the genesis validator
	require.Equal(t, 1, len(gen.Genesis.Validators))
	gval := gen.Genesis.Validators[0]

	// get the current validators
	vals, err := c.Validators()
	require.Nil(t, err, "%+v", err)
	require.Equal(t, 1, len(vals.Validators))
	val := vals.Validators[0]

	// make sure the current set is also the genesis set
	assert.Equal(t, gval.Amount, val.VotingPower)
	assert.Equal(t, gval.PubKey, val.PubKey)
}

// Make some app checks
func TestAppCalls(t *testing.T) {
	assert, require := assert.New(t), require.New(t)
	c := rpctest.GetClient()
	_, err := c.Block(1)
	assert.NotNil(err) // no block yet
	k, v, tx := MakeTxKV()
	_, err = c.BroadcastTxCommit(tx)
	require.Nil(err, "%+v", err)
	// wait before querying
	time.Sleep(time.Second * 1)
	qres, err := c.ABCIQuery("/key", k, false)
	if assert.Nil(err) && assert.True(qres.Response.Code.IsOK()) {
		data := qres.Response
		// assert.Equal(k, data.GetKey())  // only returned for proofs
		assert.Equal(v, data.GetValue())
	}
	// and we can even check the block is added
	block, err := c.Block(3)
	require.Nil(err, "%+v", err)
	appHash := block.BlockMeta.Header.AppHash
	assert.True(len(appHash) > 0)
	assert.EqualValues(3, block.BlockMeta.Header.Height)

	// check blockchain info, now that we know there is info
	// TODO: is this commented somewhere that they are returned
	// in order of descending height???
	info, err := c.BlockchainInfo(1, 3)
	require.Nil(err, "%+v", err)
	assert.True(info.LastHeight > 2)
	if assert.Equal(3, len(info.BlockMetas)) {
		lastMeta := info.BlockMetas[0]
		assert.EqualValues(3, lastMeta.Header.Height)
		bMeta := block.BlockMeta
		assert.Equal(bMeta.Header.AppHash, lastMeta.Header.AppHash)
		assert.Equal(bMeta.BlockID, lastMeta.BlockID)
	}

	// and get the corresponding commit with the same apphash
	commit, err := c.Commit(3)
	require.Nil(err, "%+v", err)
	cappHash := commit.Header.AppHash
	assert.Equal(appHash, cappHash)
	assert.NotNil(commit.Commit)

	// compare the commits (note Commit(2) has commit from Block(3))
	commit2, err := c.Commit(2)
	require.Nil(err, "%+v", err)
	assert.Equal(block.Block.LastCommit, commit2.Commit)

	// and we got a proof that works!
	pres, err := c.ABCIQuery("/key", k, true)
	if assert.Nil(err) && assert.True(pres.Response.Code.IsOK()) {
		proof, err := merkle.ReadProof(pres.Response.GetProof())
		if assert.Nil(err) {
			key := pres.Response.GetKey()
			value := pres.Response.GetValue()
			assert.Equal(appHash, proof.RootHash)
			valid := proof.Verify(key, value, appHash)
			assert.True(valid)
		}
	}
}

func TestSubscriptions(t *testing.T) {
	assert, require := assert.New(t), require.New(t)
	c := rpctest.GetClient()
	err := c.StartWebsocket()
	require.Nil(err)
	defer c.StopWebsocket()

	// subscribe to a transaction event
	_, _, tx := MakeTxKV()
	// this causes a panic in tendermint core!!!
	eventType := types.EventStringTx(types.Tx(tx))
	c.Subscribe(eventType)
	read := 0

	// set up a listener
	r, e := c.GetEventChannels()
	go func() {
		// read one event in the background
		select {
		case <-r:
			// TODO: actually parse this or something
			read += 1
		case err := <-e:
			panic(err)
		}
	}()

	// make sure nothing has happened yet.
	assert.Equal(0, read)

	// send a tx and wait for it to propogate
	_, err = c.BroadcastTxCommit(tx)
	assert.Nil(err, string(tx))
	// wait before querying
	time.Sleep(time.Second)

	// now make sure the event arrived
	assert.Equal(1, read)
}
<|endoftext|>


<commit_before>package security

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"
	exutil "github.com/openshift/origin/test/extended/util"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	installConfigName = "cluster-config-v1"
	fipsFile          = "/proc/sys/crypto/fips_enabled"
)

func validateFIPSOnNode(oc *exutil.CLI, fipsExpected bool, node *corev1.Node) error {
	command := []string{"cat", fipsFile}
	out, err := exutil.ExecCommandOnMachineConfigDaemon(oc.AdminKubeClient(), oc, node, command)
	if err != nil {
		return err
	}
	nodeFips, err := strconv.ParseBool(strings.TrimSuffix(string(out), "\n"))
	if err != nil {
		return fmt.Errorf("Error parsing %s on node %s: %v", fipsFile, node.Name, err)
	}
	if nodeFips != fipsExpected {
		return fmt.Errorf("Expected FIPS state %v, found %v", fipsExpected, nodeFips)
	}
	return nil
}

var _ = g.Describe("[sig-arch] [Conformance] FIPS", func() {
	defer g.GinkgoRecover()
	oc := exutil.NewCLI("fips")

	g.It("TestFIPS", func() {
		clusterAdminKubeClientset := oc.AdminKubeClient()
		isFIPS, err := exutil.IsFIPS(clusterAdminKubeClientset.CoreV1())
		o.Expect(err).NotTo(o.HaveOccurred())

		// fetch one control plane and one worker, and validate FIPS state on it
		masterNodes, err := clusterAdminKubeClientset.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{
			LabelSelector: "node-role.kubernetes.io/master",
		})
		o.Expect(err).NotTo(o.HaveOccurred())
		masterNode := &masterNodes.Items[0]
		err = validateFIPSOnNode(oc, isFIPS, masterNode)
		o.Expect(err).NotTo(o.HaveOccurred())
		workerNodes, err := clusterAdminKubeClientset.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{
			LabelSelector: "node-role.kubernetes.io/worker",
		})
		o.Expect(err).NotTo(o.HaveOccurred())
		if len(workerNodes.Items) > 0 {
			workerNode := &workerNodes.Items[0]
			err = validateFIPSOnNode(oc, isFIPS, workerNode)
			o.Expect(err).NotTo(o.HaveOccurred())
		}
	})
})
<commit_msg>Fix TestFIPS on Hypershift<commit_after>package security

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"
	configv1 "github.com/openshift/api/config/v1"
	exutil "github.com/openshift/origin/test/extended/util"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	installConfigName = "cluster-config-v1"
	fipsFile          = "/proc/sys/crypto/fips_enabled"
)

func validateFIPSOnNode(oc *exutil.CLI, fipsExpected bool, node *corev1.Node) error {
	// The oc debug output prints a bunch of info messages and possible warnings (the latter can not be disabled).
	// Echo a prefix to be able to identify the line with our output.
	const commandLineIdentifierPrefix = "fips-command"
	out, err := oc.AsAdmin().Run("debug").Args("node/"+node.Name, "--", "/bin/bash", "-c", "echo -n "+commandLineIdentifierPrefix+" && cat "+fipsFile).Output()
	if err != nil {
		return err
	}
	var outFiltered string
	for _, line := range strings.Split(out, "\n") {
		if strings.HasPrefix(line, commandLineIdentifierPrefix) {
			outFiltered = strings.TrimPrefix(line, commandLineIdentifierPrefix)
			break
		}
	}
	nodeFips, err := strconv.ParseBool(outFiltered)
	if err != nil {
		return fmt.Errorf("Error parsing %s on node %s: %v", fipsFile, node.Name, err)
	}
	if nodeFips != fipsExpected {
		return fmt.Errorf("Expected FIPS state %v, found %v", fipsExpected, nodeFips)
	}
	return nil
}

var _ = g.Describe("[sig-arch] [Conformance] FIPS", func() {
	defer g.GinkgoRecover()
	oc := exutil.NewCLI("fips")

	g.It("TestFIPS", func() {
		controlPlaneTopology, err := exutil.GetControlPlaneTopology(oc)
		o.Expect(err).NotTo(o.HaveOccurred())
		clusterAdminKubeClientset := oc.AdminKubeClient()
		isFIPS, err := exutil.IsFIPS(clusterAdminKubeClientset.CoreV1())
		o.Expect(err).NotTo(o.HaveOccurred())

		// fetch one control plane and one worker, and validate FIPS state on it.
		// skip the controlplane node verification when external controlPlaneTopology as
		// there are no controlplane nodes.
		if *controlPlaneTopology != configv1.ExternalTopologyMode {
			masterNodes, err := clusterAdminKubeClientset.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{
				LabelSelector: "node-role.kubernetes.io/master",
			})
			o.Expect(err).NotTo(o.HaveOccurred())
			masterNode := &masterNodes.Items[0]
			err = validateFIPSOnNode(oc, isFIPS, masterNode)
		}
		o.Expect(err).NotTo(o.HaveOccurred())
		workerNodes, err := clusterAdminKubeClientset.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{
			LabelSelector: "node-role.kubernetes.io/worker",
		})
		o.Expect(err).NotTo(o.HaveOccurred())
		if len(workerNodes.Items) > 0 {
			workerNode := &workerNodes.Items[0]
			err = validateFIPSOnNode(oc, isFIPS, workerNode)
			o.Expect(err).NotTo(o.HaveOccurred())
		}
	})
})
<|endoftext|>


<commit_before>package filesystem

import (
	"archive/tar"
	"emperror.dev/errors"
	"github.com/apex/log"
	"github.com/juju/ratelimit"
	"github.com/karrick/godirwalk"
	"github.com/klauspost/pgzip"
	"github.com/pterodactyl/wings/config"
	"github.com/sabhiram/go-gitignore"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

const memory = 4 * 1024

var pool = sync.Pool{
	New: func() interface{} {
		b := make([]byte, memory)
		return b
	},
}

type Archive struct {
	// BasePath is the absolute path to create the archive from where Files and Ignore are
	// relative to.
	BasePath string

	// Ignore is a gitignore string (most likely read from a file) of files to ignore
	// from the archive.
	Ignore string

	// Files specifies the files to archive, this takes priority over the Ignore option, if
	// unspecified, all files in the BasePath will be archived unless Ignore is set.
	Files []string
}

// Creates an archive at dst with all of the files defined in the included files struct.
func (a *Archive) Create(dst string) error {
	f, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer f.Close()

	// Select a writer based off of the WriteLimit configuration option. If there is no
	// write limit, use the file as the writer.
	var writer io.Writer
	if writeLimit := int64(config.Get().System.Backups.WriteLimit * 1024 * 1024); writeLimit > 0 {
		// Token bucket with a capacity of "writeLimit" MiB, adding "writeLimit" MiB/s
		// and then wrap the file writer with the token bucket limiter.
		writer = ratelimit.Writer(f, ratelimit.NewBucketWithRate(float64(writeLimit), writeLimit))
	} else {
		writer = f
	}

	// Create a new gzip writer around the file.
	gw, _ := pgzip.NewWriterLevel(writer, pgzip.BestSpeed)
	_ = gw.SetConcurrency(1<<20, 1)
	defer gw.Close()

	// Create a new tar writer around the gzip writer.
	tw := tar.NewWriter(gw)
	defer tw.Close()

	// Configure godirwalk.
	options := &godirwalk.Options{
		FollowSymbolicLinks: false,
		Unsorted:            true,
		Callback:            a.callback(tw),
	}

	// If we're specifically looking for only certain files, or have requested
	// that certain files be ignored we'll update the callback function to reflect
	// that request.
	if len(a.Files) == 0 && len(a.Ignore) > 0 {
		i := ignore.CompileIgnoreLines(strings.Split(a.Ignore, "\n")...)

		options.Callback = a.callback(tw, func(_ string, rp string) error {
			if i.MatchesPath(rp) {
				return godirwalk.SkipThis
			}

			return nil
		})
	} else if len(a.Files) > 0 {
		options.Callback = a.withFilesCallback(tw)
	}

	// Recursively walk the path we are archiving.
	return godirwalk.Walk(a.BasePath, options)
}

// Callback function used to determine if a given file should be included in the archive
// being generated.
func (a *Archive) callback(tw *tar.Writer, opts ...func(path string, relative string) error) func(path string, de *godirwalk.Dirent) error {
	return func(path string, de *godirwalk.Dirent) error {
		// Skip directories because we walking them recursively.
		if de.IsDir() {
			return nil
		}

		relative := filepath.ToSlash(strings.TrimPrefix(path, a.BasePath+string(filepath.Separator)))

		// Call the additional options passed to this callback function. If any of them return
		// a non-nil error we will exit immediately.
		for _, opt := range opts {
			if err := opt(path, relative); err != nil {
				return err
			}
		}

		// Add the file to the archive, if it is nested in a directory,
		// the directory will be automatically "created" in the archive.
		return a.addToArchive(path, relative, tw)
	}
}

// Pushes only files defined in the Files key to the final archive.
func (a *Archive) withFilesCallback(tw *tar.Writer) func(path string, de *godirwalk.Dirent) error {
	return a.callback(tw, func(p string, rp string) error {
		for _, f := range a.Files {
			// If the given doesn't match, or doesn't have the same prefix continue
			// to the next item in the loop.
			if p != f && !strings.HasPrefix(p, f) {
				continue
			}

			// Once we have a match return a nil value here so that the loop stops and the
			// call to this function will correctly include the file in the archive. If there
			// are no matches we'll never make it to this line, and the final error returned
			// will be the godirwalk.SkipThis error.
			return nil
		}

		return godirwalk.SkipThis
	})
}

// Adds a given file path to the final archive being created.
func (a *Archive) addToArchive(p string, rp string, w *tar.Writer) error {
	// Lstat the file, this will give us the same information as Stat except
	// that it will not follow a symlink to it's target automatically.
	s, err := os.Lstat(p)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return errors.WrapIff(err, "failed executing os.Lstat on '%s'", rp)
	}

	// Resolve the symlink target if the file is a symlink.
	var target string
	if s.Mode()&os.ModeSymlink != 0 {
		// Read the target of the symlink. If there are any errors we will dump them out to
		// the logs, but we're not going to stop the backup. There are far too many cases of
		// symlinks causing all sorts of unnecessary pain in this process. Sucks to suck if
		// it doesn't work.
		target, err = os.Readlink(s.Name())
		if err != nil {
			// Ignore the not exist errors specifically, since theres nothing important about that.
			if !os.IsNotExist(err) {
				log.WithField("path", rp).WithField("readlink_err", err.Error()).Warn("failed reading symlink for target path; skipping...")
			}
			return nil
		}
	}

	// Get the tar FileInfoHeader in order to add the file to the archive.
	header, err := tar.FileInfoHeader(s, filepath.ToSlash(target))
	if err != nil {
		return errors.WrapIff(err, "failed to get tar#FileInfoHeader for '%s'", rp)
	}

	// Fix the header name if the file is not a symlink.
	if s.Mode()&os.ModeSymlink == 0 {
		header.Name = rp
	}

	// Write the tar FileInfoHeader to the archive.
	if err := w.WriteHeader(header); err != nil {
		return errors.WrapIff(err, "failed to write tar#FileInfoHeader for '%s'", rp)
	}

	// If the size of the file is less than 1 (most likely for symlinks), skip writing the file.
	if header.Size < 1 {
		return nil
	}

	// If the buffer size is larger than the file size, create a smaller buffer to hold the file.
	var buf []byte
	if header.Size < memory {
		buf = make([]byte, header.Size)
	} else {
		// Get a fixed-size buffer from the pool to save on allocations.
		buf = pool.Get().([]byte)
		defer func() {
			buf = make([]byte, memory)
			pool.Put(buf)
		}()
	}

	// Open the file.
	f, err := os.Open(p)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return errors.WrapIff(err, "failed to open '%s' for copying", header.Name)
	}
	defer f.Close()

	// Copy the file's contents to the archive using our buffer.
	if _, err := io.CopyBuffer(w, io.LimitReader(f, header.Size), buf); err != nil {
		return errors.WrapIff(err, "failed to copy '%s' to archive", header.Name)
	}

	return nil
}
<commit_msg>Update commentary<commit_after>package filesystem

import (
	"archive/tar"
	"emperror.dev/errors"
	"github.com/apex/log"
	"github.com/juju/ratelimit"
	"github.com/karrick/godirwalk"
	"github.com/klauspost/pgzip"
	"github.com/pterodactyl/wings/config"
	"github.com/sabhiram/go-gitignore"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

const memory = 4 * 1024

var pool = sync.Pool{
	New: func() interface{} {
		b := make([]byte, memory)
		return b
	},
}

type Archive struct {
	// BasePath is the absolute path to create the archive from where Files and Ignore are
	// relative to.
	BasePath string

	// Ignore is a gitignore string (most likely read from a file) of files to ignore
	// from the archive.
	Ignore string

	// Files specifies the files to archive, this takes priority over the Ignore option, if
	// unspecified, all files in the BasePath will be archived unless Ignore is set.
	Files []string
}

// Creates an archive at dst with all of the files defined in the included files struct.
func (a *Archive) Create(dst string) error {
	f, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer f.Close()

	// Select a writer based off of the WriteLimit configuration option. If there is no
	// write limit, use the file as the writer.
	var writer io.Writer
	if writeLimit := int64(config.Get().System.Backups.WriteLimit * 1024 * 1024); writeLimit > 0 {
		// Token bucket with a capacity of "writeLimit" MiB, adding "writeLimit" MiB/s
		// and then wrap the file writer with the token bucket limiter.
		writer = ratelimit.Writer(f, ratelimit.NewBucketWithRate(float64(writeLimit), writeLimit))
	} else {
		writer = f
	}

	// Create a new gzip writer around the file.
	gw, _ := pgzip.NewWriterLevel(writer, pgzip.BestSpeed)
	_ = gw.SetConcurrency(1<<20, 1)
	defer gw.Close()

	// Create a new tar writer around the gzip writer.
	tw := tar.NewWriter(gw)
	defer tw.Close()

	// Configure godirwalk.
	options := &godirwalk.Options{
		FollowSymbolicLinks: false,
		Unsorted:            true,
		Callback:            a.callback(tw),
	}

	// If we're specifically looking for only certain files, or have requested
	// that certain files be ignored we'll update the callback function to reflect
	// that request.
	if len(a.Files) == 0 && len(a.Ignore) > 0 {
		i := ignore.CompileIgnoreLines(strings.Split(a.Ignore, "\n")...)

		options.Callback = a.callback(tw, func(_ string, rp string) error {
			if i.MatchesPath(rp) {
				return godirwalk.SkipThis
			}

			return nil
		})
	} else if len(a.Files) > 0 {
		options.Callback = a.withFilesCallback(tw)
	}

	// Recursively walk the path we are archiving.
	return godirwalk.Walk(a.BasePath, options)
}

// Callback function used to determine if a given file should be included in the archive
// being generated.
func (a *Archive) callback(tw *tar.Writer, opts ...func(path string, relative string) error) func(path string, de *godirwalk.Dirent) error {
	return func(path string, de *godirwalk.Dirent) error {
		// Skip directories because we walking them recursively.
		if de.IsDir() {
			return nil
		}

		relative := filepath.ToSlash(strings.TrimPrefix(path, a.BasePath+string(filepath.Separator)))

		// Call the additional options passed to this callback function. If any of them return
		// a non-nil error we will exit immediately.
		for _, opt := range opts {
			if err := opt(path, relative); err != nil {
				return err
			}
		}

		// Add the file to the archive, if it is nested in a directory,
		// the directory will be automatically "created" in the archive.
		return a.addToArchive(path, relative, tw)
	}
}

// Pushes only files defined in the Files key to the final archive.
func (a *Archive) withFilesCallback(tw *tar.Writer) func(path string, de *godirwalk.Dirent) error {
	return a.callback(tw, func(p string, rp string) error {
		for _, f := range a.Files {
			// If the given doesn't match, or doesn't have the same prefix continue
			// to the next item in the loop.
			if p != f && !strings.HasPrefix(p, f) {
				continue
			}

			// Once we have a match return a nil value here so that the loop stops and the
			// call to this function will correctly include the file in the archive. If there
			// are no matches we'll never make it to this line, and the final error returned
			// will be the godirwalk.SkipThis error.
			return nil
		}

		return godirwalk.SkipThis
	})
}

// Adds a given file path to the final archive being created.
func (a *Archive) addToArchive(p string, rp string, w *tar.Writer) error {
	// Lstat the file, this will give us the same information as Stat except that it will not
	// follow a symlink to it's target automatically. This is important to avoid including
	// files that exist outside the server root unintentionally in the backup.
	s, err := os.Lstat(p)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return errors.WrapIff(err, "failed executing os.Lstat on '%s'", rp)
	}

	// Resolve the symlink target if the file is a symlink.
	var target string
	if s.Mode()&os.ModeSymlink != 0 {
		// Read the target of the symlink. If there are any errors we will dump them out to
		// the logs, but we're not going to stop the backup. There are far too many cases of
		// symlinks causing all sorts of unnecessary pain in this process. Sucks to suck if
		// it doesn't work.
		target, err = os.Readlink(s.Name())
		if err != nil {
			// Ignore the not exist errors specifically, since theres nothing important about that.
			if !os.IsNotExist(err) {
				log.WithField("path", rp).WithField("readlink_err", err.Error()).Warn("failed reading symlink for target path; skipping...")
			}
			return nil
		}
	}

	// Get the tar FileInfoHeader in order to add the file to the archive.
	header, err := tar.FileInfoHeader(s, filepath.ToSlash(target))
	if err != nil {
		return errors.WrapIff(err, "failed to get tar#FileInfoHeader for '%s'", rp)
	}

	// Fix the header name if the file is not a symlink.
	if s.Mode()&os.ModeSymlink == 0 {
		header.Name = rp
	}

	// Write the tar FileInfoHeader to the archive.
	if err := w.WriteHeader(header); err != nil {
		return errors.WrapIff(err, "failed to write tar#FileInfoHeader for '%s'", rp)
	}

	// If the size of the file is less than 1 (most likely for symlinks), skip writing the file.
	if header.Size < 1 {
		return nil
	}

	// If the buffer size is larger than the file size, create a smaller buffer to hold the file.
	var buf []byte
	if header.Size < memory {
		buf = make([]byte, header.Size)
	} else {
		// Get a fixed-size buffer from the pool to save on allocations.
		buf = pool.Get().([]byte)
		defer func() {
			buf = make([]byte, memory)
			pool.Put(buf)
		}()
	}

	// Open the file.
	f, err := os.Open(p)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return errors.WrapIff(err, "failed to open '%s' for copying", header.Name)
	}
	defer f.Close()

	// Copy the file's contents to the archive using our buffer.
	if _, err := io.CopyBuffer(w, io.LimitReader(f, header.Size), buf); err != nil {
		return errors.WrapIff(err, "failed to copy '%s' to archive", header.Name)
	}

	return nil
}
<|endoftext|>