<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

package hawk

import (
	"bytes"
	"encoding/hex"
	"testing"
)

func Test_parseParameters(t *testing.T) {
	parameters, err := parseParameters(`id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", hash="Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=", ext="some-app-ext-data", mac="aSe1DERmZuRl3pI36/9BdZmnErTw3sNzOOAUlfeKjVw="`)
	if err != nil {
		t.Error("Cannot parse Hawk parameters", err)
	}

	if parameters.Id != "dh37fgj492je" {
		t.Error("id mismatch")
	}

	if parameters.Timestamp != 1353832234 {
		t.Error("ts mismatch")
	}

	if parameters.Nonce != "j4h3g2" {
		t.Error("nonce mismatch")
	}

	if parameters.Ext != "some-app-ext-data" {
		t.Error("ext mismatch")
	}

	expectedHash, _ := hex.DecodeString("622f4b7c820546d0443edef83d599b4c5ff1540c0f9fbb9bd7978f2027e09ee6")
	if !bytes.Equal(parameters.Hash, expectedHash) {
		t.Error("mac mismatch")
	}

	expectedMac, _ := hex.DecodeString("6927b50c446666e465de9237ebff417599a712b4f0dec37338e01495f78a8d5c")
	if !bytes.Equal(parameters.Mac, expectedMac) {
		t.Error("mac mismatch")
	}
}
<commit_msg>More Hawk tests<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

package hawk

import (
	"bytes"
	"encoding/hex"
	"net/http"
	"strings"
	"testing"
)

func Test_parseParameters(t *testing.T) {
	parameters, err := parseParameters(`id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", hash="Yi9LfIIFRtBEPt74PVmbTF/xVAwPn7ub15ePICfgnuY=", ext="some-app-ext-data", mac="aSe1DERmZuRl3pI36/9BdZmnErTw3sNzOOAUlfeKjVw="`)
	if err != nil {
		t.Error("Cannot parse Hawk parameters", err)
	}

	if parameters.Id != "dh37fgj492je" {
		t.Error("id mismatch")
	}

	if parameters.Timestamp != 1353832234 {
		t.Error("ts mismatch")
	}

	if parameters.Nonce != "j4h3g2" {
		t.Error("nonce mismatch")
	}

	if parameters.Ext != "some-app-ext-data" {
		t.Error("ext mismatch")
	}

	expectedHash, _ := hex.DecodeString("622f4b7c820546d0443edef83d599b4c5ff1540c0f9fbb9bd7978f2027e09ee6")
	if !bytes.Equal(parameters.Hash, expectedHash) {
		t.Error("mac mismatch")
	}

	expectedMac, _ := hex.DecodeString("6927b50c446666e465de9237ebff417599a712b4f0dec37338e01495f78a8d5c")
	if !bytes.Equal(parameters.Mac, expectedMac) {
		t.Error("mac mismatch")
	}
}

func Test_validateParameters(t *testing.T) {
}

func Test_getRequestHost(t *testing.T) {
	test := func(url string, expectedHost string, headers map[string]string) {
		request, err := http.NewRequest("GET", url, nil)
		if err != nil {
			t.Error(err)
		}
		host := getRequestHost(request)
		if host != expectedHost {
			t.Errorf("Expected host %s for %s but got %s", expectedHost, url, host)
		}
	}
	test("http://localhost/foo", "localhost", nil)
	test("https://127.0.0.1/foo", "127.0.0.1", nil)
	test("http://localhost:8080/foo", "localhost", nil)
	test("https://127.0.0.1:8443/foo", "127.0.0.1", nil)
	// TODO: Add tests here that mimic a typical front proxy (X-Forwarded-Proto?)
	test("http://localhost/foo", "localhost", map[string]string{})
	test("https://127.0.0.1/foo", "127.0.0.1", map[string]string{})
	test("http://localhost:8080/foo", "localhost", map[string]string{})
	test("https://127.0.0.1:8443/foo", "127.0.0.1", map[string]string{})
}

func Test_getRequestPort(t *testing.T) {
	test := func(url string, expectedPort int) {
		request, err := http.NewRequest("GET", url, nil)
		if err != nil {
			t.Error(err)
		}
		port := getRequestPort(request)
		if port != expectedPort {
			t.Errorf("Expected port %d for %s but got %d", expectedPort, url, port)
		}
	}
	test("http://localhost/foo", 80)
	test("https://localhost/foo", 443)
	test("http://localhost:8080/foo", 8080)
	test("https://localhost:8443/foo", 8443)
}

func Test_calculatePayloadHash(t *testing.T) {
	r, err := http.NewRequest("POST", "http://localhost", strings.NewReader("Thank you for flying Hawk"))
	if err != nil {
		t.Error(err)
	}
	r.Header.Add("Content-Type", "text/plain")
	hash, err := calculatePayloadHash(r)
	if err != nil {
		t.Error(err)
	}
	expectedHash, _ := hex.DecodeString("622f4b7c820546d0443edef83d599b4c5ff1540c0f9fbb9bd7978f2027e09ee6")
	if !bytes.Equal(hash, expectedHash) {
		t.Error("Hash mismatch")
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2022 The Kubernetes Authors.

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

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

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

package main

import (
	"log"
	"net/http"
	"os"
	"regexp"
	"strings"
)

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Printf("listening on port %s", port)
	http.ListenAndServe(":"+port, http.HandlerFunc(handler))
}

func handler(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	switch {
	case strings.HasPrefix(path, "/v2/"):
		doV2(w, r)
	case strings.HasPrefix(path, "/v1/"):
		doV1(w, r)
	default:
		log.Printf("unknown request: %q", path)
		http.NotFound(w, r)
	}
}

var reBlob = regexp.MustCompile("^/v2/.*/blobs/sha256:[0-9a-f]{64}$")

func doV2(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path

	if reBlob.MatchString(path) {
		// Blob requests are the fun ones.
		log.Printf("v2 blob request: %q", path)
		//FIXME: look up the best backend
		http.Redirect(w, r, "https://public.ecr.aws"+path, http.StatusTemporaryRedirect)
		return
	}

	// Anything else (manifests in particular) go to the canonical registry.
	log.Printf("v2 request: %q", path)
	http.Redirect(w, r, "https://index.docker.io"+path, http.StatusPermanentRedirect)
}

func doV1(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	log.Printf("v1 request: %q", path)
	//FIXME: look up backend?
	http.Redirect(w, r, "https://index.docker.io"+path, http.StatusPermanentRedirect)
}
<commit_msg>fix lint(s) so we can start enforcing linter<commit_after>/*
Copyright 2022 The Kubernetes Authors.

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

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

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

package main

import (
	"log"
	"net/http"
	"os"
	"regexp"
	"strings"
)

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Printf("listening on port %s", port)
	if err := http.ListenAndServe(":"+port, http.HandlerFunc(handler)); err != nil {
		log.Fatal(err)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	switch {
	case strings.HasPrefix(path, "/v2/"):
		doV2(w, r)
	case strings.HasPrefix(path, "/v1/"):
		doV1(w, r)
	default:
		log.Printf("unknown request: %q", path)
		http.NotFound(w, r)
	}
}

var reBlob = regexp.MustCompile("^/v2/.*/blobs/sha256:[0-9a-f]{64}$")

func doV2(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path

	if reBlob.MatchString(path) {
		// Blob requests are the fun ones.
		log.Printf("v2 blob request: %q", path)
		//FIXME: look up the best backend
		http.Redirect(w, r, "https://public.ecr.aws"+path, http.StatusTemporaryRedirect)
		return
	}

	// Anything else (manifests in particular) go to the canonical registry.
	log.Printf("v2 request: %q", path)
	http.Redirect(w, r, "https://index.docker.io"+path, http.StatusPermanentRedirect)
}

func doV1(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	log.Printf("v1 request: %q", path)
	//FIXME: look up backend?
	http.Redirect(w, r, "https://index.docker.io"+path, http.StatusPermanentRedirect)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
	"syscall"

	"github.com/containers/buildah"
	"github.com/containers/buildah/pkg/cli"
	"github.com/containers/buildah/pkg/parse"
	"github.com/containers/storage"
	"github.com/containers/storage/pkg/unshare"
	ispecs "github.com/opencontainers/image-spec/specs-go"
	rspecs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

type globalFlags struct {
	Debug             bool
	LogLevel          string
	Root              string
	RunRoot           string
	StorageDriver     string
	RegistriesConf    string
	RegistriesConfDir string
	DefaultMountsFile string
	StorageOpts       []string
	UserNSUID         []string
	UserNSGID         []string
}

var rootCmd = &cobra.Command{
	Use:  "buildah",
	Long: "A tool that facilitates building OCI images",
	RunE: func(cmd *cobra.Command, args []string) error {
		return cmd.Help()
	},
	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
		return before(cmd)
	},
	PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
		return after(cmd)
	},
	SilenceUsage:  true,
	SilenceErrors: true,
}

var (
	globalFlagResults globalFlags
)

func init() {
	logrus.SetFormatter(&logrus.TextFormatter{DisableTimestamp: true})

	var (
		defaultStoreDriverOptions []string
	)
	storageOptions, err := storage.DefaultStoreOptions(false, 0)
	if err != nil {
		logrus.Errorf(err.Error())
		os.Exit(1)

	}

	if len(storageOptions.GraphDriverOptions) > 0 {
		optionSlice := storageOptions.GraphDriverOptions[:]
		defaultStoreDriverOptions = optionSlice
	}

	cobra.OnInitialize(initConfig)
	//rootCmd.TraverseChildren = true
	rootCmd.Version = fmt.Sprintf("%s (image-spec %s, runtime-spec %s)", buildah.Version, ispecs.Version, rspecs.Version)
	rootCmd.PersistentFlags().BoolVar(&globalFlagResults.Debug, "debug", false, "print debugging information")
	// TODO Need to allow for environment variable
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.RegistriesConf, "registries-conf", "", "path to registries.conf file (not usually used)")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.RegistriesConfDir, "registries-conf-dir", "", "path to registries.conf.d directory (not usually used)")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.Root, "root", storageOptions.GraphRoot, "storage root dir")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.RunRoot, "runroot", storageOptions.RunRoot, "storage state dir")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.StorageDriver, "storage-driver", storageOptions.GraphDriverName, "storage-driver")
	rootCmd.PersistentFlags().StringSliceVar(&globalFlagResults.StorageOpts, "storage-opt", defaultStoreDriverOptions, "storage driver option")
	rootCmd.PersistentFlags().StringSliceVar(&globalFlagResults.UserNSUID, "userns-uid-map", []string{}, "default `ctrID:hostID:length` UID mapping to use")
	rootCmd.PersistentFlags().StringSliceVar(&globalFlagResults.UserNSGID, "userns-gid-map", []string{}, "default `ctrID:hostID:length` GID mapping to use")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.DefaultMountsFile, "default-mounts-file", "", "path to default mounts file")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.LogLevel, logLevel, "error", `The log level to be used. Either "debug", "info", "warn" or "error".`)

	if err := rootCmd.PersistentFlags().MarkHidden("debug"); err != nil {
		logrus.Fatalf("unable to mark debug flag as hidden: %v", err)
	}
	if err := rootCmd.PersistentFlags().MarkHidden("default-mounts-file"); err != nil {
		logrus.Fatalf("unable to mark default-mounts-file flag as hidden: %v", err)
	}
}

func initConfig() {
	// TODO Cobra allows us to do extra stuff here at init
	// time if we ever want to take advantage.
}

const logLevel = "log-level"

func before(cmd *cobra.Command) error {
	strLvl, err := cmd.Flags().GetString(logLevel)
	if err != nil {
		return err
	}
	logrusLvl, err := logrus.ParseLevel(strLvl)
	if err != nil {
		return errors.Wrapf(err, "unable to parse log level")
	}
	logrus.SetLevel(logrusLvl)
	if globalFlagResults.Debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	switch cmd.Use {
	case "", "help", "version", "mount":
		return nil
	}
	unshare.MaybeReexecUsingUserNamespace(false)
	return nil
}

func after(cmd *cobra.Command) error {
	if needToShutdownStore {
		store, err := getStore(cmd)
		if err != nil {
			return err
		}
		_, _ = store.Shutdown(false)
	}
	return nil
}

func main() {
	if buildah.InitReexec() {
		return
	}

	// Hard code TMPDIR functions to use $TMPDIR or /var/tmp
	os.Setenv("TMPDIR", parse.GetTempDir())

	if err := rootCmd.Execute(); err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err)
		exitCode := cli.ExecErrorCodeGeneric
		if ee, ok := (errors.Cause(err)).(*exec.ExitError); ok {
			if w, ok := ee.Sys().(syscall.WaitStatus); ok {
				exitCode = w.ExitStatus()
			}
		}
		os.Exit(exitCode)
	}
}
<commit_msg>Add preliminary profiling support to the CLI<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"runtime/pprof"
	"syscall"

	"github.com/containers/buildah"
	"github.com/containers/buildah/pkg/cli"
	"github.com/containers/buildah/pkg/parse"
	"github.com/containers/storage"
	"github.com/containers/storage/pkg/unshare"
	ispecs "github.com/opencontainers/image-spec/specs-go"
	rspecs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

type globalFlags struct {
	Debug             bool
	LogLevel          string
	Root              string
	RunRoot           string
	StorageDriver     string
	RegistriesConf    string
	RegistriesConfDir string
	DefaultMountsFile string
	StorageOpts       []string
	UserNSUID         []string
	UserNSGID         []string
	CPUProfile        string
	cpuProfileFile    *os.File
	MemoryProfile     string
}

var rootCmd = &cobra.Command{
	Use:  "buildah",
	Long: "A tool that facilitates building OCI images",
	RunE: func(cmd *cobra.Command, args []string) error {
		return cmd.Help()
	},
	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
		return before(cmd)
	},
	PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
		return after(cmd)
	},
	SilenceUsage:  true,
	SilenceErrors: true,
}

var (
	globalFlagResults globalFlags
)

func init() {
	logrus.SetFormatter(&logrus.TextFormatter{DisableTimestamp: true})

	var (
		defaultStoreDriverOptions []string
	)
	storageOptions, err := storage.DefaultStoreOptions(false, 0)
	if err != nil {
		logrus.Errorf(err.Error())
		os.Exit(1)

	}

	if len(storageOptions.GraphDriverOptions) > 0 {
		optionSlice := storageOptions.GraphDriverOptions[:]
		defaultStoreDriverOptions = optionSlice
	}

	cobra.OnInitialize(initConfig)
	//rootCmd.TraverseChildren = true
	rootCmd.Version = fmt.Sprintf("%s (image-spec %s, runtime-spec %s)", buildah.Version, ispecs.Version, rspecs.Version)
	rootCmd.PersistentFlags().BoolVar(&globalFlagResults.Debug, "debug", false, "print debugging information")
	// TODO Need to allow for environment variable
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.RegistriesConf, "registries-conf", "", "path to registries.conf file (not usually used)")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.RegistriesConfDir, "registries-conf-dir", "", "path to registries.conf.d directory (not usually used)")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.Root, "root", storageOptions.GraphRoot, "storage root dir")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.RunRoot, "runroot", storageOptions.RunRoot, "storage state dir")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.StorageDriver, "storage-driver", storageOptions.GraphDriverName, "storage-driver")
	rootCmd.PersistentFlags().StringSliceVar(&globalFlagResults.StorageOpts, "storage-opt", defaultStoreDriverOptions, "storage driver option")
	rootCmd.PersistentFlags().StringSliceVar(&globalFlagResults.UserNSUID, "userns-uid-map", []string{}, "default `ctrID:hostID:length` UID mapping to use")
	rootCmd.PersistentFlags().StringSliceVar(&globalFlagResults.UserNSGID, "userns-gid-map", []string{}, "default `ctrID:hostID:length` GID mapping to use")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.DefaultMountsFile, "default-mounts-file", "", "path to default mounts file")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.LogLevel, logLevel, "error", `The log level to be used. Either "debug", "info", "warn" or "error".`)
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.CPUProfile, "cpu-profile", "", "`file` to write CPU profile")
	rootCmd.PersistentFlags().StringVar(&globalFlagResults.MemoryProfile, "memory-profile", "", "`file` to write memory profile")

	if err := rootCmd.PersistentFlags().MarkHidden("cpu-profile"); err != nil {
		logrus.Fatalf("unable to mark cpu-profile flag as hidden: %v", err)
	}
	if err := rootCmd.PersistentFlags().MarkHidden("debug"); err != nil {
		logrus.Fatalf("unable to mark debug flag as hidden: %v", err)
	}
	if err := rootCmd.PersistentFlags().MarkHidden("default-mounts-file"); err != nil {
		logrus.Fatalf("unable to mark default-mounts-file flag as hidden: %v", err)
	}
	if err := rootCmd.PersistentFlags().MarkHidden("memory-profile"); err != nil {
		logrus.Fatalf("unable to mark memory-profile flag as hidden: %v", err)
	}
}

func initConfig() {
	// TODO Cobra allows us to do extra stuff here at init
	// time if we ever want to take advantage.
}

const logLevel = "log-level"

func before(cmd *cobra.Command) error {
	strLvl, err := cmd.Flags().GetString(logLevel)
	if err != nil {
		return err
	}
	logrusLvl, err := logrus.ParseLevel(strLvl)
	if err != nil {
		return errors.Wrapf(err, "unable to parse log level")
	}
	logrus.SetLevel(logrusLvl)
	if globalFlagResults.Debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	switch cmd.Use {
	case "", "help", "version", "mount":
		return nil
	}
	unshare.MaybeReexecUsingUserNamespace(false)
	if globalFlagResults.CPUProfile != "" {
		globalFlagResults.cpuProfileFile, err = os.Create(globalFlagResults.CPUProfile)
		if err != nil {
			logrus.Fatalf("could not create CPU profile %s: %v", globalFlagResults.CPUProfile, err)
		}
		if err = pprof.StartCPUProfile(globalFlagResults.cpuProfileFile); err != nil {
			logrus.Fatalf("error starting CPU profiling: %v", err)
		}
	}
	return nil
}

func after(cmd *cobra.Command) error {
	if needToShutdownStore {
		store, err := getStore(cmd)
		if err != nil {
			return err
		}
		_, _ = store.Shutdown(false)
	}
	if globalFlagResults.CPUProfile != "" {
		pprof.StopCPUProfile()
		globalFlagResults.cpuProfileFile.Close()
	}
	if globalFlagResults.MemoryProfile != "" {
		memoryProfileFile, err := os.Create(globalFlagResults.MemoryProfile)
		if err != nil {
			logrus.Fatalf("could not create memory profile %s: %v", globalFlagResults.MemoryProfile, err)
		}
		defer memoryProfileFile.Close()
		runtime.GC()
		if err := pprof.Lookup("heap").WriteTo(memoryProfileFile, 1); err != nil {
			logrus.Fatalf("could not write memory profile %s: %v", globalFlagResults.MemoryProfile, err)
		}
	}
	return nil
}

func main() {
	if buildah.InitReexec() {
		return
	}

	// Hard code TMPDIR functions to use $TMPDIR or /var/tmp
	os.Setenv("TMPDIR", parse.GetTempDir())

	if err := rootCmd.Execute(); err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err)
		exitCode := cli.ExecErrorCodeGeneric
		if ee, ok := (errors.Cause(err)).(*exec.ExitError); ok {
			if w, ok := ee.Sys().(syscall.WaitStatus); ok {
				exitCode = w.ExitStatus()
			}
		}
		os.Exit(exitCode)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/containers/image/manifest"
	"github.com/containers/image/transports"
	"github.com/containers/image/transports/alltransports"
	"github.com/containers/storage/pkg/archive"
	imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
	"github.com/projectatomic/buildah"
	"github.com/projectatomic/buildah/pkg/parse"
	"github.com/projectatomic/buildah/util"
	"github.com/urfave/cli"
)

var (
	pushFlags = []cli.Flag{
		cli.StringFlag{
			Name:  "authfile",
			Usage: "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
		},
		cli.StringFlag{
			Name:  "cert-dir",
			Value: "",
			Usage: "use certificates at the specified path to access the registry",
		},
		cli.StringFlag{
			Name:  "creds",
			Value: "",
			Usage: "use `[username[:password]]` for accessing the registry",
		},
		cli.BoolFlag{
			Name:  "disable-compression, D",
			Usage: "don't compress layers",
		},
		cli.StringFlag{
			Name:  "format, f",
			Usage: "manifest type (oci, v2s1, or v2s2) to use when saving image using the 'dir:' transport (default is manifest type of source)",
		},
		cli.BoolFlag{
			Name:  "quiet, q",
			Usage: "don't output progress information when pushing images",
		},
		cli.StringFlag{
			Name:  "signature-policy",
			Usage: "`pathname` of signature policy file (not usually used)",
		},
		cli.BoolTFlag{
			Name:  "tls-verify",
			Usage: "require HTTPS and verify certificates when accessing the registry",
		},
	}
	pushDescription = fmt.Sprintf(`
   Pushes an image to a specified location.

   The Image "DESTINATION" uses a "transport":"details" format.

   Supported transports:
   %s

   See buildah-push(1) section "DESTINATION" for the expected format
`, getListOfTransports())

	pushCommand = cli.Command{
		Name:           "push",
		Usage:          "Push an image to a specified destination",
		Description:    pushDescription,
		Flags:          pushFlags,
		Action:         pushCmd,
		ArgsUsage:      "IMAGE DESTINATION",
		SkipArgReorder: true,
	}
)

func pushCmd(c *cli.Context) error {
	args := c.Args()
	if len(args) < 2 {
		return errors.New("source and destination image IDs must be specified")
	}
	if err := parse.ValidateFlags(c, pushFlags); err != nil {
		return err
	}
	src := args[0]
	destSpec := args[1]

	compress := archive.Gzip
	if c.Bool("disable-compression") {
		compress = archive.Uncompressed
	}

	store, err := getStore(c)
	if err != nil {
		return err
	}

	dest, err := alltransports.ParseImageName(destSpec)
	// add the docker:// transport to see if they neglected it.
	if err != nil {
		if strings.Contains(destSpec, "://") {
			return err
		}

		destSpec = "docker://" + destSpec
		dest2, err2 := alltransports.ParseImageName(destSpec)
		if err2 != nil {
			return err
		}
		dest = dest2
	}

	systemContext, err := parse.SystemContextFromOptions(c)
	if err != nil {
		return errors.Wrapf(err, "error building system context")
	}

	var manifestType string
	if c.IsSet("format") {
		switch c.String("format") {
		case "oci":
			manifestType = imgspecv1.MediaTypeImageManifest
		case "v2s1":
			manifestType = manifest.DockerV2Schema1SignedMediaType
		case "v2s2", "docker":
			manifestType = manifest.DockerV2Schema2MediaType
		default:
			return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", c.String("format"))
		}
	}

	options := buildah.PushOptions{
		Compression:         compress,
		ManifestType:        manifestType,
		SignaturePolicyPath: c.String("signature-policy"),
		Store:               store,
		SystemContext:       systemContext,
	}
	if !c.Bool("quiet") {
		options.ReportWriter = os.Stderr
	}

	err = buildah.Push(getContext(), src, dest, options)
	if err != nil {
		return util.GetFailureCause(
			err,
			errors.Wrapf(err, "error pushing image %q to %q", src, destSpec),
		)
	}

	return nil
}

// getListOfTransports gets the transports supported from the image library
// and strips of the "tarball" transport from the string of transports returned
func getListOfTransports() string {
	allTransports := strings.Join(transports.ListNames(), ",")
	return strings.Replace(allTransports, ",tarball", "", 1)
}
<commit_msg>Report errors on bad transports specification when pushing images<commit_after>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/containers/image/manifest"
	"github.com/containers/image/transports"
	"github.com/containers/image/transports/alltransports"
	"github.com/containers/storage/pkg/archive"
	imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
	"github.com/projectatomic/buildah"
	"github.com/projectatomic/buildah/pkg/parse"
	"github.com/projectatomic/buildah/util"
	"github.com/urfave/cli"
)

var (
	pushFlags = []cli.Flag{
		cli.StringFlag{
			Name:  "authfile",
			Usage: "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
		},
		cli.StringFlag{
			Name:  "cert-dir",
			Value: "",
			Usage: "use certificates at the specified path to access the registry",
		},
		cli.StringFlag{
			Name:  "creds",
			Value: "",
			Usage: "use `[username[:password]]` for accessing the registry",
		},
		cli.BoolFlag{
			Name:  "disable-compression, D",
			Usage: "don't compress layers",
		},
		cli.StringFlag{
			Name:  "format, f",
			Usage: "manifest type (oci, v2s1, or v2s2) to use when saving image using the 'dir:' transport (default is manifest type of source)",
		},
		cli.BoolFlag{
			Name:  "quiet, q",
			Usage: "don't output progress information when pushing images",
		},
		cli.StringFlag{
			Name:  "signature-policy",
			Usage: "`pathname` of signature policy file (not usually used)",
		},
		cli.BoolTFlag{
			Name:  "tls-verify",
			Usage: "require HTTPS and verify certificates when accessing the registry",
		},
	}
	pushDescription = fmt.Sprintf(`
   Pushes an image to a specified location.

   The Image "DESTINATION" uses a "transport":"details" format.

   Supported transports:
   %s

   See buildah-push(1) section "DESTINATION" for the expected format
`, getListOfTransports())

	pushCommand = cli.Command{
		Name:           "push",
		Usage:          "Push an image to a specified destination",
		Description:    pushDescription,
		Flags:          pushFlags,
		Action:         pushCmd,
		ArgsUsage:      "IMAGE DESTINATION",
		SkipArgReorder: true,
	}
)

func pushCmd(c *cli.Context) error {
	args := c.Args()
	if len(args) < 2 {
		return errors.New("source and destination image IDs must be specified")
	}
	if err := parse.ValidateFlags(c, pushFlags); err != nil {
		return err
	}
	src := args[0]
	destSpec := args[1]

	compress := archive.Gzip
	if c.Bool("disable-compression") {
		compress = archive.Uncompressed
	}

	store, err := getStore(c)
	if err != nil {
		return err
	}

	dest, err := alltransports.ParseImageName(destSpec)
	// add the docker:// transport to see if they neglected it.
	if err != nil {
		destTransport := strings.Split(destSpec, ":")[0]
		if t := transports.Get(destTransport); t != nil {
			return err
		}

		if strings.Contains(destSpec, "://") {
			return err
		}

		destSpec = "docker://" + destSpec
		dest2, err2 := alltransports.ParseImageName(destSpec)
		if err2 != nil {
			return err
		}
		dest = dest2
	}

	systemContext, err := parse.SystemContextFromOptions(c)
	if err != nil {
		return errors.Wrapf(err, "error building system context")
	}

	var manifestType string
	if c.IsSet("format") {
		switch c.String("format") {
		case "oci":
			manifestType = imgspecv1.MediaTypeImageManifest
		case "v2s1":
			manifestType = manifest.DockerV2Schema1SignedMediaType
		case "v2s2", "docker":
			manifestType = manifest.DockerV2Schema2MediaType
		default:
			return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", c.String("format"))
		}
	}

	options := buildah.PushOptions{
		Compression:         compress,
		ManifestType:        manifestType,
		SignaturePolicyPath: c.String("signature-policy"),
		Store:               store,
		SystemContext:       systemContext,
	}
	if !c.Bool("quiet") {
		options.ReportWriter = os.Stderr
	}

	err = buildah.Push(getContext(), src, dest, options)
	if err != nil {
		return util.GetFailureCause(
			err,
			errors.Wrapf(err, "error pushing image %q to %q", src, destSpec),
		)
	}

	return nil
}

// getListOfTransports gets the transports supported from the image library
// and strips of the "tarball" transport from the string of transports returned
func getListOfTransports() string {
	allTransports := strings.Join(transports.ListNames(), ",")
	return strings.Replace(allTransports, ",tarball", "", 1)
}
<|endoftext|>


<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package cmd

import (
	"bytes"
	"io/ioutil"
	"os"
	"regexp"
	"testing"

	"github.com/pmezard/go-difflib/difflib"
	"github.com/spf13/afero"
	"github.com/stretchr/testify/assert"
)

const testHAR = `
{
	"log": {
		"version": "1.2",
		"creator": {
		"name": "WebInspector",
		"version": "537.36"
		},
		"pages": [
		{
			"startedDateTime": "2018-01-21T19:48:40.432Z",
			"id": "page_2",
			"title": "https://golang.org/",
			"pageTimings": {
			"onContentLoad": 590.3389999875799,
			"onLoad": 1593.1009999476373
			}
		}
		],
		"entries": [
		{
			"startedDateTime": "2018-01-21T19:48:40.587Z",
			"time": 147.5899999756366,
			"request": {
				"method": "GET",
				"url": "https://golang.org/",
				"httpVersion": "http/2.0+quic/39",
				"headers": [
					{
					"name": "pragma",
					"value": "no-cache"
					}
				],
				"queryString": [],
				"cookies": [],
				"headersSize": -1,
				"bodySize": 0
			},
			"cache": {},
			"timings": {
				"blocked": 0.43399997614324004,
				"dns": -1,
				"ssl": -1,
				"connect": -1,
				"send": 0.12700003571808005,
				"wait": 149.02899996377528,
				"receive": 0,
				"_blocked_queueing": -1
			},
			"serverIPAddress": "172.217.22.177",
			"pageref": "page_2"
		}
		]
	}
}
`

const testHARConvertResult = `import { group, sleep } from 'k6';
import http from 'k6/http';

// Version: 1.2
// Creator: WebInspector

export let options = {
    maxRedirects: 0,
};

export default function() {

	group("page_2 - https://golang.org/", function() {
		let req, res;
		req = [{
			"method": "get",
			"url": "https://golang.org/",
			"params": {
				"headers": {
					"pragma": "no-cache"
				}
			}
		}];
		res = http.batch(req);
		// Random sleep between 20s and 40s
		sleep(Math.floor(Math.random()*20+20));
	});

}
`

func TestIntegrationConvertCmd(t *testing.T) {
	var tmpFile, err = ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("Couldn't create temporary file: %s", err)
	}
	harFile := tmpFile.Name()
	defer os.Remove(harFile)
	tmpFile.Close()
	t.Run("Correlate", func(t *testing.T) {
		har, err := ioutil.ReadFile("testdata/example.har")
		assert.NoError(t, err)

		expectedTestPlan, err := ioutil.ReadFile("testdata/example.js")
		assert.NoError(t, err)

		defaultFs = afero.NewMemMapFs()

		err = afero.WriteFile(defaultFs, harFile, har, 0644)
		assert.NoError(t, err)

		buf := &bytes.Buffer{}
		defaultWriter = buf

		assert.NoError(t, convertCmd.Flags().Set("correlate", "true"))
		assert.NoError(t, convertCmd.Flags().Set("no-batch", "true"))
		assert.NoError(t, convertCmd.Flags().Set("enable-status-code-checks", "true"))
		assert.NoError(t, convertCmd.Flags().Set("return-on-failed-check", "true"))

		err = convertCmd.RunE(convertCmd, []string{harFile})

		// reset the convertCmd to default flags. There must be a nicer and less error prone way to do this...
		assert.NoError(t, convertCmd.Flags().Set("correlate", "false"))
		assert.NoError(t, convertCmd.Flags().Set("no-batch", "false"))
		assert.NoError(t, convertCmd.Flags().Set("enable-status-code-checks", "false"))
		assert.NoError(t, convertCmd.Flags().Set("return-on-failed-check", "false"))

		//Sanitizing to avoid windows problems with carriage returns
		re := regexp.MustCompile(`\r`)
		expected := re.ReplaceAllString(string(expectedTestPlan), ``)
		result := re.ReplaceAllString(buf.String(), ``)

		if assert.NoError(t, err) {
			// assert.Equal suppresses the diff it is too big, so we add it as the test error message manually as well.
			diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
				A:        difflib.SplitLines(expected),
				B:        difflib.SplitLines(result),
				FromFile: "Expected",
				FromDate: "",
				ToFile:   "Actual",
				ToDate:   "",
				Context:  1,
			})

			assert.Equal(t, expected, result, diff)
		}
	})
	t.Run("Stdout", func(t *testing.T) {
		defaultFs = afero.NewMemMapFs()
		err := afero.WriteFile(defaultFs, harFile, []byte(testHAR), 0644)
		assert.NoError(t, err)

		buf := &bytes.Buffer{}
		defaultWriter = buf

		err = convertCmd.RunE(convertCmd, []string{harFile})
		assert.NoError(t, err)
		assert.Equal(t, testHARConvertResult, buf.String())
	})
	t.Run("Output file", func(t *testing.T) {
		defaultFs = afero.NewMemMapFs()
		err := afero.WriteFile(defaultFs, harFile, []byte(testHAR), 0644)
		assert.NoError(t, err)

		err = convertCmd.Flags().Set("output", "/output.js")
		assert.NoError(t, err)
		err = convertCmd.RunE(convertCmd, []string{harFile})
		assert.NoError(t, err)

		output, err := afero.ReadFile(defaultFs, "/output.js")
		assert.NoError(t, err)
		assert.Equal(t, testHARConvertResult, string(output))
	})
	// TODO: test options injection; right now that's difficult because when there are multiple
	// options, they can be emitted in different order in the JSON
}
<commit_msg>refactor TestIntegrationConvertCmd to be more stable (#1484)<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package cmd

import (
	"bytes"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"testing"

	"github.com/pmezard/go-difflib/difflib"
	"github.com/spf13/afero"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const testHAR = `
{
	"log": {
		"version": "1.2",
		"creator": {
		"name": "WebInspector",
		"version": "537.36"
		},
		"pages": [
		{
			"startedDateTime": "2018-01-21T19:48:40.432Z",
			"id": "page_2",
			"title": "https://golang.org/",
			"pageTimings": {
			"onContentLoad": 590.3389999875799,
			"onLoad": 1593.1009999476373
			}
		}
		],
		"entries": [
		{
			"startedDateTime": "2018-01-21T19:48:40.587Z",
			"time": 147.5899999756366,
			"request": {
				"method": "GET",
				"url": "https://golang.org/",
				"httpVersion": "http/2.0+quic/39",
				"headers": [
					{
					"name": "pragma",
					"value": "no-cache"
					}
				],
				"queryString": [],
				"cookies": [],
				"headersSize": -1,
				"bodySize": 0
			},
			"cache": {},
			"timings": {
				"blocked": 0.43399997614324004,
				"dns": -1,
				"ssl": -1,
				"connect": -1,
				"send": 0.12700003571808005,
				"wait": 149.02899996377528,
				"receive": 0,
				"_blocked_queueing": -1
			},
			"serverIPAddress": "172.217.22.177",
			"pageref": "page_2"
		}
		]
	}
}
`

const testHARConvertResult = `import { group, sleep } from 'k6';
import http from 'k6/http';

// Version: 1.2
// Creator: WebInspector

export let options = {
    maxRedirects: 0,
};

export default function() {

	group("page_2 - https://golang.org/", function() {
		let req, res;
		req = [{
			"method": "get",
			"url": "https://golang.org/",
			"params": {
				"headers": {
					"pragma": "no-cache"
				}
			}
		}];
		res = http.batch(req);
		// Random sleep between 20s and 40s
		sleep(Math.floor(Math.random()*20+20));
	});

}
`

func TestIntegrationConvertCmd(t *testing.T) {
	t.Run("Correlate", func(t *testing.T) {
		harFile, err := filepath.Abs("correlate.har")
		require.NoError(t, err)
		har, err := ioutil.ReadFile("testdata/example.har")
		require.NoError(t, err)

		expectedTestPlan, err := ioutil.ReadFile("testdata/example.js")
		require.NoError(t, err)

		defaultFs = afero.NewMemMapFs()

		err = afero.WriteFile(defaultFs, harFile, har, 0644)
		require.NoError(t, err)

		buf := &bytes.Buffer{}
		defaultWriter = buf

		assert.NoError(t, convertCmd.Flags().Set("correlate", "true"))
		assert.NoError(t, convertCmd.Flags().Set("no-batch", "true"))
		assert.NoError(t, convertCmd.Flags().Set("enable-status-code-checks", "true"))
		assert.NoError(t, convertCmd.Flags().Set("return-on-failed-check", "true"))

		err = convertCmd.RunE(convertCmd, []string{harFile})

		// reset the convertCmd to default flags. There must be a nicer and less error prone way to do this...
		assert.NoError(t, convertCmd.Flags().Set("correlate", "false"))
		assert.NoError(t, convertCmd.Flags().Set("no-batch", "false"))
		assert.NoError(t, convertCmd.Flags().Set("enable-status-code-checks", "false"))
		assert.NoError(t, convertCmd.Flags().Set("return-on-failed-check", "false"))

		//Sanitizing to avoid windows problems with carriage returns
		re := regexp.MustCompile(`\r`)
		expected := re.ReplaceAllString(string(expectedTestPlan), ``)
		result := re.ReplaceAllString(buf.String(), ``)

		if assert.NoError(t, err) {
			// assert.Equal suppresses the diff it is too big, so we add it as the test error message manually as well.
			diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
				A:        difflib.SplitLines(expected),
				B:        difflib.SplitLines(result),
				FromFile: "Expected",
				FromDate: "",
				ToFile:   "Actual",
				ToDate:   "",
				Context:  1,
			})

			assert.Equal(t, expected, result, diff)
		}
	})
	t.Run("Stdout", func(t *testing.T) {
		harFile, err := filepath.Abs("stdout.har")
		require.NoError(t, err)
		defaultFs = afero.NewMemMapFs()
		err = afero.WriteFile(defaultFs, harFile, []byte(testHAR), 0644)
		assert.NoError(t, err)

		buf := &bytes.Buffer{}
		defaultWriter = buf

		err = convertCmd.RunE(convertCmd, []string{harFile})
		assert.NoError(t, err)
		assert.Equal(t, testHARConvertResult, buf.String())
	})
	t.Run("Output file", func(t *testing.T) {
		harFile, err := filepath.Abs("output.har")
		require.NoError(t, err)
		defaultFs = afero.NewMemMapFs()
		err = afero.WriteFile(defaultFs, harFile, []byte(testHAR), 0644)
		assert.NoError(t, err)

		err = convertCmd.Flags().Set("output", "/output.js")
		defer func() {
			err = convertCmd.Flags().Set("output", "")
		}()
		assert.NoError(t, err)
		err = convertCmd.RunE(convertCmd, []string{harFile})
		assert.NoError(t, err)

		output, err := afero.ReadFile(defaultFs, "/output.js")
		assert.NoError(t, err)
		assert.Equal(t, testHARConvertResult, string(output))
	})
	// TODO: test options injection; right now that's difficult because when there are multiple
	// options, they can be emitted in different order in the JSON
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/gorilla/mux"

	coreConfig "github.com/skygeario/skygear-server/pkg/core/config"
	"github.com/skygeario/skygear-server/pkg/core/db"
	"github.com/skygeario/skygear-server/pkg/core/logging"
	coreMiddleware "github.com/skygeario/skygear-server/pkg/core/middleware"
	"github.com/skygeario/skygear-server/pkg/core/server"
	"github.com/skygeario/skygear-server/pkg/gateway"
	gatewayConfig "github.com/skygeario/skygear-server/pkg/gateway/config"
	"github.com/skygeario/skygear-server/pkg/gateway/handler"
	"github.com/skygeario/skygear-server/pkg/gateway/middleware"
	"github.com/skygeario/skygear-server/pkg/gateway/provider"
	"github.com/skygeario/skygear-server/pkg/gateway/store"
	pqStore "github.com/skygeario/skygear-server/pkg/gateway/store/pq"
	standaloneStore "github.com/skygeario/skygear-server/pkg/gateway/store/standalone"
)

var config gatewayConfig.Configuration

func init() {
	// logging initialization
	logging.SetModule("gateway")

	logger := logging.LoggerEntry("gateway")
	if err := config.ReadFromEnv(); err != nil {
		logger.WithError(err).Panic(
			"Fail to load config for starting gateway server")
	}

	logger.WithField("config", config).Debug("Gateway config")
}

func main() {
	logger := logging.LoggerEntry("gateway")

	// create gateway store
	var store store.GatewayStore
	var connErr error
	if config.Standalone {
		filename := config.StandaloneTenantConfigurationFile
		tenantConfig, err := coreConfig.NewTenantConfigurationFromYAMLAndEnv(func() (io.Reader, error) {
			return os.Open(filename)
		})
		if err != nil {
			logger.WithError(err).Panic("Fail to load config")
		}
		store = &standaloneStore.Store{
			TenantConfig: *tenantConfig,
		}
	} else {
		store, connErr = pqStore.NewGatewayStore(
			context.Background(),
			config.ConnectionStr,
			logger,
		)
		if connErr != nil {
			logger.WithError(connErr).Panic("Fail to create db conn")
		}
	}
	defer store.Close()

	gatewayDependency := gateway.DependencyMap{}

	r := mux.NewRouter()
	r.HandleFunc("/healthz", HealthCheckHandler)

	r = r.PathPrefix("/").Subrouter()
	// RecoverMiddleware must come first
	r.Use(coreMiddleware.RecoverMiddleware{
		RecoverHandler: server.DefaultRecoverPanicHandler,
	}.Handle)

	r.Use(middleware.FindAppMiddleware{Store: store}.Handle)

	gr := r.PathPrefix("/_{gear}").Subrouter()

	gr.Use(coreMiddleware.TenantConfigurationMiddleware{
		ConfigurationProvider: provider.GatewayTenantConfigurationProvider{
			Store: store,
		},
	}.Handle)
	gr.Use(middleware.TenantAuthzMiddleware{
		Store:         store,
		Configuration: config,
	}.Handle)
	gr.Use(coreMiddleware.CORSMiddleware{}.Handle)

	gr.HandleFunc("/{rest:.*}", handler.NewGearHandler("rest"))

	cr := r.PathPrefix("/").Subrouter()

	cr.Use(func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			r = db.InitRequestDBContext(r)
			next.ServeHTTP(w, r)
		})
	})

	cr.Use(coreMiddleware.TenantConfigurationMiddleware{
		ConfigurationProvider: provider.GatewayTenantConfigurationProvider{
			Store: store,
		},
	}.Handle)

	cr.Use(middleware.FindDeploymentRouteMiddleware{
		RestPathIdentifier: "rest",
		Store:              store,
	}.Handle)

	cr.Use(middleware.Injecter{
		MiddlewareFactory: middleware.AuthInfoMiddlewareFactory{},
		Dependency:        gatewayDependency,
	}.Handle)
	cr.Use(coreMiddleware.CORSMiddleware{}.Handle)

	cr.HandleFunc("/{rest:.*}", handler.NewDeploymentRouteHandler())

	srv := &http.Server{
		Addr: config.Host,
		// Good practice to set timeouts to avoid Slowloris attacks.
		WriteTimeout: time.Second * 15,
		ReadTimeout:  time.Second * 15,
		IdleTimeout:  time.Second * 60,
		Handler:      r, // Pass our instance of gorilla/mux in.
	}

	logger.Info("Start gateway server")
	if err := srv.ListenAndServe(); err != nil {
		logger.Errorf("Fail to start gateway server %v", err)
	}
}

// HealthCheckHandler is basic handler for server health check
func HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Header().Set("Content-Type", "application/json")
	io.WriteString(w, "OK")
}
<commit_msg>Always apply RequestIDMiddleware in gateway<commit_after>package main

import (
	"context"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/gorilla/mux"

	coreConfig "github.com/skygeario/skygear-server/pkg/core/config"
	"github.com/skygeario/skygear-server/pkg/core/db"
	"github.com/skygeario/skygear-server/pkg/core/logging"
	coreMiddleware "github.com/skygeario/skygear-server/pkg/core/middleware"
	"github.com/skygeario/skygear-server/pkg/core/server"
	"github.com/skygeario/skygear-server/pkg/gateway"
	gatewayConfig "github.com/skygeario/skygear-server/pkg/gateway/config"
	"github.com/skygeario/skygear-server/pkg/gateway/handler"
	"github.com/skygeario/skygear-server/pkg/gateway/middleware"
	"github.com/skygeario/skygear-server/pkg/gateway/provider"
	"github.com/skygeario/skygear-server/pkg/gateway/store"
	pqStore "github.com/skygeario/skygear-server/pkg/gateway/store/pq"
	standaloneStore "github.com/skygeario/skygear-server/pkg/gateway/store/standalone"
)

var config gatewayConfig.Configuration

func init() {
	// logging initialization
	logging.SetModule("gateway")

	logger := logging.LoggerEntry("gateway")
	if err := config.ReadFromEnv(); err != nil {
		logger.WithError(err).Panic(
			"Fail to load config for starting gateway server")
	}

	logger.WithField("config", config).Debug("Gateway config")
}

func main() {
	logger := logging.LoggerEntry("gateway")

	// create gateway store
	var store store.GatewayStore
	var connErr error
	if config.Standalone {
		filename := config.StandaloneTenantConfigurationFile
		tenantConfig, err := coreConfig.NewTenantConfigurationFromYAMLAndEnv(func() (io.Reader, error) {
			return os.Open(filename)
		})
		if err != nil {
			logger.WithError(err).Panic("Fail to load config")
		}
		store = &standaloneStore.Store{
			TenantConfig: *tenantConfig,
		}
	} else {
		store, connErr = pqStore.NewGatewayStore(
			context.Background(),
			config.ConnectionStr,
			logger,
		)
		if connErr != nil {
			logger.WithError(connErr).Panic("Fail to create db conn")
		}
	}
	defer store.Close()

	gatewayDependency := gateway.DependencyMap{}

	r := mux.NewRouter()
	r.HandleFunc("/healthz", HealthCheckHandler)

	r = r.PathPrefix("/").Subrouter()
	// RecoverMiddleware must come first
	r.Use(coreMiddleware.RecoverMiddleware{
		RecoverHandler: server.DefaultRecoverPanicHandler,
	}.Handle)

	r.Use(coreMiddleware.RequestIDMiddleware{}.Handle)
	r.Use(middleware.FindAppMiddleware{Store: store}.Handle)

	gr := r.PathPrefix("/_{gear}").Subrouter()

	gr.Use(coreMiddleware.TenantConfigurationMiddleware{
		ConfigurationProvider: provider.GatewayTenantConfigurationProvider{
			Store: store,
		},
	}.Handle)
	gr.Use(middleware.TenantAuthzMiddleware{
		Store:         store,
		Configuration: config,
	}.Handle)
	gr.Use(coreMiddleware.CORSMiddleware{}.Handle)

	gr.HandleFunc("/{rest:.*}", handler.NewGearHandler("rest"))

	cr := r.PathPrefix("/").Subrouter()

	cr.Use(func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			r = db.InitRequestDBContext(r)
			next.ServeHTTP(w, r)
		})
	})

	cr.Use(coreMiddleware.TenantConfigurationMiddleware{
		ConfigurationProvider: provider.GatewayTenantConfigurationProvider{
			Store: store,
		},
	}.Handle)

	cr.Use(middleware.FindDeploymentRouteMiddleware{
		RestPathIdentifier: "rest",
		Store:              store,
	}.Handle)

	cr.Use(middleware.Injecter{
		MiddlewareFactory: middleware.AuthInfoMiddlewareFactory{},
		Dependency:        gatewayDependency,
	}.Handle)
	cr.Use(coreMiddleware.CORSMiddleware{}.Handle)

	cr.HandleFunc("/{rest:.*}", handler.NewDeploymentRouteHandler())

	srv := &http.Server{
		Addr: config.Host,
		// Good practice to set timeouts to avoid Slowloris attacks.
		WriteTimeout: time.Second * 15,
		ReadTimeout:  time.Second * 15,
		IdleTimeout:  time.Second * 60,
		Handler:      r, // Pass our instance of gorilla/mux in.
	}

	logger.Info("Start gateway server")
	if err := srv.ListenAndServe(); err != nil {
		logger.Errorf("Fail to start gateway server %v", err)
	}
}

// HealthCheckHandler is basic handler for server health check
func HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Header().Set("Content-Type", "application/json")
	io.WriteString(w, "OK")
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"strings"

	platform "github.com/influxdata/influxdb"
	"github.com/influxdata/influxdb/http"
	"github.com/influxdata/influxdb/kit/signals"
	"github.com/influxdata/influxdb/models"
	"github.com/influxdata/influxdb/write"
	"github.com/spf13/cobra"
)

const (
	inputFormatCsv          = "csv"
	inputFormatLineProtocol = "lp"
)

var writeFlags struct {
	OrgID     string
	Org       string
	BucketID  string
	Bucket    string
	Precision string
	Format    string
	File      string
}

func cmdWrite(f *globalFlags, opt genericCLIOpts) *cobra.Command {
	cmd := opt.newCmd("write", fluxWriteF, true)
	cmd.Args = cobra.MaximumNArgs(1)
	cmd.Short = "Write points to InfluxDB"
	cmd.Long = `Write data to InfluxDB via stdin, or add an entire file specified with the -f flag`

	opts := flagOpts{
		{
			DestP:      &writeFlags.OrgID,
			Flag:       "org-id",
			Desc:       "The ID of the organization that owns the bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.Org,
			Flag:       "org",
			Short:      'o',
			Desc:       "The name of the organization that owns the bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.BucketID,
			Flag:       "bucket-id",
			Desc:       "The ID of destination bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.Bucket,
			Flag:       "bucket",
			Short:      'b',
			EnvVar:     "BUCKET_NAME",
			Desc:       "The name of destination bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.Precision,
			Flag:       "precision",
			Short:      'p',
			Default:    "ns",
			Desc:       "Precision of the timestamps of the lines",
			Persistent: true,
		},
	}
	opts.mustRegister(cmd)
	cmd.Flags().StringVar(&writeFlags.Format, "format", "", "Input format, either lp (Line Protocol) or csv (Comma Separated Values). Defaults to lp unless '.csv' extension")
	cmd.Flags().StringVarP(&writeFlags.File, "file", "f", "", "The path to the file to import")

	return cmd
}

func fluxWriteF(cmd *cobra.Command, args []string) error {
	ctx := context.Background()

	var bucketID, orgID platform.ID
	// validate flags unless writing to stdout
	if flags.Host != "-" {
		if writeFlags.Org != "" && writeFlags.OrgID != "" {
			return fmt.Errorf("please specify one of org or org-id")
		}

		if writeFlags.Bucket != "" && writeFlags.BucketID != "" {
			return fmt.Errorf("please specify one of bucket or bucket-id")
		}

		if !models.ValidPrecision(writeFlags.Precision) {
			return fmt.Errorf("invalid precision")
		}

		bs, err := newBucketService()
		if err != nil {
			return err
		}

		var filter platform.BucketFilter
		if writeFlags.BucketID != "" {
			filter.ID, err = platform.IDFromString(writeFlags.BucketID)
			if err != nil {
				return fmt.Errorf("failed to decode bucket-id: %v", err)
			}
		}
		if writeFlags.Bucket != "" {
			filter.Name = &writeFlags.Bucket
		}

		if writeFlags.OrgID != "" {
			filter.OrganizationID, err = platform.IDFromString(writeFlags.OrgID)
			if err != nil {
				return fmt.Errorf("failed to decode org-id id: %v", err)
			}
		}
		if writeFlags.Org != "" {
			filter.Org = &writeFlags.Org
		}

		buckets, n, err := bs.FindBuckets(ctx, filter)
		if err != nil {
			return fmt.Errorf("failed to retrieve buckets: %v", err)
		}

		if n == 0 {
			if writeFlags.Bucket != "" {
				return fmt.Errorf("bucket %q was not found", writeFlags.Bucket)
			}

			if writeFlags.BucketID != "" {
				return fmt.Errorf("bucket with id %q does not exist", writeFlags.BucketID)
			}
		}

		bucketID, orgID = buckets[0].ID, buckets[0].OrgID
	}

	var r io.Reader
	if len(args) > 0 && args[0][0] == '@' {
		// backward compatibility
		writeFlags.File = args[0][1:]
	}

	if len(writeFlags.File) > 0 {
		f, err := os.Open(writeFlags.File)
		if err != nil {
			return fmt.Errorf("failed to open %q: %v", writeFlags.File, err)
		}
		defer f.Close()
		r = f
		if len(writeFlags.Format) == 0 && strings.HasSuffix(writeFlags.File, ".csv") {
			writeFlags.Format = inputFormatCsv
		}
	} else if len(args) == 0 || args[0] == "-" {
		r = os.Stdin
	} else {
		r = strings.NewReader(args[0])
	}
	// validate input format
	if len(writeFlags.Format) > 0 && writeFlags.Format != inputFormatLineProtocol && writeFlags.Format != inputFormatCsv {
		return fmt.Errorf("unsupported input format: %s", writeFlags.Format)
	}

	if writeFlags.Format == inputFormatCsv {
		r = write.CsvToProtocolLines(r)
	}

	if flags.Host == "-" {
		// write lines to tdout
		_, err := io.Copy(os.Stdout, r)
		if err != nil {
			return fmt.Errorf("failed: %v", err)
		}
	} else {
		s := write.Batcher{
			Service: &http.WriteService{
				Addr:               flags.Host,
				Token:              flags.Token,
				Precision:          writeFlags.Precision,
				InsecureSkipVerify: flags.skipVerify,
			},
		}

		ctx = signals.WithStandardSignals(ctx)
		if err := s.Write(ctx, orgID, bucketID, r); err != nil && err != context.Canceled {
			return fmt.Errorf("failed to write data: %v", err)
		}
	}

	return nil
}
<commit_msg>feat(cmd/write): add dry-run flag<commit_after>package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"strings"

	platform "github.com/influxdata/influxdb"
	"github.com/influxdata/influxdb/http"
	"github.com/influxdata/influxdb/kit/signals"
	"github.com/influxdata/influxdb/models"
	"github.com/influxdata/influxdb/write"
	"github.com/spf13/cobra"
)

const (
	inputFormatCsv          = "csv"
	inputFormatLineProtocol = "lp"
)

var writeFlags struct {
	OrgID     string
	Org       string
	BucketID  string
	Bucket    string
	Precision string
	Format    string
	File      string
	DryRun    bool
}

func cmdWrite(f *globalFlags, opt genericCLIOpts) *cobra.Command {
	cmd := opt.newCmd("write", fluxWriteF, true)
	cmd.Args = cobra.MaximumNArgs(1)
	cmd.Short = "Write points to InfluxDB"
	cmd.Long = `Write data to InfluxDB via stdin, or add an entire file specified with the -f flag`

	opts := flagOpts{
		{
			DestP:      &writeFlags.OrgID,
			Flag:       "org-id",
			Desc:       "The ID of the organization that owns the bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.Org,
			Flag:       "org",
			Short:      'o',
			Desc:       "The name of the organization that owns the bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.BucketID,
			Flag:       "bucket-id",
			Desc:       "The ID of destination bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.Bucket,
			Flag:       "bucket",
			Short:      'b',
			EnvVar:     "BUCKET_NAME",
			Desc:       "The name of destination bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.Precision,
			Flag:       "precision",
			Short:      'p',
			Default:    "ns",
			Desc:       "Precision of the timestamps of the lines",
			Persistent: true,
		},
	}
	opts.mustRegister(cmd)
	cmd.Flags().StringVar(&writeFlags.Format, "format", "", "Input format, either lp (Line Protocol) or csv (Comma Separated Values). Defaults to lp unless '.csv' extension")
	cmd.Flags().StringVarP(&writeFlags.File, "file", "f", "", "The path to the file to import")
	cmd.Flags().BoolVar(&writeFlags.DryRun, "dry-run", false, "Write protocol lines to stdout")

	return cmd
}

func fluxWriteF(cmd *cobra.Command, args []string) error {
	ctx := context.Background()
	if writeFlags.DryRun {
		flags.Host = "-"
	}

	var bucketID, orgID platform.ID
	// validate flags unless writing to stdout
	if flags.Host != "-" {
		if writeFlags.Org != "" && writeFlags.OrgID != "" {
			return fmt.Errorf("please specify one of org or org-id")
		}

		if writeFlags.Bucket != "" && writeFlags.BucketID != "" {
			return fmt.Errorf("please specify one of bucket or bucket-id")
		}

		if !models.ValidPrecision(writeFlags.Precision) {
			return fmt.Errorf("invalid precision")
		}

		bs, err := newBucketService()
		if err != nil {
			return err
		}

		var filter platform.BucketFilter
		if writeFlags.BucketID != "" {
			filter.ID, err = platform.IDFromString(writeFlags.BucketID)
			if err != nil {
				return fmt.Errorf("failed to decode bucket-id: %v", err)
			}
		}
		if writeFlags.Bucket != "" {
			filter.Name = &writeFlags.Bucket
		}

		if writeFlags.OrgID != "" {
			filter.OrganizationID, err = platform.IDFromString(writeFlags.OrgID)
			if err != nil {
				return fmt.Errorf("failed to decode org-id id: %v", err)
			}
		}
		if writeFlags.Org != "" {
			filter.Org = &writeFlags.Org
		}

		buckets, n, err := bs.FindBuckets(ctx, filter)
		if err != nil {
			return fmt.Errorf("failed to retrieve buckets: %v", err)
		}

		if n == 0 {
			if writeFlags.Bucket != "" {
				return fmt.Errorf("bucket %q was not found", writeFlags.Bucket)
			}

			if writeFlags.BucketID != "" {
				return fmt.Errorf("bucket with id %q does not exist", writeFlags.BucketID)
			}
		}

		bucketID, orgID = buckets[0].ID, buckets[0].OrgID
	}

	var r io.Reader
	if len(args) > 0 && args[0][0] == '@' {
		// backward compatibility
		writeFlags.File = args[0][1:]
	}

	if len(writeFlags.File) > 0 {
		f, err := os.Open(writeFlags.File)
		if err != nil {
			return fmt.Errorf("failed to open %q: %v", writeFlags.File, err)
		}
		defer f.Close()
		r = f
		if len(writeFlags.Format) == 0 && strings.HasSuffix(writeFlags.File, ".csv") {
			writeFlags.Format = inputFormatCsv
		}
	} else if len(args) == 0 || args[0] == "-" {
		r = os.Stdin
	} else {
		r = strings.NewReader(args[0])
	}
	// validate input format
	if len(writeFlags.Format) > 0 && writeFlags.Format != inputFormatLineProtocol && writeFlags.Format != inputFormatCsv {
		return fmt.Errorf("unsupported input format: %s", writeFlags.Format)
	}

	if writeFlags.Format == inputFormatCsv {
		r = write.CsvToProtocolLines(r)
	}

	if flags.Host == "-" {
		// write lines to tdout
		_, err := io.Copy(os.Stdout, r)
		if err != nil {
			return fmt.Errorf("failed: %v", err)
		}
	} else {
		s := write.Batcher{
			Service: &http.WriteService{
				Addr:               flags.Host,
				Token:              flags.Token,
				Precision:          writeFlags.Precision,
				InsecureSkipVerify: flags.skipVerify,
			},
		}

		ctx = signals.WithStandardSignals(ctx)
		if err := s.Write(ctx, orgID, bucketID, r); err != nil && err != context.Canceled {
			return fmt.Errorf("failed to write data: %v", err)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package configurations

import (
	"fmt"
	"github.com/karlseguin/garnish/gc"
	"net"
	"net/http"
	"strings"
	"time"
)

// The default headers to forward to the upstream
var DefaultHeaders = []string{"Content-Length"}

type Upstreams struct {
	upstreams map[string]*Upstream
}

func NewUpstreams() *Upstreams {
	return &Upstreams{
		upstreams: make(map[string]*Upstream),
	}
}

// Used internally
func (u *Upstreams) Add(name string) *Upstream {
	if _, exists := u.upstreams[name]; exists {
		gc.Log.Warnf("Upstream %q already defined. Overwriting.", name)
	}
	one := &Upstream{
		name:        name,
		dnsDuration: time.Minute,
		headers:     DefaultHeaders,
		transports:  make([]*Transport, 0, 2),
	}
	u.upstreams[name] = one
	return one
}

func (u *Upstreams) Build(runtime *gc.Runtime, defaultTweaker gc.RequestTweaker) error {
	if u == nil {
		runtime.Upstreams = make(map[string]gc.Upstream, 0)
		return nil
	}

	upstreams := make(map[string]gc.Upstream, len(u.upstreams))
	for name, one := range u.upstreams {
		tweaker := defaultTweaker
		if len(one.tweakerRef) != 0 {
			ref, ok := u.upstreams[one.tweakerRef]
			if ok == false {
				return fmt.Errorf("upstream %s's tweaker referenced %s, a non-existent upstream", name, one.tweakerRef)
			}
			if ref.tweaker == nil {
				return fmt.Errorf("upstream %s's tweaker referenced %s, which does not have a tweaker", name, one.tweakerRef)
			}
			tweaker = ref.tweaker
		}
		upstream, err := one.Build(runtime, tweaker)
		if err != nil {
			return err
		}
		upstreams[name] = upstream
	}
	runtime.Upstreams = upstreams
	return nil
}

type Upstream struct {
	name        string
	transports  []*Transport
	dnsDuration time.Duration
	headers     []string
	tweakerRef  string
	tweaker     gc.RequestTweaker
}

type Transport struct {
	address   string
	keepalive int
}

// the duration to cache the upstream's dns lookup. Set to 0 to prevent
// garnish from caching this value (even a few seconds can help)
// [1 minute]
func (u *Upstream) DnsCache(duration time.Duration) *Upstream {
	u.dnsDuration = duration
	return u
}

// The headers to copy from the incoming request to the outgoing request
// [Content-Length]
func (u *Upstream) Headers(headers ...string) *Upstream {
	u.headers = headers
	return u
}

// Custom callback to modify the request (out) that will get sent to the upstream
func (u *Upstream) Tweaker(tweaker gc.RequestTweaker) *Upstream {
	u.tweaker = tweaker
	return u
}

// Allows the upstream to reference another upstream's tweaker (based on that
// upstream's name). Used by the file-based configuration; when configured
// programmatically, Tweaker should be used.
func (u *Upstream) TweakerRef(upstream string) *Upstream {
	u.tweakerRef = upstream
	return u
}

// the address to connect to. Should begin with unix:/  http://  or https://
// [""]
func (u *Upstream) Address(address string) *Transport {
	transport := &Transport{
		address:   address,
		keepalive: 16,
	}
	u.transports = append(u.transports, transport)
	return transport
}

// the number of connections to keep alive. Set to 0 to disable
// [16]
func (t *Transport) KeepAlive(count uint32) *Transport {
	t.keepalive = int(count)
	return t
}

func (u *Upstream) Build(runtime *gc.Runtime, tweaker gc.RequestTweaker) (gc.Upstream, error) {
	l := len(u.transports)
	if l == 0 {
		return nil, fmt.Errorf("Upstream %s doesn't have a configured transport", u.name)
	}

	transports := make([]*gc.Transport, l)
	for i := 0; i < l; i++ {
		t := u.transports[i]
		if len(t.address) < 8 {
			return nil, fmt.Errorf("Upstream %s has an invalid address: %q", u.name, t.address)
		}
		var domain string
		if t.address[:7] == "http://" {
			domain = t.address[7:]
		} else if t.address[:8] == "https://" {
			domain = t.address[8:]
		}
		if t.address[:6] != "unix:/" && len(domain) == 0 {
			return nil, fmt.Errorf("Upstream %s's address should begin with unix:/, http:// or https://", u.name)
		}

		transport := &http.Transport{
			MaxIdleConnsPerHost: t.keepalive,
			DisableKeepAlives:   t.keepalive == 0,
		}
		if t.address[:6] == "unix:/" {
			transport.Dial = func(network, address string) (net.Conn, error) {
				//strip out the :80 which Go adds
				return net.Dial("unix", address[:len(address)-3])
			}
		} else if strings.Contains(t.address, "localhost") {
			transport.Dial = func(network, address string) (net.Conn, error) {
				return net.Dial(network, address)
			}
		} else {
			transport.Dial = func(network, address string) (net.Conn, error) {
				separator := strings.LastIndex(address, ":")
				ip, _ := runtime.Resolver.FetchOneString(address[:separator])
				return net.Dial(network, ip+address[separator:])
			}
		}

		if u.dnsDuration > 0 && len(domain) > 0 {
			runtime.Resolver.TTL(domain, u.dnsDuration)
		}

		transports[i] = &gc.Transport{
			Transport: transport,
			Address:   t.address,
		}
	}

	if u.tweaker != nil {
		tweaker = u.tweaker
	}

	return gc.CreateUpstream(u.headers, tweaker, transports)
}
<commit_msg>use net libraries to split and join host/ports...this should make it work with ipv6<commit_after>package configurations

import (
	"fmt"
	"github.com/karlseguin/garnish/gc"
	"net"
	"net/http"
	"strings"
	"time"
)

// The default headers to forward to the upstream
var DefaultHeaders = []string{"Content-Length"}

type Upstreams struct {
	upstreams map[string]*Upstream
}

func NewUpstreams() *Upstreams {
	return &Upstreams{
		upstreams: make(map[string]*Upstream),
	}
}

// Used internally
func (u *Upstreams) Add(name string) *Upstream {
	if _, exists := u.upstreams[name]; exists {
		gc.Log.Warnf("Upstream %q already defined. Overwriting.", name)
	}
	one := &Upstream{
		name:        name,
		dnsDuration: time.Minute,
		headers:     DefaultHeaders,
		transports:  make([]*Transport, 0, 2),
	}
	u.upstreams[name] = one
	return one
}

func (u *Upstreams) Build(runtime *gc.Runtime, defaultTweaker gc.RequestTweaker) error {
	if u == nil {
		runtime.Upstreams = make(map[string]gc.Upstream, 0)
		return nil
	}

	upstreams := make(map[string]gc.Upstream, len(u.upstreams))
	for name, one := range u.upstreams {
		tweaker := defaultTweaker
		if len(one.tweakerRef) != 0 {
			ref, ok := u.upstreams[one.tweakerRef]
			if ok == false {
				return fmt.Errorf("upstream %s's tweaker referenced %s, a non-existent upstream", name, one.tweakerRef)
			}
			if ref.tweaker == nil {
				return fmt.Errorf("upstream %s's tweaker referenced %s, which does not have a tweaker", name, one.tweakerRef)
			}
			tweaker = ref.tweaker
		}
		upstream, err := one.Build(runtime, tweaker)
		if err != nil {
			return err
		}
		upstreams[name] = upstream
	}
	runtime.Upstreams = upstreams
	return nil
}

type Upstream struct {
	name        string
	transports  []*Transport
	dnsDuration time.Duration
	headers     []string
	tweakerRef  string
	tweaker     gc.RequestTweaker
}

type Transport struct {
	address   string
	keepalive int
}

// the duration to cache the upstream's dns lookup. Set to 0 to prevent
// garnish from caching this value (even a few seconds can help)
// [1 minute]
func (u *Upstream) DnsCache(duration time.Duration) *Upstream {
	u.dnsDuration = duration
	return u
}

// The headers to copy from the incoming request to the outgoing request
// [Content-Length]
func (u *Upstream) Headers(headers ...string) *Upstream {
	u.headers = headers
	return u
}

// Custom callback to modify the request (out) that will get sent to the upstream
func (u *Upstream) Tweaker(tweaker gc.RequestTweaker) *Upstream {
	u.tweaker = tweaker
	return u
}

// Allows the upstream to reference another upstream's tweaker (based on that
// upstream's name). Used by the file-based configuration; when configured
// programmatically, Tweaker should be used.
func (u *Upstream) TweakerRef(upstream string) *Upstream {
	u.tweakerRef = upstream
	return u
}

// the address to connect to. Should begin with unix:/  http://  or https://
// [""]
func (u *Upstream) Address(address string) *Transport {
	transport := &Transport{
		address:   address,
		keepalive: 16,
	}
	u.transports = append(u.transports, transport)
	return transport
}

// the number of connections to keep alive. Set to 0 to disable
// [16]
func (t *Transport) KeepAlive(count uint32) *Transport {
	t.keepalive = int(count)
	return t
}

func (u *Upstream) Build(runtime *gc.Runtime, tweaker gc.RequestTweaker) (gc.Upstream, error) {
	l := len(u.transports)
	if l == 0 {
		return nil, fmt.Errorf("Upstream %s doesn't have a configured transport", u.name)
	}

	transports := make([]*gc.Transport, l)
	for i := 0; i < l; i++ {
		t := u.transports[i]
		if len(t.address) < 8 {
			return nil, fmt.Errorf("Upstream %s has an invalid address: %q", u.name, t.address)
		}
		var domain string
		if t.address[:7] == "http://" {
			domain = t.address[7:]
		} else if t.address[:8] == "https://" {
			domain = t.address[8:]
		}
		if t.address[:6] != "unix:/" && len(domain) == 0 {
			return nil, fmt.Errorf("Upstream %s's address should begin with unix:/, http:// or https://", u.name)
		}

		transport := &http.Transport{
			MaxIdleConnsPerHost: t.keepalive,
			DisableKeepAlives:   t.keepalive == 0,
		}
		if t.address[:6] == "unix:/" {
			transport.Dial = func(network, address string) (net.Conn, error) {
				//strip out the :80 which Go adds
				return net.Dial("unix", address[:len(address)-3])
			}
		} else if strings.Contains(t.address, "localhost") {
			transport.Dial = func(network, address string) (net.Conn, error) {
				return net.Dial(network, address)
			}
		} else {
			transport.Dial = func(network, address string) (net.Conn, error) {
				host, port, _ := net.SplitHostPort(address)
				ip, _ := runtime.Resolver.FetchOneString(host)
				return net.Dial(network, net.JoinHostPort(ip, port))
			}
		}

		if u.dnsDuration > 0 && len(domain) > 0 {
			runtime.Resolver.TTL(domain, u.dnsDuration)
		}

		transports[i] = &gc.Transport{
			Transport: transport,
			Address:   t.address,
		}
	}

	if u.tweaker != nil {
		tweaker = u.tweaker
	}

	return gc.CreateUpstream(u.headers, tweaker, transports)
}
<|endoftext|>


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

// Pkgsite extracts and generates documentation for Go programs.
// It runs as a web server and presents the documentation as a
// web page.
//
// After running `go install ./cmd/pkgsite` from the pkgsite repo root, you can
// run `pkgsite` from anywhere, but if you don't run it from the pkgsite repo
// root you must specify the location of the static assets with -static.
//
// With just -static, pkgsite will serve docs for the module in the current
// directory, which must have a go.mod file:
//
//   cd ~/repos/cue && pkgsite -static ~/repos/pkgsite/static
//
// You can also serve docs from your module cache, directly from the proxy
// (it uses the GOPROXY environment variable), or both:
//
//   pkgsite -static ~/repos/pkgsite/static -cache -proxy
//
// With either -cache or -proxy, it won't look for a module in the current directory.
// You can still provide modules on the local filesystem by listing their paths:
//
//   pkgsite -static ~/repos/pkgsite/static -cache -proxy ~/repos/cue some/other/module
package main

import (
	"context"
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/google/safehtml/template"
	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/dcensus"
	"golang.org/x/pkgsite/internal/fetch"
	"golang.org/x/pkgsite/internal/fetchdatasource"
	"golang.org/x/pkgsite/internal/frontend"
	"golang.org/x/pkgsite/internal/log"
	"golang.org/x/pkgsite/internal/middleware"
	"golang.org/x/pkgsite/internal/proxy"
	"golang.org/x/pkgsite/internal/source"
)

const defaultAddr = "localhost:8080" // default webserver address

var (
	_          = flag.String("static", "static", "path to folder containing static files served")
	gopathMode = flag.Bool("gopath_mode", false, "assume that local modules' paths are relative to GOPATH/src")
	httpAddr   = flag.String("http", defaultAddr, "HTTP service address to listen for incoming requests on")
	useCache   = flag.Bool("cache", false, "fetch from the module cache")
	cacheDir   = flag.String("cachedir", "", "module cache directory (defaults to `go env GOMODCACHE`)")
	useProxy   = flag.Bool("proxy", false, "fetch from GOPROXY if not found locally")
)

func main() {
	flag.Usage = func() {
		out := flag.CommandLine.Output()
		fmt.Fprintf(out, "usage: %s [flags] [PATHS ...]\n", os.Args[0])
		fmt.Fprintf(out, "    where each PATHS is a single path or a comma-separated list\n")
		fmt.Fprintf(out, "    (default is current directory if neither -cache nor -proxy is provided)\n")
		flag.PrintDefaults()
	}
	flag.Parse()
	ctx := context.Background()

	paths := collectPaths(flag.Args())
	if len(paths) == 0 && !*useCache && !*useProxy {
		paths = []string{"."}
	}

	var downloadDir string
	if *useCache {
		downloadDir = *cacheDir
		if downloadDir == "" {
			var err error
			downloadDir, err = defaultCacheDir()
			if err != nil {
				die("%v", err)
			}
			if downloadDir == "" {
				die("empty value for GOMODCACHE")
			}
		}
		// We actually serve from the download subdirectory.
		downloadDir = filepath.Join(downloadDir, "cache", "download")
	}

	if *useCache || *useProxy {
		fmt.Fprintf(os.Stderr, "BYPASSING LICENSE CHECKING: MAY DISPLAY NON-REDISTRIBUTABLE INFORMATION\n")
	}
	var prox *proxy.Client
	if *useProxy {
		url := os.Getenv("GOPROXY")
		if url == "" {
			die("GOPROXY environment variable is not set")
		}
		var err error
		prox, err = proxy.New(url)
		if err != nil {
			die("connecting to proxy: %s", err)
		}
	}
	server, err := newServer(ctx, paths, *gopathMode, downloadDir, prox)
	if err != nil {
		die("%s\nMaybe you need to provide the location of static assets with -static.", err)
	}
	router := dcensus.NewRouter(frontend.TagRoute)
	server.Install(router.Handle, nil, nil)
	mw := middleware.Timeout(54 * time.Second)
	log.Infof(ctx, "Listening on addr %s", *httpAddr)
	die("%v", http.ListenAndServe(*httpAddr, mw(router)))
}

func die(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	fmt.Fprintln(os.Stderr)
	os.Exit(1)
}

func collectPaths(args []string) []string {
	var paths []string
	for _, arg := range args {
		paths = append(paths, strings.Split(arg, ",")...)
	}
	return paths
}

func newServer(ctx context.Context, paths []string, gopathMode bool, downloadDir string, prox *proxy.Client) (*frontend.Server, error) {
	getters := buildGetters(ctx, paths, gopathMode)
	if downloadDir != "" {
		getters = append(getters, fetch.NewFSProxyModuleGetter(downloadDir))
	}
	if prox != nil {
		getters = append(getters, fetch.NewProxyModuleGetter(prox))
	}
	lds := fetchdatasource.Options{
		Getters:              getters,
		SourceClient:         source.NewClient(time.Second),
		ProxyClientForLatest: prox,
		BypassLicenseCheck:   true,
	}.New()
	server, err := frontend.NewServer(frontend.ServerConfig{
		DataSourceGetter: func(context.Context) internal.DataSource { return lds },
		StaticPath:       template.TrustedSourceFromFlag(flag.Lookup("static").Value),
	})
	if err != nil {
		return nil, err
	}
	return server, nil
}

func buildGetters(ctx context.Context, paths []string, gopathMode bool) []fetch.ModuleGetter {
	var getters []fetch.ModuleGetter
	loaded := len(paths)
	for _, path := range paths {
		var (
			mg  fetch.ModuleGetter
			err error
		)
		if gopathMode {
			mg, err = fetchdatasource.NewGOPATHModuleGetter(path)
		} else {
			mg, err = fetch.NewDirectoryModuleGetter("", path)
		}
		if err != nil {
			log.Error(ctx, err)
			loaded--
		} else {
			getters = append(getters, mg)
		}
	}

	if loaded == 0 && len(paths) > 0 {
		die("failed to load module(s) at %v", paths)
	}
	return getters
}

func defaultCacheDir() (string, error) {
	out, err := exec.Command("go", "env", "GOMODCACHE").CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("running 'go env GOMODCACHE': %v: %s", err, out)
	}
	return strings.TrimSpace(string(out)), nil
}
<commit_msg>cmd/pkgsite: remove dcensus<commit_after>// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Pkgsite extracts and generates documentation for Go programs.
// It runs as a web server and presents the documentation as a
// web page.
//
// After running `go install ./cmd/pkgsite` from the pkgsite repo root, you can
// run `pkgsite` from anywhere, but if you don't run it from the pkgsite repo
// root you must specify the location of the static assets with -static.
//
// With just -static, pkgsite will serve docs for the module in the current
// directory, which must have a go.mod file:
//
//   cd ~/repos/cue && pkgsite -static ~/repos/pkgsite/static
//
// You can also serve docs from your module cache, directly from the proxy
// (it uses the GOPROXY environment variable), or both:
//
//   pkgsite -static ~/repos/pkgsite/static -cache -proxy
//
// With either -cache or -proxy, it won't look for a module in the current directory.
// You can still provide modules on the local filesystem by listing their paths:
//
//   pkgsite -static ~/repos/pkgsite/static -cache -proxy ~/repos/cue some/other/module
package main

import (
	"context"
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/google/safehtml/template"
	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/fetch"
	"golang.org/x/pkgsite/internal/fetchdatasource"
	"golang.org/x/pkgsite/internal/frontend"
	"golang.org/x/pkgsite/internal/log"
	"golang.org/x/pkgsite/internal/middleware"
	"golang.org/x/pkgsite/internal/proxy"
	"golang.org/x/pkgsite/internal/source"
)

const defaultAddr = "localhost:8080" // default webserver address

var (
	_          = flag.String("static", "static", "path to folder containing static files served")
	gopathMode = flag.Bool("gopath_mode", false, "assume that local modules' paths are relative to GOPATH/src")
	httpAddr   = flag.String("http", defaultAddr, "HTTP service address to listen for incoming requests on")
	useCache   = flag.Bool("cache", false, "fetch from the module cache")
	cacheDir   = flag.String("cachedir", "", "module cache directory (defaults to `go env GOMODCACHE`)")
	useProxy   = flag.Bool("proxy", false, "fetch from GOPROXY if not found locally")
)

func main() {
	flag.Usage = func() {
		out := flag.CommandLine.Output()
		fmt.Fprintf(out, "usage: %s [flags] [PATHS ...]\n", os.Args[0])
		fmt.Fprintf(out, "    where each PATHS is a single path or a comma-separated list\n")
		fmt.Fprintf(out, "    (default is current directory if neither -cache nor -proxy is provided)\n")
		flag.PrintDefaults()
	}
	flag.Parse()
	ctx := context.Background()

	paths := collectPaths(flag.Args())
	if len(paths) == 0 && !*useCache && !*useProxy {
		paths = []string{"."}
	}

	var downloadDir string
	if *useCache {
		downloadDir = *cacheDir
		if downloadDir == "" {
			var err error
			downloadDir, err = defaultCacheDir()
			if err != nil {
				die("%v", err)
			}
			if downloadDir == "" {
				die("empty value for GOMODCACHE")
			}
		}
		// We actually serve from the download subdirectory.
		downloadDir = filepath.Join(downloadDir, "cache", "download")
	}

	if *useCache || *useProxy {
		fmt.Fprintf(os.Stderr, "BYPASSING LICENSE CHECKING: MAY DISPLAY NON-REDISTRIBUTABLE INFORMATION\n")
	}
	var prox *proxy.Client
	if *useProxy {
		url := os.Getenv("GOPROXY")
		if url == "" {
			die("GOPROXY environment variable is not set")
		}
		var err error
		prox, err = proxy.New(url)
		if err != nil {
			die("connecting to proxy: %s", err)
		}
	}
	server, err := newServer(ctx, paths, *gopathMode, downloadDir, prox)
	if err != nil {
		die("%s\nMaybe you need to provide the location of static assets with -static.", err)
	}
	router := http.NewServeMux()
	server.Install(router.Handle, nil, nil)
	mw := middleware.Timeout(54 * time.Second)
	log.Infof(ctx, "Listening on addr %s", *httpAddr)
	die("%v", http.ListenAndServe(*httpAddr, mw(router)))
}

func die(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	fmt.Fprintln(os.Stderr)
	os.Exit(1)
}

func collectPaths(args []string) []string {
	var paths []string
	for _, arg := range args {
		paths = append(paths, strings.Split(arg, ",")...)
	}
	return paths
}

func newServer(ctx context.Context, paths []string, gopathMode bool, downloadDir string, prox *proxy.Client) (*frontend.Server, error) {
	getters := buildGetters(ctx, paths, gopathMode)
	if downloadDir != "" {
		getters = append(getters, fetch.NewFSProxyModuleGetter(downloadDir))
	}
	if prox != nil {
		getters = append(getters, fetch.NewProxyModuleGetter(prox))
	}
	lds := fetchdatasource.Options{
		Getters:              getters,
		SourceClient:         source.NewClient(time.Second),
		ProxyClientForLatest: prox,
		BypassLicenseCheck:   true,
	}.New()
	server, err := frontend.NewServer(frontend.ServerConfig{
		DataSourceGetter: func(context.Context) internal.DataSource { return lds },
		StaticPath:       template.TrustedSourceFromFlag(flag.Lookup("static").Value),
	})
	if err != nil {
		return nil, err
	}
	return server, nil
}

func buildGetters(ctx context.Context, paths []string, gopathMode bool) []fetch.ModuleGetter {
	var getters []fetch.ModuleGetter
	loaded := len(paths)
	for _, path := range paths {
		var (
			mg  fetch.ModuleGetter
			err error
		)
		if gopathMode {
			mg, err = fetchdatasource.NewGOPATHModuleGetter(path)
		} else {
			mg, err = fetch.NewDirectoryModuleGetter("", path)
		}
		if err != nil {
			log.Error(ctx, err)
			loaded--
		} else {
			getters = append(getters, mg)
		}
	}

	if loaded == 0 && len(paths) > 0 {
		die("failed to load module(s) at %v", paths)
	}
	return getters
}

func defaultCacheDir() (string, error) {
	out, err := exec.Command("go", "env", "GOMODCACHE").CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("running 'go env GOMODCACHE': %v: %s", err, out)
	}
	return strings.TrimSpace(string(out)), nil
}
<|endoftext|>


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

// ----------------

// ractool manipulates Random Access Compression (RAC) files.
package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"

	"github.com/google/wuffs/lib/raczlib"
)

// TODO: a flag to use a disk-backed (not memory-backed) TempFile.

var (
	decodeFlag = flag.Bool("decode", false, "whether to decode the input")
	encodeFlag = flag.Bool("encode", false, "whether to encode the input")

	codecFlag         = flag.String("codec", "zlib", "when encoding, the compression codec")
	cpagesizeFlag     = flag.Uint64("cpagesize", 0, "when encoding, the page size (in CSpace)")
	cchunksizeFlag    = flag.Uint64("cchunksize", 0, "when encoding, the chunk size (in CSpace)")
	dchunksizeFlag    = flag.Uint64("dchunksize", 0, "when encoding, the chunk size (in DSpace)")
	indexlocationFlag = flag.String("indexlocation", "start",
		"when encoding, the index location, \"start\" or \"end\"")
)

const usageStr = `usage: ractool [flags] [input_filename]

If no input_filename is given, stdin is used. Either way, output is written to
stdout.

The flags should include exactly one of -decode or -encode.

Decoding is not yet implemented.

When encoding, the input is partitioned into chunks and each chunk is
compressed independently. You can specify the target chunk size in terms of
either its compressed size or decompressed size. By default (if both
-cchunksize and -dchunksize are zero), a 64KiB -cchunksize is used.

You can also specify a -cpagesize, which is similar to but not exactly the same
concept as alignment. If non-zero, padding is inserted into the output to
minimize the number of pages that each chunk occupies. Look for "CPageSize" in
the "package rac" documentation for more details.

A RAC file consists of an index and the chunks. The index may be either at the
start or at the end of the file. See the RAC specification for more details:

https://github.com/google/wuffs/blob/master/doc/spec/rac-spec.md

`

func usage() {
	fmt.Fprintf(os.Stderr, usageStr)
	flag.PrintDefaults()
}

func main() {
	if err := main1(); err != nil {
		os.Stderr.WriteString(err.Error() + "\n")
		os.Exit(1)
	}
}

func main1() error {
	flag.Usage = usage
	flag.Parse()

	r := io.Reader(os.Stdin)
	switch flag.NArg() {
	case 0:
		// No-op.
	case 1:
		f, err := os.Open(flag.Arg(0))
		if err != nil {
			return err
		}
		defer f.Close()
		r = f
	default:
		return errors.New("too many filenames; the maximum is one")
	}

	if *decodeFlag && !*encodeFlag {
		return decode(r)
	}
	if *encodeFlag && !*decodeFlag {
		return encode(r)
	}
	return errors.New("must specify exactly one of -decode or -encode")
}

func decode(r io.Reader) error {
	return errors.New("TODO: implement a decoder")
}

func encode(r io.Reader) error {
	indexLocation := raczlib.IndexLocation(0)
	switch *indexlocationFlag {
	case "start":
		indexLocation = raczlib.IndexLocationAtStart
	case "end":
		indexLocation = raczlib.IndexLocationAtEnd
	default:
		return errors.New("invalid -indexlocation")
	}

	if (*cchunksizeFlag != 0) && (*dchunksizeFlag != 0) {
		return errors.New("must specify none or one of -cchunksize or -dchunksize")
	} else if (*cchunksizeFlag == 0) && (*dchunksizeFlag == 0) {
		*cchunksizeFlag = 65536 // 64 KiB.
	}

	switch *codecFlag {
	case "zlib":
		w := &raczlib.Writer{
			Writer:        os.Stdout,
			IndexLocation: indexLocation,
			TempFile:      &bytes.Buffer{},
			CPageSize:     *cpagesizeFlag,
			CChunkSize:    *cchunksizeFlag,
			DChunkSize:    *dchunksizeFlag,
		}
		if _, err := io.Copy(w, r); err != nil {
			return err
		}
		return w.Close()
	}
	return errors.New("unsupported -codec")
}
<commit_msg>Have cmd/ractool support decoding<commit_after>// Copyright 2019 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// ----------------

// ractool manipulates Random Access Compression (RAC) files.
package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"

	"github.com/google/wuffs/lib/raczlib"
)

// TODO: a flag to use a disk-backed (not memory-backed) TempFile.

var (
	decodeFlag = flag.Bool("decode", false, "whether to decode the input")
	encodeFlag = flag.Bool("encode", false, "whether to encode the input")

	codecFlag         = flag.String("codec", "zlib", "when encoding, the compression codec")
	cpagesizeFlag     = flag.Uint64("cpagesize", 0, "when encoding, the page size (in CSpace)")
	cchunksizeFlag    = flag.Uint64("cchunksize", 0, "when encoding, the chunk size (in CSpace)")
	dchunksizeFlag    = flag.Uint64("dchunksize", 0, "when encoding, the chunk size (in DSpace)")
	indexlocationFlag = flag.String("indexlocation", "start",
		"when encoding, the index location, \"start\" or \"end\"")
)

const usageStr = `usage: ractool [flags] [input_filename]

If no input_filename is given, stdin is used. Either way, output is written to
stdout.

The flags should include exactly one of -decode or -encode.

When encoding, the input is partitioned into chunks and each chunk is
compressed independently. You can specify the target chunk size in terms of
either its compressed size or decompressed size. By default (if both
-cchunksize and -dchunksize are zero), a 64KiB -cchunksize is used.

You can also specify a -cpagesize, which is similar to but not exactly the same
concept as alignment. If non-zero, padding is inserted into the output to
minimize the number of pages that each chunk occupies. Look for "CPageSize" in
the "package rac" documentation for more details.

A RAC file consists of an index and the chunks. The index may be either at the
start or at the end of the file. See the RAC specification for more details:

https://github.com/google/wuffs/blob/master/doc/spec/rac-spec.md

`

func usage() {
	fmt.Fprintf(os.Stderr, usageStr)
	flag.PrintDefaults()
}

func main() {
	if err := main1(); err != nil {
		os.Stderr.WriteString(err.Error() + "\n")
		os.Exit(1)
	}
}

func main1() error {
	flag.Usage = usage
	flag.Parse()

	r, usingStdin := io.Reader(os.Stdin), true
	switch flag.NArg() {
	case 0:
		// No-op.
	case 1:
		f, err := os.Open(flag.Arg(0))
		if err != nil {
			return err
		}
		defer f.Close()
		r, usingStdin = f, false
	default:
		return errors.New("too many filenames; the maximum is one")
	}

	if *decodeFlag && !*encodeFlag {
		return decode(r, usingStdin)
	}
	if *encodeFlag && !*decodeFlag {
		return encode(r)
	}
	return errors.New("must specify exactly one of -decode or -encode")
}

func decode(r io.Reader, usingStdin bool) error {
	rs, ok := r.(io.ReadSeeker)
	if !ok {
		return fmt.Errorf("input is not seekable")
	}

	// Even if the os.File type is a ReadSeeker, it might not actually support
	// seeking. Instead, read all of stdin into memory.
	if usingStdin {
		if input, err := ioutil.ReadAll(r); err != nil {
			return err
		} else {
			rs = bytes.NewReader(input)
		}
	}

	compressedSize, err := rs.Seek(0, io.SeekEnd)
	if err != nil {
		return err
	}
	if _, err := rs.Seek(0, io.SeekStart); err != nil {
		return err
	}

	switch *codecFlag {
	case "zlib":
		_, err := io.Copy(os.Stdout, &raczlib.Reader{
			ReadSeeker:     rs,
			CompressedSize: compressedSize,
		})
		return err
	}
	return errors.New("unsupported -codec")
}

func encode(r io.Reader) error {
	indexLocation := raczlib.IndexLocation(0)
	switch *indexlocationFlag {
	case "start":
		indexLocation = raczlib.IndexLocationAtStart
	case "end":
		indexLocation = raczlib.IndexLocationAtEnd
	default:
		return errors.New("invalid -indexlocation")
	}

	if (*cchunksizeFlag != 0) && (*dchunksizeFlag != 0) {
		return errors.New("must specify none or one of -cchunksize or -dchunksize")
	} else if (*cchunksizeFlag == 0) && (*dchunksizeFlag == 0) {
		*cchunksizeFlag = 65536 // 64 KiB.
	}

	switch *codecFlag {
	case "zlib":
		w := &raczlib.Writer{
			Writer:        os.Stdout,
			IndexLocation: indexLocation,
			TempFile:      &bytes.Buffer{},
			CPageSize:     *cpagesizeFlag,
			CChunkSize:    *cchunksizeFlag,
			DChunkSize:    *dchunksizeFlag,
		}
		if _, err := io.Copy(w, r); err != nil {
			return err
		}
		return w.Close()
	}
	return errors.New("unsupported -codec")
}
<|endoftext|>