<commit_before>package main

import (
	"fmt"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"time"

	"github.com/astaxie/beego"
	"github.com/sirupsen/logrus"
	"github.com/snagles/docker-registry-manager/app/models"
	_ "github.com/snagles/docker-registry-manager/app/routers"
	"github.com/spf13/viper"
	"github.com/urfave/cli"
)

const (
	appVersion = "3.0.0"
)

func main() {
	app := cli.NewApp()
	app.Name = "Docker Registry Manager"
	app.Usage = "Connect to, view, and manage multiple private Docker registries"

	var registriesFile, logLevel, keyPath, certPath string
	var enableHTTPS bool
	var appPort int
	app.Flags = []cli.Flag{
		cli.IntFlag{
			Name:        "port, p",
			Usage:       "port to use for the registry manager `port`",
			Value:       8080,
			Destination: &appPort,
			EnvVar:      "MANAGER_PORT",
		},
		cli.StringFlag{
			Name:        "registries, r",
			Usage:       "file location of the registries.yml `/app/registries.yml`",
			EnvVar:      "MANAGER_REGISTRIES",
			Destination: &registriesFile,
		},
		cli.StringFlag{
			Name:        "log-level, l",
			Usage:       "log-level `warn`",
			Value:       "info",
			EnvVar:      "MANAGER_LOG_LEVEL",
			Destination: &logLevel,
		},

		// Beego HTTPS options
		cli.BoolFlag{
			Name:        "enable-https, e",
			Usage:       "enable https `true or false`",
			EnvVar:      "MANAGER_ENABLE_HTTPS",
			Destination: &enableHTTPS,
		},
		cli.StringFlag{
			Name:        "tls-key, k",
			Usage:       "tls certificate path and name `/app/key.key`",
			EnvVar:      "MANAGER_KEY",
			Destination: &keyPath,
		},
		cli.StringFlag{
			Name:        "tls-certificate, cert",
			Usage:       "tls certificate path and name `/app/certificate.crt`",
			EnvVar:      "MANAGER_CERTIFICATE",
			Destination: &certPath,
		},
	}

	app.Action = func(ctx *cli.Context) {
		c, err := parseRegistries(registriesFile)
		if err != nil {
			logrus.Fatal(err)
		}

		err = setlevel(logLevel)
		if err != nil {
			logrus.Fatal(err)
		}

		for name, r := range c.Registries {
			if r.URL != "" {
				url, err := url.Parse(r.URL)
				if err != nil {
					logrus.Fatalf("Failed to parse registry from the passed url (%s): %s", r.URL, err)
				}
				duration, err := time.ParseDuration(r.RefreshRate)
				if err != nil {
					logrus.Fatalf("Failed to add registry (%s), invalid duration: %s", r.URL, err)
				}
				if r.Password != "" && r.Username != "" {
					if _, err := manager.AddRegistry(url.Scheme, url.Hostname(), name, r.Username, r.Password, r.Port, duration, r.SkipTLS, r.DockerhubIntegration); err != nil {
						logrus.Fatalf("Failed to add registry (%s): %s", r.URL, err)
					}
				} else {
					if _, err := manager.AddRegistry(url.Scheme, url.Hostname(), name, "", "", r.Port, duration, r.SkipTLS, r.DockerhubIntegration); err != nil {
						logrus.Fatalf("Failed to add registry (%s): %s", r.URL, err)
					}
				}
			}
		}

		// Beego configuration
		beego.BConfig.AppName = "docker-registry-manager"
		beego.BConfig.RunMode = "dev"
		beego.BConfig.Listen.EnableAdmin = true
		beego.BConfig.CopyRequestBody = true
		beego.BConfig.WebConfig.ViewsPath = "views"

		// set http port
		if enableHTTPS {
			beego.BConfig.Listen.HTTPSPort = appPort
			// make sure we have both key and cert
			if keyPath == "" {
				logrus.Fatal("HTTPS enabled, but no key file provided")
			} else {
				beego.BConfig.Listen.HTTPSKeyFile = keyPath
			}
			if certPath == "" {
				logrus.Fatal("HTTPS enabled, but no certificate file provided")
			} else {
				beego.BConfig.Listen.HTTPSCertFile = certPath
			}
			// if we're not using https just use standard http
		} else {
			beego.BConfig.Listen.HTTPPort = appPort
		}

		// add template functions
		beego.AddFuncMap("shortenDigest", DigestShortener)
		beego.AddFuncMap("statToSeconds", StatToSeconds)
		beego.AddFuncMap("bytefmt", ByteFmt)
		beego.AddFuncMap("bytefmtdiff", ByteDiffFmt)
		beego.AddFuncMap("timeAgo", TimeAgo)
		beego.AddFuncMap("oneIndex", func(i int) int { return i + 1 })
		beego.Run()
	}
	app.Run(os.Args)
}

func setlevel(level string) error {
	switch {
	case level == "panic":
		logrus.SetLevel(logrus.PanicLevel)
	case level == "fatal":
		logrus.SetLevel(logrus.FatalLevel)
	case level == "error":
		logrus.SetLevel(logrus.ErrorLevel)
	case level == "warn":
		logrus.SetLevel(logrus.WarnLevel)
	case level == "info":
		logrus.SetLevel(logrus.InfoLevel)
	case level == "debug":
		logrus.SetLevel(logrus.DebugLevel)
	default:
		return fmt.Errorf("Unrecognized log level: %s", level)
	}
	return nil
}

type registries struct {
	Registries map[string]struct {
		URL                  string
		Port                 int
		Username             string
		Password             string
		SkipTLS              bool   `mapstructure:"skip-tls-validation"`
		RefreshRate          string `mapstructure:"refresh-rate"`
		DockerhubIntegration bool   `mapstructure:"dockerhub-integration"`
	} `mapstructure:"registries"`
}

func parseRegistries(registriesFile string) (*registries, error) {
	v := viper.New()

	// If the registries path is not passed use the default project dir
	if registriesFile != "" {
		v.AddConfigPath(path.Dir(registriesFile))
		base := path.Base(registriesFile)
		ext := path.Ext(registriesFile)
		v.SetConfigName(base[0 : len(base)-len(ext)])
		logrus.Infof("Using registries located in %s with file name %s", path.Dir(registriesFile), base[0:len(base)-len(ext)])
	} else {
		v.SetConfigName("registries")
		var root string
		_, r, _, ok := runtime.Caller(0)
		if ok {
			root = filepath.Dir(r)
			v.AddConfigPath(root)
		} else {
			logrus.Fatalf("Failed to get runtime caller for parser")
		}
		logrus.Infof("Using registries located in %s with file name %s", root, "registries.yml")
	}

	if err := v.ReadInConfig(); err != nil {
		return nil, fmt.Errorf("Failed to read in registries file: %s", err)
	}

	c := registries{}
	if err := v.Unmarshal(&c); err != nil {
		return nil, fmt.Errorf("Unable to unmarshal registries file: %s", err)
	}
	return &c, nil
}
<commit_msg>Writing registry file should update interface. #108<commit_after>package main

import (
	"fmt"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"time"

	"github.com/astaxie/beego"
	"github.com/fsnotify/fsnotify"
	"github.com/sirupsen/logrus"
	"github.com/snagles/docker-registry-manager/app/models"
	_ "github.com/snagles/docker-registry-manager/app/routers"
	"github.com/spf13/viper"
	"github.com/urfave/cli"
)

const (
	appVersion = "3.0.0"
)

func main() {
	app := cli.NewApp()
	app.Name = "Docker Registry Manager"
	app.Usage = "Connect to, view, and manage multiple private Docker registries"

	var registriesFile, logLevel, keyPath, certPath string
	var enableHTTPS bool
	var appPort int
	app.Flags = []cli.Flag{
		cli.IntFlag{
			Name:        "port, p",
			Usage:       "port to use for the registry manager `port`",
			Value:       8080,
			Destination: &appPort,
			EnvVar:      "MANAGER_PORT",
		},
		cli.StringFlag{
			Name:        "registries, r",
			Usage:       "file location of the registries.yml `/app/registries.yml`",
			EnvVar:      "MANAGER_REGISTRIES",
			Destination: &registriesFile,
		},
		cli.StringFlag{
			Name:        "log-level, l",
			Usage:       "log-level `warn`",
			Value:       "info",
			EnvVar:      "MANAGER_LOG_LEVEL",
			Destination: &logLevel,
		},

		// Beego HTTPS options
		cli.BoolFlag{
			Name:        "enable-https, e",
			Usage:       "enable https `true or false`",
			EnvVar:      "MANAGER_ENABLE_HTTPS",
			Destination: &enableHTTPS,
		},
		cli.StringFlag{
			Name:        "tls-key, k",
			Usage:       "tls certificate path and name `/app/key.key`",
			EnvVar:      "MANAGER_KEY",
			Destination: &keyPath,
		},
		cli.StringFlag{
			Name:        "tls-certificate, cert",
			Usage:       "tls certificate path and name `/app/certificate.crt`",
			EnvVar:      "MANAGER_CERTIFICATE",
			Destination: &certPath,
		},
	}

	app.Action = func(ctx *cli.Context) {
		c, err := parseRegistries(registriesFile)
		if err != nil {
			logrus.Fatal(err)
		}

		err = setlevel(logLevel)
		if err != nil {
			logrus.Fatal(err)
		}

		addRegistries(c)

		// Beego configuration
		beego.BConfig.AppName = "docker-registry-manager"
		beego.BConfig.RunMode = "dev"
		beego.BConfig.Listen.EnableAdmin = true
		beego.BConfig.CopyRequestBody = true
		beego.BConfig.WebConfig.ViewsPath = "views"

		// set http port
		if enableHTTPS {
			beego.BConfig.Listen.HTTPSPort = appPort
			// make sure we have both key and cert
			if keyPath == "" {
				logrus.Fatal("HTTPS enabled, but no key file provided")
			} else {
				beego.BConfig.Listen.HTTPSKeyFile = keyPath
			}
			if certPath == "" {
				logrus.Fatal("HTTPS enabled, but no certificate file provided")
			} else {
				beego.BConfig.Listen.HTTPSCertFile = certPath
			}
			// if we're not using https just use standard http
		} else {
			beego.BConfig.Listen.HTTPPort = appPort
		}

		// add template functions
		beego.AddFuncMap("shortenDigest", DigestShortener)
		beego.AddFuncMap("statToSeconds", StatToSeconds)
		beego.AddFuncMap("bytefmt", ByteFmt)
		beego.AddFuncMap("bytefmtdiff", ByteDiffFmt)
		beego.AddFuncMap("timeAgo", TimeAgo)
		beego.AddFuncMap("oneIndex", func(i int) int { return i + 1 })
		beego.Run()
	}
	app.Run(os.Args)
}

func addRegistries(c *registries) {
	for name, r := range c.Registries {
		if r.URL != "" {
			url, err := url.Parse(r.URL)
			if err != nil {
				logrus.Fatalf("Failed to parse registry from the passed url (%s): %s", r.URL, err)
			}
			duration, err := time.ParseDuration(r.RefreshRate)
			if err != nil {
				logrus.Fatalf("Failed to add registry (%s), invalid duration: %s", r.URL, err)
			}
			if r.Password != "" && r.Username != "" {
				if _, err := manager.AddRegistry(url.Scheme, url.Hostname(), name, r.Username, r.Password, r.Port, duration, r.SkipTLS, r.DockerhubIntegration); err != nil {
					logrus.Fatalf("Failed to add registry (%s): %s", r.URL, err)
				}
			} else {
				if _, err := manager.AddRegistry(url.Scheme, url.Hostname(), name, "", "", r.Port, duration, r.SkipTLS, r.DockerhubIntegration); err != nil {
					logrus.Fatalf("Failed to add registry (%s): %s", r.URL, err)
				}
			}
		}
	}
}

func setlevel(level string) error {
	switch {
	case level == "panic":
		logrus.SetLevel(logrus.PanicLevel)
	case level == "fatal":
		logrus.SetLevel(logrus.FatalLevel)
	case level == "error":
		logrus.SetLevel(logrus.ErrorLevel)
	case level == "warn":
		logrus.SetLevel(logrus.WarnLevel)
	case level == "info":
		logrus.SetLevel(logrus.InfoLevel)
	case level == "debug":
		logrus.SetLevel(logrus.DebugLevel)
	default:
		return fmt.Errorf("Unrecognized log level: %s", level)
	}
	return nil
}

type registries struct {
	Registries map[string]struct {
		URL                  string
		Port                 int
		Username             string
		Password             string
		SkipTLS              bool   `mapstructure:"skip-tls-validation"`
		RefreshRate          string `mapstructure:"refresh-rate"`
		DockerhubIntegration bool   `mapstructure:"dockerhub-integration"`
	} `mapstructure:"registries"`
}

func parseRegistries(registriesFile string) (*registries, error) {
	v := viper.New()

	// If the registries path is not passed use the default project dir
	if registriesFile != "" {
		v.AddConfigPath(path.Dir(registriesFile))
		base := path.Base(registriesFile)
		ext := path.Ext(registriesFile)
		v.SetConfigName(base[0 : len(base)-len(ext)])
		logrus.Infof("Using registries located in %s with file name %s", path.Dir(registriesFile), base[0:len(base)-len(ext)])
	} else {
		v.SetConfigName("registries")
		var root string
		_, r, _, ok := runtime.Caller(0)
		if ok {
			root = filepath.Dir(r)
			v.AddConfigPath(root)
		} else {
			logrus.Fatalf("Failed to get runtime caller for parser")
		}
		logrus.Infof("Using registries located in %s with file name %s", root, "registries.yml")
	}

	if err := v.ReadInConfig(); err != nil {
		return nil, fmt.Errorf("Failed to read in registries file: %s", err)
	}

	c := registries{}
	if err := v.Unmarshal(&c); err != nil {
		return nil, fmt.Errorf("Unable to unmarshal registries file: %s", err)
	}

	v.WatchConfig()
	v.OnConfigChange(func(e fsnotify.Event) {
		c, err := parseRegistries(registriesFile)
		if err != nil {
			logrus.Fatal(err)
		}
		addRegistries(c)
	})

	return &c, nil
}
<|endoftext|>


<commit_before>package ase

import (
	"bytes"
	"testing"
)

var testColors = []Color{
	Color{
		Name:   "RGB",
		Model:  "RGB",
		Values: []float32{1, 1, 1},
		Type:   "Normal",
	},
	Color{
		Name:   "Grayscale",
		Model:  "CMYK",
		Values: []float32{0, 0, 0, 0.47},
		Type:   "Spot",
	},
	Color{
		Name:   "cmyk",
		Model:  "CMYK",
		Values: []float32{0, 1, 0, 0},
		Type:   "Spot",
	},
	Color{
		Name:   "LAB",
		Model:  "RGB",
		Values: []float32{0, 0.6063648, 0.524658},
		Type:   "Global",
	},
	Color{
		Name:   "PANTONE P 1-8 C",
		Model:  "LAB",
		Values: []float32{0.9137255, -5, 94},
		Type:   "Spot",
	},
	Color{
		Name:   "Red",
		Model:  "RGB",
		Values: []float32{1, 0, 0},
		Type:   "Global",
	},
	Color{
		Name:   "Green",
		Model:  "RGB",
		Values: []float32{0, 1, 0},
		Type:   "Global",
	},
	Color{
		Name:   "Blue",
		Model:  "RGB",
		Values: []float32{0, 0, 1},
		Type:   "Global",
	},
}

var testGroup = Group{
	Name: "A Color Group",
	Colors: []Color{
		Color{
			Name:   "Red",
			Model:  "RGB",
			Values: []float32{1, 0, 0},
			Type:   "Global",
		},
		Color{
			Name:   "Green",
			Model:  "RGB",
			Values: []float32{0, 1, 0},
			Type:   "Global",
		},
		Color{
			Name:   "Blue",
			Model:  "RGB",
			Values: []float32{0, 0, 1},
			Type:   "Global",
		},
	},
}

func TestSignature(t *testing.T) {
	testFile := "samples/test.ase"

	ase, err := DecodeFile(testFile)
	if err != nil {
		t.Error(err)
	}

	expectedSignature := "ASEF"
	if ase.Signature() != expectedSignature {
		t.Error("expected signature of", expectedSignature, ", got:", ase.Signature())
	}
}

func TestVersion(t *testing.T) {
	testFile := "samples/test.ase"

	ase, err := DecodeFile(testFile)
	if err != nil {
		t.Error(err)
	}

	expectedVersion := "1.0"
	if ase.Version() != expectedVersion {
		t.Error("expected version of", expectedVersion, ", got:", ase.Version())
	}
}

func TestDecode(t *testing.T) {
	testFile := "samples/test.ase"

	ase, err := DecodeFile(testFile)
	if err != nil {
		t.Error(err)
	}

	expectedNumBlocks := int32(10)
	if ase.numBlocks != expectedNumBlocks {
		t.Error("expected ", expectedNumBlocks, " numBlocks, got ", ase.numBlocks)
	}

	expectedColors := testColors[0:5]
	expectedNumColors := len(expectedColors)
	actualNumColors := len(ase.Colors)

	if actualNumColors != expectedNumColors {
		t.Error("expected number of colors to be", expectedNumColors,
			"got ", actualNumColors)
	}

	for i, color := range ase.Colors {
		expectedColor := expectedColors[i]

		if color.Name != expectedColor.Name {
			t.Error("expected initial color with name ", expectedColor.Name,
				"got ", color.Name)
		}

		if color.Model != expectedColor.Model {
			t.Error("expected initial color of Model ", expectedColor.Model,
				"got ", color.Model)
		}

		for j, _ := range expectedColor.Values {
			if color.Values[j] != expectedColor.Values[j] {
				t.Error("expected color value ", expectedColor.Values[j],
					"got ", color.Values[j])
			}
		}

		if color.Type != expectedColor.Type {
			t.Error("expected color type ", expectedColor.Type,
				"got ", color.Type)
		}
	}
}

func TestEncode(t *testing.T) {

	// Initialize a sample ASE
	sampleAse := ASE{}
	sampleAse.Colors = testColors
	sampleAse.Groups = append(sampleAse.Groups, testGroup)

	// Encode the sampleAse into the buffer and immediately decode it.
	b := new(bytes.Buffer)
	Encode(sampleAse, b)
	ase, _ := Decode(b)

	// Check the ASE's decoded values.
	if string(ase.signature[0:]) != "ASEF" {
		t.Error("ase: file not an ASE file")
	}

	if ase.version[0] != 1 && ase.version[1] != 0 {
		t.Error("ase: version is not 1.0")
	}

	expectedNumBlocks := int32(13)
	actualNumBlocks := ase.numBlocks
	if actualNumBlocks != expectedNumBlocks {
		t.Error("ase: expected", expectedNumBlocks,
			" blocks to be present, got: ", actualNumBlocks)
	}

	expectedAmountOfColors := 8
	if len(ase.Colors) != expectedAmountOfColors {
		t.Error("ase: expected", expectedAmountOfColors, " colors to be present")
	}

	for i, color := range ase.Colors {
		expectedColor := testColors[i]

		if color.Name != expectedColor.Name {
			t.Error("expected initial color with name ", expectedColor.Name,
				"got ", color.Name)
		}

		if color.Model != expectedColor.Model {
			t.Error("expected initial color of Model ", expectedColor.Model,
				"got ", color.Model)
		}

		for j, _ := range expectedColor.Values {
			if color.Values[j] != expectedColor.Values[j] {
				t.Error("expected color value ", expectedColor.Values[j],
					"got ", color.Values[j])
			}
		}

		if color.Type != expectedColor.Type {
			t.Error("expected color type ", expectedColor.Type,
				"got ", color.Type)
		}
	}

	expectedAmountOfGroups := 1
	actualAmountOfGroups := len(ase.Groups)
	if actualAmountOfGroups != expectedAmountOfGroups {
		t.Error("expected ", expectedAmountOfGroups,
			"amount of groups, got: ", actualAmountOfGroups)
	}

	group := ase.Groups[0]

	if group.Name != testGroup.Name {
		t.Error("expected group name to be ", testGroup.Name,
			", got: ", group.Name)
	}

	for i, color := range group.Colors {
		expectedColor := testGroup.Colors[i]

		if color.Name != expectedColor.Name {
			t.Error("expected initial color with name ", expectedColor.Name,
				"got ", color.Name)
		}

		if color.Model != expectedColor.Model {
			t.Error("expected initial color of Model ", expectedColor.Model,
				"got ", color.Model)
		}

		for j, _ := range expectedColor.Values {
			if color.Values[j] != expectedColor.Values[j] {
				t.Error("expected color value ", expectedColor.Values[j],
					"got ", color.Values[j])
			}
		}

		if color.Type != expectedColor.Type {
			t.Error("expected color type ", expectedColor.Type,
				"got ", color.Type)
		}
	}

}
<commit_msg>(decode) include units for a decoded ASE's groups<commit_after>package ase

import (
	"bytes"
	"testing"
)

var testColors = []Color{
	Color{
		Name:   "RGB",
		Model:  "RGB",
		Values: []float32{1, 1, 1},
		Type:   "Normal",
	},
	Color{
		Name:   "Grayscale",
		Model:  "CMYK",
		Values: []float32{0, 0, 0, 0.47},
		Type:   "Spot",
	},
	Color{
		Name:   "cmyk",
		Model:  "CMYK",
		Values: []float32{0, 1, 0, 0},
		Type:   "Spot",
	},
	Color{
		Name:   "LAB",
		Model:  "RGB",
		Values: []float32{0, 0.6063648, 0.524658},
		Type:   "Global",
	},
	Color{
		Name:   "PANTONE P 1-8 C",
		Model:  "LAB",
		Values: []float32{0.9137255, -5, 94},
		Type:   "Spot",
	},
	Color{
		Name:   "Red",
		Model:  "RGB",
		Values: []float32{1, 0, 0},
		Type:   "Global",
	},
	Color{
		Name:   "Green",
		Model:  "RGB",
		Values: []float32{0, 1, 0},
		Type:   "Global",
	},
	Color{
		Name:   "Blue",
		Model:  "RGB",
		Values: []float32{0, 0, 1},
		Type:   "Global",
	},
}

var testGroup = Group{
	Name: "A Color Group",
	Colors: []Color{
		Color{
			Name:   "Red",
			Model:  "RGB",
			Values: []float32{1, 0, 0},
			Type:   "Global",
		},
		Color{
			Name:   "Green",
			Model:  "RGB",
			Values: []float32{0, 1, 0},
			Type:   "Global",
		},
		Color{
			Name:   "Blue",
			Model:  "RGB",
			Values: []float32{0, 0, 1},
			Type:   "Global",
		},
	},
}

func TestSignature(t *testing.T) {
	testFile := "samples/test.ase"

	ase, err := DecodeFile(testFile)
	if err != nil {
		t.Error(err)
	}

	expectedSignature := "ASEF"
	if ase.Signature() != expectedSignature {
		t.Error("expected signature of", expectedSignature, ", got:", ase.Signature())
	}
}

func TestVersion(t *testing.T) {
	testFile := "samples/test.ase"

	ase, err := DecodeFile(testFile)
	if err != nil {
		t.Error(err)
	}

	expectedVersion := "1.0"
	if ase.Version() != expectedVersion {
		t.Error("expected version of", expectedVersion, ", got:", ase.Version())
	}
}

func TestDecode(t *testing.T) {
	testFile := "samples/test.ase"

	ase, err := DecodeFile(testFile)
	if err != nil {
		t.Error(err)
	}

	// Check ASE's metadata (Signature and Version tested in separate functions)
	expectedNumBlocks := int32(10)
	if ase.numBlocks != expectedNumBlocks {
		t.Error("expected ", expectedNumBlocks, " numBlocks, got ", ase.numBlocks)
	}

	// Check the ASE's Colors
	expectedColors := testColors[0:5]
	expectedNumColors := len(expectedColors)
	actualNumColors := len(ase.Colors)

	if actualNumColors != expectedNumColors {
		t.Error("expected number of colors to be", expectedNumColors,
			"got ", actualNumColors)
	}

	for i, color := range ase.Colors {
		expectedColor := expectedColors[i]

		if color.Name != expectedColor.Name {
			t.Error("expected initial color with name ", expectedColor.Name,
				"got ", color.Name)
		}

		if color.Model != expectedColor.Model {
			t.Error("expected initial color of Model ", expectedColor.Model,
				"got ", color.Model)
		}

		for j, _ := range expectedColor.Values {
			if color.Values[j] != expectedColor.Values[j] {
				t.Error("expected color value ", expectedColor.Values[j],
					"got ", color.Values[j])
			}
		}

		if color.Type != expectedColor.Type {
			t.Error("expected color type ", expectedColor.Type,
				"got ", color.Type)
		}
	}

	// Check the ASE's Groups' data
	expectedGroupsLen := 1
	actualGroupLen := len(ase.Groups)

	if actualGroupLen != expectedGroupsLen {
		t.Error("expected group length of ", expectedGroupsLen,
			"got ", actualGroupLen)
	}

	group := ase.Groups[0]

	if group.Name != testGroup.Name {
		t.Error("expected group name to be ", testGroup.Name,
			", got: ", group.Name)
	}

	// Check the ASE's Groups' Color Data
	for i, color := range group.Colors {
		expectedColor := testGroup.Colors[i]

		if color.Name != expectedColor.Name {
			t.Error("expected initial color with name ", expectedColor.Name,
				"got ", color.Name)
		}

		if color.Model != expectedColor.Model {
			t.Error("expected initial color of Model ", expectedColor.Model,
				"got ", color.Model)
		}

		for j, _ := range expectedColor.Values {
			if color.Values[j] != expectedColor.Values[j] {
				t.Error("expected color value ", expectedColor.Values[j],
					"got ", color.Values[j])
			}
		}

		if color.Type != expectedColor.Type {
			t.Error("expected color type ", expectedColor.Type,
				"got ", color.Type)
		}
	}

}

func TestEncode(t *testing.T) {

	// Initialize a sample ASE
	sampleAse := ASE{}
	sampleAse.Colors = testColors
	sampleAse.Groups = append(sampleAse.Groups, testGroup)

	// Encode the sampleAse into the buffer and immediately decode it.
	b := new(bytes.Buffer)
	Encode(sampleAse, b)
	ase, _ := Decode(b)

	// Check the ASE's decoded values.
	if string(ase.signature[0:]) != "ASEF" {
		t.Error("ase: file not an ASE file")
	}

	if ase.version[0] != 1 && ase.version[1] != 0 {
		t.Error("ase: version is not 1.0")
	}

	expectedNumBlocks := int32(13)
	actualNumBlocks := ase.numBlocks
	if actualNumBlocks != expectedNumBlocks {
		t.Error("ase: expected", expectedNumBlocks,
			" blocks to be present, got: ", actualNumBlocks)
	}

	expectedAmountOfColors := 8
	if len(ase.Colors) != expectedAmountOfColors {
		t.Error("ase: expected", expectedAmountOfColors, " colors to be present")
	}

	for i, color := range ase.Colors {
		expectedColor := testColors[i]

		if color.Name != expectedColor.Name {
			t.Error("expected initial color with name ", expectedColor.Name,
				"got ", color.Name)
		}

		if color.Model != expectedColor.Model {
			t.Error("expected initial color of Model ", expectedColor.Model,
				"got ", color.Model)
		}

		for j, _ := range expectedColor.Values {
			if color.Values[j] != expectedColor.Values[j] {
				t.Error("expected color value ", expectedColor.Values[j],
					"got ", color.Values[j])
			}
		}

		if color.Type != expectedColor.Type {
			t.Error("expected color type ", expectedColor.Type,
				"got ", color.Type)
		}
	}

	expectedAmountOfGroups := 1
	actualAmountOfGroups := len(ase.Groups)
	if actualAmountOfGroups != expectedAmountOfGroups {
		t.Error("expected ", expectedAmountOfGroups,
			"amount of groups, got: ", actualAmountOfGroups)
	}

	group := ase.Groups[0]

	if group.Name != testGroup.Name {
		t.Error("expected group name to be ", testGroup.Name,
			", got: ", group.Name)
	}

	for i, color := range group.Colors {
		expectedColor := testGroup.Colors[i]

		if color.Name != expectedColor.Name {
			t.Error("expected initial color with name ", expectedColor.Name,
				"got ", color.Name)
		}

		if color.Model != expectedColor.Model {
			t.Error("expected initial color of Model ", expectedColor.Model,
				"got ", color.Model)
		}

		for j, _ := range expectedColor.Values {
			if color.Values[j] != expectedColor.Values[j] {
				t.Error("expected color value ", expectedColor.Values[j],
					"got ", color.Values[j])
			}
		}

		if color.Type != expectedColor.Type {
			t.Error("expected color type ", expectedColor.Type,
				"got ", color.Type)
		}
	}

}
<|endoftext|>


<commit_before>package copy

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	. "github.com/otiai10/mint"
)

func TestMain(m *testing.M) {
	setup(m)
	code := m.Run()
	teardown(m)
	os.Exit(code)
}

func setup(m *testing.M) {
	os.MkdirAll("testdata.copy", os.ModePerm)
	os.Symlink("testdata/case01", "testdata/case03/case01")
	os.Chmod("testdata/case07/dir_0500", 0500)
	os.Chmod("testdata/case07/file_0444", 0444)
}

func teardown(m *testing.M) {
	os.RemoveAll("testdata/case03/case01")
	os.RemoveAll("testdata.copy")
	os.RemoveAll("testdata.copyTime")
}

func TestCopy(t *testing.T) {

	err := Copy("./testdata/case00", "./testdata.copy/case00")
	Expect(t, err).ToBe(nil)
	info, err := os.Stat("./testdata.copy/case00/README.md")
	Expect(t, err).ToBe(nil)
	Expect(t, info.IsDir()).ToBe(false)

	When(t, "specified src doesn't exist", func(t *testing.T) {
		err := Copy("NOT/EXISTING/SOURCE/PATH", "anywhere")
		Expect(t, err).Not().ToBe(nil)
	})

	When(t, "specified src is just a file", func(t *testing.T) {
		err := Copy("testdata/case01/README.md", "testdata.copy/case01/README.md")
		Expect(t, err).ToBe(nil)
	})

	When(t, "too long name is given", func(t *testing.T) {
		dest := "foobar"
		for i := 0; i < 8; i++ {
			dest = dest + dest
		}
		err := Copy("testdata/case00", filepath.Join("testdata/case00", dest))
		Expect(t, err).Not().ToBe(nil)
		Expect(t, err).TypeOf("*os.PathError")
	})

	When(t, "try to create not permitted location", func(t *testing.T) {
		if runtime.GOOS == "windows" {
			t.Skipf("FIXME: error IS nil here in Windows")
		}
		err := Copy("testdata/case00", "/case00")
		Expect(t, err).Not().ToBe(nil)
		Expect(t, err).TypeOf("*os.PathError")
	})

	When(t, "try to create a directory on existing file name", func(t *testing.T) {
		err := Copy("testdata/case02", "testdata.copy/case00/README.md")
		Expect(t, err).Not().ToBe(nil)
		Expect(t, err).TypeOf("*os.PathError")
	})

	When(t, "source directory includes symbolic link", func(t *testing.T) {
		err := Copy("testdata/case03", "testdata.copy/case03")
		Expect(t, err).ToBe(nil)
		info, err := os.Lstat("testdata.copy/case03/case01")
		Expect(t, err).ToBe(nil)
		Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(0)
	})

	When(t, "symlink with Opt.OnSymlink provided", func(t *testing.T) {
		opt := Options{OnSymlink: func(string) SymlinkAction { return Deep }}
		err := Copy("testdata/case03", "testdata.copy/case03.deep", opt)
		Expect(t, err).ToBe(nil)
		info, err := os.Lstat("testdata.copy/case03.deep/case01")
		Expect(t, err).ToBe(nil)
		Expect(t, info.Mode()&os.ModeSymlink).ToBe(os.FileMode(0))

		opt = Options{OnSymlink: func(string) SymlinkAction { return Shallow }}
		err = Copy("testdata/case03", "testdata.copy/case03.shallow", opt)
		Expect(t, err).ToBe(nil)
		info, err = os.Lstat("testdata.copy/case03.shallow/case01")
		Expect(t, err).ToBe(nil)
		Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0))

		opt = Options{OnSymlink: func(string) SymlinkAction { return Skip }}
		err = Copy("testdata/case03", "testdata.copy/case03.skip", opt)
		Expect(t, err).ToBe(nil)
		_, err = os.Stat("testdata.copy/case03.skip/case01")
		Expect(t, os.IsNotExist(err)).ToBe(true)

		err = Copy("testdata/case03", "testdata.copy/case03.default")
		Expect(t, err).ToBe(nil)
		info, err = os.Lstat("testdata.copy/case03.default/case01")
		Expect(t, err).ToBe(nil)
		Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0))

		opt = Options{OnSymlink: nil}
		err = Copy("testdata/case03", "testdata.copy/case03.not-specified", opt)
		Expect(t, err).ToBe(nil)
		info, err = os.Lstat("testdata.copy/case03.not-specified/case01")
		Expect(t, err).ToBe(nil)
		Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0))
	})

	When(t, "try to copy to an existing path", func(t *testing.T) {
		err := Copy("testdata/case03", "testdata.copy/case03")
		Expect(t, err).Not().ToBe(nil)
	})

	When(t, "try to copy READ-not-allowed source", func(t *testing.T) {
		err := Copy("testdata/doesNotExist", "testdata.copy/doesNotExist")
		Expect(t, err).Not().ToBe(nil)
	})

	When(t, "try to copy a file to existing path", func(t *testing.T) {
		err := Copy("testdata/case04/README.md", "testdata/case04")
		Expect(t, err).Not().ToBe(nil)
		err = Copy("testdata/case04/README.md", "testdata/case04/README.md/foobar")
		Expect(t, err).Not().ToBe(nil)
	})

	When(t, "try to copy a directory that has no write permission and copy file inside along with it", func(t *testing.T) {
		src := "testdata/case05"
		dest := "testdata.copy/case05"
		err := os.Chmod(src, os.FileMode(0555))
		Expect(t, err).ToBe(nil)
		err = Copy(src, dest)
		Expect(t, err).ToBe(nil)
		info, err := os.Lstat(dest)
		Expect(t, err).ToBe(nil)
		Expect(t, info.Mode().Perm()).ToBe(os.FileMode(0555))
		err = os.Chmod(dest, 0755)
		Expect(t, err).ToBe(nil)
	})

	When(t, "Options.Skip provided", func(t *testing.T) {
		opt := Options{Skip: func(src string) (bool, error) {
			switch {
			case strings.HasSuffix(src, "_skip"):
				return true, nil
			case strings.HasSuffix(src, ".gitfake"):
				return true, nil
			default:
				return false, nil
			}
		}}
		err := Copy("testdata/case06", "testdata.copy/case06", opt)
		Expect(t, err).ToBe(nil)
		info, err := os.Stat("./testdata.copy/case06/dir_skip")
		Expect(t, info).ToBe(nil)
		Expect(t, os.IsNotExist(err)).ToBe(true)

		info, err = os.Stat("./testdata.copy/case06/file_skip")
		Expect(t, info).ToBe(nil)
		Expect(t, os.IsNotExist(err)).ToBe(true)

		info, err = os.Stat("./testdata.copy/case06/README.md")
		Expect(t, info).Not().ToBe(nil)
		Expect(t, err).ToBe(nil)

		info, err = os.Stat("./testdata.copy/case06/repo/.gitfake")
		Expect(t, info).ToBe(nil)
		Expect(t, os.IsNotExist(err)).ToBe(true)

		info, err = os.Stat("./testdata.copy/case06/repo/README.md")
		Expect(t, info).Not().ToBe(nil)
		Expect(t, err).ToBe(nil)

		Because(t, "if Skip func returns error, Copy should be interrupted", func(t *testing.T) {
			errInsideSkipFunc := errors.New("Something wrong inside Skip")
			opt := Options{Skip: func(src string) (bool, error) {
				return false, errInsideSkipFunc
			}}
			err := Copy("testdata/case06", "testdata.copy/case06.01", opt)
			Expect(t, err).ToBe(errInsideSkipFunc)
			files, err := ioutil.ReadDir("./testdata.copy/case06.01")
			Expect(t, err).ToBe(nil)
			Expect(t, len(files)).ToBe(0)
		})
	})

	When(t, "Options.AddPermission provided", func(t *testing.T) {

		info, err := os.Stat("testdata/case07/dir_0500")
		Expect(t, err).ToBe(nil)
		Expect(t, info.Mode()).ToBe(os.FileMode(0500) | os.ModeDir)

		info, err = os.Stat("testdata/case07/file_0444")
		Expect(t, err).ToBe(nil)
		Expect(t, info.Mode()).ToBe(os.FileMode(0444))

		opt := Options{AddPermission: 0200}
		err = Copy("testdata/case07", "testdata.copy/case07", opt)
		Expect(t, err).ToBe(nil)

		info, err = os.Stat("testdata.copy/case07/dir_0500")
		Expect(t, err).ToBe(nil)
		Expect(t, info.Mode()).ToBe(os.FileMode(0500|0200) | os.ModeDir)

		info, err = os.Stat("testdata.copy/case07/file_0444")
		Expect(t, err).ToBe(nil)
		Expect(t, info.Mode()).ToBe(os.FileMode(0444 | 0200))
	})

	When(t, "Options.Sync provided", func(t *testing.T) {
		// With Sync option, each file will be flushed to storage on copying.
		// TODO: Since it's a bit hard to simulate real usecases here. This testcase is nonsense.
		opt := Options{Sync: true}
		err = Copy("testdata/case08", "testdata.copy/case08", opt)
		Expect(t, err).ToBe(nil)
	})

	When(t, "Options.PreserveTimes provided", func(t *testing.T) {

		err = Copy("testdata/case09", "testdata.copy/case09")
		Expect(t, err).ToBe(nil)
		opt := Options{PreserveTimes: true}
		err = Copy("testdata/case09", "testdata.copy/case09-preservetimes", opt)
		Expect(t, err).ToBe(nil)

		for _, entry := range []string{"", "README.md", "symlink"} {
			orig, err := os.Stat("testdata/case09/" + entry)
			Expect(t, err).ToBe(nil)
			plain, err := os.Stat("testdata.copy/case09/" + entry)
			Expect(t, err).ToBe(nil)
			preserved, err := os.Stat("testdata.copy/case09-preservetimes/" + entry)
			Expect(t, err).ToBe(nil)
			Expect(t, plain.ModTime().Unix()).Not().ToBe(orig.ModTime().Unix())
			Expect(t, preserved.ModTime().Unix()).ToBe(orig.ModTime().Unix())
		}

	})
}

func TestOptions_OnDirExists(t *testing.T) {
	err := Copy("testdata/case10/dest", "testdata.copy/case10/dest.1")
	Expect(t, err).ToBe(nil)
	err = Copy("testdata/case10/dest", "testdata.copy/case10/dest.2")
	Expect(t, err).ToBe(nil)
	err = Copy("testdata/case10/dest", "testdata.copy/case10/dest.3")
	Expect(t, err).ToBe(nil)

	opt := Options{}

	opt.OnDirExists = func(src, dest string) DirExistsAction {
		return Merge
	}
	err = Copy("testdata/case10/src", "testdata.copy/case10/dest.1", opt)
	Expect(t, err).ToBe(nil)
	err = Copy("testdata/case10/src", "testdata.copy/case10/dest.1", opt)
	Expect(t, err).ToBe(nil)
	b, err := ioutil.ReadFile("testdata.copy/case10/dest.1/" + "foo/" + "text_aaa")
	Expect(t, err).ToBe(nil)
	Expect(t, string(b)).ToBe("This is text_aaa from src")
	stat, err := os.Stat("testdata.copy/case10/dest.1/foo/text_eee")
	Expect(t, err).ToBe(nil)
	Expect(t, stat).Not().ToBe(nil)

	opt.OnDirExists = func(src, dest string) DirExistsAction {
		return Replace
	}
	err = Copy("testdata/case10/src", "testdata.copy/case10/dest.2", opt)
	Expect(t, err).ToBe(nil)
	err = Copy("testdata/case10/src", "testdata.copy/case10/dest.2", opt)
	Expect(t, err).ToBe(nil)
	b, err = ioutil.ReadFile("testdata.copy/case10/dest.2/" + "foo/" + "text_aaa")
	Expect(t, err).ToBe(nil)
	Expect(t, string(b)).ToBe("This is text_aaa from src")
	stat, err = os.Stat("testdata.copy/case10/dest.2/foo/text_eee")
	Expect(t, os.IsNotExist(err)).ToBe(true)
	Expect(t, stat).ToBe(nil)

	opt.OnDirExists = func(src, dest string) DirExistsAction {
		return Untouchable
	}
	err = Copy("testdata/case10/src", "testdata.copy/case10/dest.3", opt)
	Expect(t, err).ToBe(nil)
	b, err = ioutil.ReadFile("testdata.copy/case10/dest.3/" + "foo/" + "text_aaa")
	Expect(t, err).ToBe(nil)
	Expect(t, string(b)).ToBe("This is text_aaa from dest")
}
<commit_msg>Refactor tests<commit_after>package copy

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	. "github.com/otiai10/mint"
)

func TestMain(m *testing.M) {
	setup(m)
	code := m.Run()
	teardown(m)
	os.Exit(code)
}

func setup(m *testing.M) {
	os.MkdirAll("testdata.copy", os.ModePerm)
	os.Symlink("testdata/case01", "testdata/case03/case01")
	os.Chmod("testdata/case07/dir_0500", 0500)
	os.Chmod("testdata/case07/file_0444", 0444)
}

func teardown(m *testing.M) {
	os.RemoveAll("testdata/case03/case01")
	os.RemoveAll("testdata.copy")
	os.RemoveAll("testdata.copyTime")
}

func TestCopy(t *testing.T) {

	err := Copy("./testdata/case00", "./testdata.copy/case00")
	Expect(t, err).ToBe(nil)
	info, err := os.Stat("./testdata.copy/case00/README.md")
	Expect(t, err).ToBe(nil)
	Expect(t, info.IsDir()).ToBe(false)

	When(t, "specified src doesn't exist", func(t *testing.T) {
		err := Copy("NOT/EXISTING/SOURCE/PATH", "anywhere")
		Expect(t, err).Not().ToBe(nil)
	})

	When(t, "specified src is just a file", func(t *testing.T) {
		err := Copy("testdata/case01/README.md", "testdata.copy/case01/README.md")
		Expect(t, err).ToBe(nil)
	})

	When(t, "too long name is given", func(t *testing.T) {
		dest := "foobar"
		for i := 0; i < 8; i++ {
			dest = dest + dest
		}
		err := Copy("testdata/case00", filepath.Join("testdata/case00", dest))
		Expect(t, err).Not().ToBe(nil)
		Expect(t, err).TypeOf("*os.PathError")
	})

	When(t, "try to create not permitted location", func(t *testing.T) {
		if runtime.GOOS == "windows" {
			t.Skipf("FIXME: error IS nil here in Windows")
		}
		err := Copy("testdata/case00", "/case00")
		Expect(t, err).Not().ToBe(nil)
		Expect(t, err).TypeOf("*os.PathError")
	})

	When(t, "try to create a directory on existing file name", func(t *testing.T) {
		err := Copy("testdata/case02", "testdata.copy/case00/README.md")
		Expect(t, err).Not().ToBe(nil)
		Expect(t, err).TypeOf("*os.PathError")
	})

	When(t, "source directory includes symbolic link", func(t *testing.T) {
		err := Copy("testdata/case03", "testdata.copy/case03")
		Expect(t, err).ToBe(nil)
		info, err := os.Lstat("testdata.copy/case03/case01")
		Expect(t, err).ToBe(nil)
		Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(0)
	})

	When(t, "try to copy to an existing path", func(t *testing.T) {
		err := Copy("testdata/case03", "testdata.copy/case03")
		Expect(t, err).Not().ToBe(nil)
	})

	When(t, "try to copy READ-not-allowed source", func(t *testing.T) {
		err := Copy("testdata/doesNotExist", "testdata.copy/doesNotExist")
		Expect(t, err).Not().ToBe(nil)
	})

	When(t, "try to copy a file to existing path", func(t *testing.T) {
		err := Copy("testdata/case04/README.md", "testdata/case04")
		Expect(t, err).Not().ToBe(nil)
		err = Copy("testdata/case04/README.md", "testdata/case04/README.md/foobar")
		Expect(t, err).Not().ToBe(nil)
	})

	When(t, "try to copy a directory that has no write permission and copy file inside along with it", func(t *testing.T) {
		src := "testdata/case05"
		dest := "testdata.copy/case05"
		err := os.Chmod(src, os.FileMode(0555))
		Expect(t, err).ToBe(nil)
		err = Copy(src, dest)
		Expect(t, err).ToBe(nil)
		info, err := os.Lstat(dest)
		Expect(t, err).ToBe(nil)
		Expect(t, info.Mode().Perm()).ToBe(os.FileMode(0555))
		err = os.Chmod(dest, 0755)
		Expect(t, err).ToBe(nil)
	})

}

func TestOptions_OnSymlink(t *testing.T) {
	opt := Options{OnSymlink: func(string) SymlinkAction { return Deep }}
	err := Copy("testdata/case03", "testdata.copy/case03.deep", opt)
	Expect(t, err).ToBe(nil)
	info, err := os.Lstat("testdata.copy/case03.deep/case01")
	Expect(t, err).ToBe(nil)
	Expect(t, info.Mode()&os.ModeSymlink).ToBe(os.FileMode(0))

	opt = Options{OnSymlink: func(string) SymlinkAction { return Shallow }}
	err = Copy("testdata/case03", "testdata.copy/case03.shallow", opt)
	Expect(t, err).ToBe(nil)
	info, err = os.Lstat("testdata.copy/case03.shallow/case01")
	Expect(t, err).ToBe(nil)
	Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0))

	opt = Options{OnSymlink: func(string) SymlinkAction { return Skip }}
	err = Copy("testdata/case03", "testdata.copy/case03.skip", opt)
	Expect(t, err).ToBe(nil)
	_, err = os.Stat("testdata.copy/case03.skip/case01")
	Expect(t, os.IsNotExist(err)).ToBe(true)

	err = Copy("testdata/case03", "testdata.copy/case03.default")
	Expect(t, err).ToBe(nil)
	info, err = os.Lstat("testdata.copy/case03.default/case01")
	Expect(t, err).ToBe(nil)
	Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0))

	opt = Options{OnSymlink: nil}
	err = Copy("testdata/case03", "testdata.copy/case03.not-specified", opt)
	Expect(t, err).ToBe(nil)
	info, err = os.Lstat("testdata.copy/case03.not-specified/case01")
	Expect(t, err).ToBe(nil)
	Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0))
}

func TestOptions_Skip(t *testing.T) {
	opt := Options{Skip: func(src string) (bool, error) {
		switch {
		case strings.HasSuffix(src, "_skip"):
			return true, nil
		case strings.HasSuffix(src, ".gitfake"):
			return true, nil
		default:
			return false, nil
		}
	}}
	err := Copy("testdata/case06", "testdata.copy/case06", opt)
	Expect(t, err).ToBe(nil)
	info, err := os.Stat("./testdata.copy/case06/dir_skip")
	Expect(t, info).ToBe(nil)
	Expect(t, os.IsNotExist(err)).ToBe(true)

	info, err = os.Stat("./testdata.copy/case06/file_skip")
	Expect(t, info).ToBe(nil)
	Expect(t, os.IsNotExist(err)).ToBe(true)

	info, err = os.Stat("./testdata.copy/case06/README.md")
	Expect(t, info).Not().ToBe(nil)
	Expect(t, err).ToBe(nil)

	info, err = os.Stat("./testdata.copy/case06/repo/.gitfake")
	Expect(t, info).ToBe(nil)
	Expect(t, os.IsNotExist(err)).ToBe(true)

	info, err = os.Stat("./testdata.copy/case06/repo/README.md")
	Expect(t, info).Not().ToBe(nil)
	Expect(t, err).ToBe(nil)

	Because(t, "if Skip func returns error, Copy should be interrupted", func(t *testing.T) {
		errInsideSkipFunc := errors.New("Something wrong inside Skip")
		opt := Options{Skip: func(src string) (bool, error) {
			return false, errInsideSkipFunc
		}}
		err := Copy("testdata/case06", "testdata.copy/case06.01", opt)
		Expect(t, err).ToBe(errInsideSkipFunc)
		files, err := ioutil.ReadDir("./testdata.copy/case06.01")
		Expect(t, err).ToBe(nil)
		Expect(t, len(files)).ToBe(0)
	})
}

func TestOptions_AddPermission(t *testing.T) {
	info, err := os.Stat("testdata/case07/dir_0500")
	Expect(t, err).ToBe(nil)
	Expect(t, info.Mode()).ToBe(os.FileMode(0500) | os.ModeDir)

	info, err = os.Stat("testdata/case07/file_0444")
	Expect(t, err).ToBe(nil)
	Expect(t, info.Mode()).ToBe(os.FileMode(0444))

	opt := Options{AddPermission: 0200}
	err = Copy("testdata/case07", "testdata.copy/case07", opt)
	Expect(t, err).ToBe(nil)

	info, err = os.Stat("testdata.copy/case07/dir_0500")
	Expect(t, err).ToBe(nil)
	Expect(t, info.Mode()).ToBe(os.FileMode(0500|0200) | os.ModeDir)

	info, err = os.Stat("testdata.copy/case07/file_0444")
	Expect(t, err).ToBe(nil)
	Expect(t, info.Mode()).ToBe(os.FileMode(0444 | 0200))
}

func TestOptions_Sync(t *testing.T) {
	// With Sync option, each file will be flushed to storage on copying.
	// TODO: Since it's a bit hard to simulate real usecases here. This testcase is nonsense.
	opt := Options{Sync: true}
	err := Copy("testdata/case08", "testdata.copy/case08", opt)
	Expect(t, err).ToBe(nil)
}

func TestOptions_PreserveTimes(t *testing.T) {
	err := Copy("testdata/case09", "testdata.copy/case09")
	Expect(t, err).ToBe(nil)
	opt := Options{PreserveTimes: true}
	err = Copy("testdata/case09", "testdata.copy/case09-preservetimes", opt)
	Expect(t, err).ToBe(nil)

	for _, entry := range []string{"", "README.md", "symlink"} {
		orig, err := os.Stat("testdata/case09/" + entry)
		Expect(t, err).ToBe(nil)
		plain, err := os.Stat("testdata.copy/case09/" + entry)
		Expect(t, err).ToBe(nil)
		preserved, err := os.Stat("testdata.copy/case09-preservetimes/" + entry)
		Expect(t, err).ToBe(nil)
		Expect(t, plain.ModTime().Unix()).Not().ToBe(orig.ModTime().Unix())
		Expect(t, preserved.ModTime().Unix()).ToBe(orig.ModTime().Unix())
	}
}

func TestOptions_OnDirExists(t *testing.T) {
	err := Copy("testdata/case10/dest", "testdata.copy/case10/dest.1")
	Expect(t, err).ToBe(nil)
	err = Copy("testdata/case10/dest", "testdata.copy/case10/dest.2")
	Expect(t, err).ToBe(nil)
	err = Copy("testdata/case10/dest", "testdata.copy/case10/dest.3")
	Expect(t, err).ToBe(nil)

	opt := Options{}

	opt.OnDirExists = func(src, dest string) DirExistsAction {
		return Merge
	}
	err = Copy("testdata/case10/src", "testdata.copy/case10/dest.1", opt)
	Expect(t, err).ToBe(nil)
	err = Copy("testdata/case10/src", "testdata.copy/case10/dest.1", opt)
	Expect(t, err).ToBe(nil)
	b, err := ioutil.ReadFile("testdata.copy/case10/dest.1/" + "foo/" + "text_aaa")
	Expect(t, err).ToBe(nil)
	Expect(t, string(b)).ToBe("This is text_aaa from src")
	stat, err := os.Stat("testdata.copy/case10/dest.1/foo/text_eee")
	Expect(t, err).ToBe(nil)
	Expect(t, stat).Not().ToBe(nil)

	opt.OnDirExists = func(src, dest string) DirExistsAction {
		return Replace
	}
	err = Copy("testdata/case10/src", "testdata.copy/case10/dest.2", opt)
	Expect(t, err).ToBe(nil)
	err = Copy("testdata/case10/src", "testdata.copy/case10/dest.2", opt)
	Expect(t, err).ToBe(nil)
	b, err = ioutil.ReadFile("testdata.copy/case10/dest.2/" + "foo/" + "text_aaa")
	Expect(t, err).ToBe(nil)
	Expect(t, string(b)).ToBe("This is text_aaa from src")
	stat, err = os.Stat("testdata.copy/case10/dest.2/foo/text_eee")
	Expect(t, os.IsNotExist(err)).ToBe(true)
	Expect(t, stat).ToBe(nil)

	opt.OnDirExists = func(src, dest string) DirExistsAction {
		return Untouchable
	}
	err = Copy("testdata/case10/src", "testdata.copy/case10/dest.3", opt)
	Expect(t, err).ToBe(nil)
	b, err = ioutil.ReadFile("testdata.copy/case10/dest.3/" + "foo/" + "text_aaa")
	Expect(t, err).ToBe(nil)
	Expect(t, string(b)).ToBe("This is text_aaa from dest")
}
<|endoftext|>


<commit_before>package appdeploy

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"
	"time"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"

	"github.com/eapache/go-resiliency/retrier"
	"github.com/rubenv/kube-appdeploy/kubectl"
)

var CleanTypes = []string{
	"deployment",
	"service",
	"cronjob",
}

type Target interface {
	Prepare(vars *ProcessVariables) error
	Apply(m Manifest, data []byte) error
	Cleanup(items []Manifest) error
}

// ---------- Folder ----------

type FolderTarget struct {
	Path string
}

var _ Target = &FolderTarget{}

func NewFolderTarget(path string) *FolderTarget {
	return &FolderTarget{
		Path: path,
	}
}

func (t *FolderTarget) Prepare(vars *ProcessVariables) error {
	return os.MkdirAll(t.Path, 0755)
}

func (t *FolderTarget) Apply(m Manifest, data []byte) error {
	return ioutil.WriteFile(m.Filename(t.Path), data, 0644)
}

func (t *FolderTarget) Cleanup(items []Manifest) error {
	files, err := ioutil.ReadDir(t.Path)
	if err != nil {
		return err
	}

	filenames := make([]string, 0, len(items))
	for _, item := range items {
		filenames = append(filenames, item.Filename(""))
	}

	for _, file := range files {
		if file.IsDir() {
			continue
		}

		name := file.Name()
		sep := strings.Index(name, "--")
		if sep < 0 {
			continue
		}

		prefix := name[0:sep]
		found := false
		for _, t := range CleanTypes {
			if t == prefix {
				found = true
			}
		}
		if !found {
			continue
		}

		known := false
		for _, f := range filenames {
			if f == name {
				known = true
			}
		}

		if !known {
			err = os.Remove(path.Join(t.Path, name))
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// ---------- Kubernetes ----------

type KubernetesTarget struct {
	config         *rest.Config
	client         *kubernetes.Clientset
	kubectl        *kubectl.KubeCtl
	namespace      string
	manageCronjobs bool
}

var _ Target = &KubernetesTarget{}

func NewKubernetesTarget(config *rest.Config) *KubernetesTarget {
	return &KubernetesTarget{
		config: config,
	}
}

func (t *KubernetesTarget) Prepare(vars *ProcessVariables) error {
	client, err := kubernetes.NewForConfig(t.config)
	if err != nil {
		return err
	}

	t.client = client

	// Copy some vars
	t.namespace = vars.Namespace
	t.kubectl = kubectl.NewKubeCtl(t.config, t.namespace)
	t.manageCronjobs = vars.ManageCronjobs

	// Ensure we have the needed namespace
	nsClient := t.client.Core().Namespaces()

	create := false
	_, err = nsClient.Get(t.namespace, metav1.GetOptions{})
	if err != nil {
		ignore := false
		if e, ok := err.(*errors.StatusError); ok {
			if e.ErrStatus.Reason == "NotFound" {
				ignore = true
				create = true
			}
		}
		if !ignore {
			return err
		}
	}
	if create {
		_, err = nsClient.Create(&v1.Namespace{
			ObjectMeta: metav1.ObjectMeta{
				Name: t.namespace,
			},
		})
		if err != nil {
			return err
		}
	}

	// Add the image pull secrets
	if len(vars.ImagePullSecrets) > 0 {
		saClient := t.client.Core().ServiceAccounts(t.namespace)

		var sa *v1.ServiceAccount
		// Account isn't always available right away, but it gets created in the end, just wait for it
		r := retrier.New(retrier.ConstantBackoff(10, 1*time.Second), nil)
		err := r.Run(func() error {
			s, err := saClient.Get("default", metav1.GetOptions{})
			if err != nil {
				return err
			}
			if s == nil {
				return fmt.Errorf("Service account not found (yet)")
			}
			sa = s
			return nil
		})
		if err != nil {
			return err
		}

		secrets := make([]v1.LocalObjectReference, 0)
		for _, s := range vars.ImagePullSecrets {
			secrets = append(secrets, v1.LocalObjectReference{
				Name: s,
			})
		}

		sa.ImagePullSecrets = secrets
		_, err = saClient.Update(sa)
		if err != nil {
			return err
		}
	}

	return nil
}

func (t *KubernetesTarget) Apply(m Manifest, data []byte) error {
	// Temporary fix for https://github.com/kubernetes/kubernetes/issues/35149
	// If a cronjob is applied, an error occurs
	// Thus we delete the cronjob first if it exists
	if m.Kind == "CronJob" {
		out, err := t.runKubeCtl(nil, "get", "cronjob", "-o", "name")
		if err != nil {
			return err
		}
		lines := strings.Split(strings.TrimSpace(out), "\n")
		searchline := fmt.Sprintf("cronjob/%s", m.Metadata.Name)
		for _, line := range lines {
			if line == searchline {
				_, err := t.runKubeCtl(nil, "delete", "cronjob", m.Metadata.Name)
				if err != nil {
					return err
				}
				break
			}
		}
	}
	_, err := t.runKubeCtl(data, "apply", "-f", "-")
	return err
}

func (t *KubernetesTarget) Cleanup(items []Manifest) error {
	for _, ct := range CleanTypes {
		if ct == "cronjob" && !t.manageCronjobs {
			continue
		}
		err := t.cleanType(items, ct)
		if err != nil {
			return err
		}
	}
	return nil
}

type itemList struct {
	Items []struct {
		Kind     string `json:"kind"`
		Metadata struct {
			Name string `json:"name"`
		} `json:"metadata"`
	} `json:"items"`
}

func (t *KubernetesTarget) cleanType(items []Manifest, ct string) error {
	out, err := t.runKubeCtl(nil, "get", ct, "-o", "json")
	if err != nil {
		return err
	}

	known := []string{}
	for _, m := range items {
		if strings.ToLower(m.Kind) == ct {
			known = append(known, fmt.Sprintf("%s/%s", ct, m.Metadata.Name))
		}
	}

	seen := &itemList{}
	err = json.Unmarshal([]byte(out), &seen)
	if err != nil {
		return err
	}

	for _, item := range seen.Items {
		found := false
		key := fmt.Sprintf("%s/%s", strings.ToLower(item.Kind), item.Metadata.Name)
		for _, k := range known {
			if key == k {
				found = true
			}
		}

		if !found {
			log.Printf("Deleting %s", key)
			_, err := t.runKubeCtl(nil, "delete", item.Kind, item.Metadata.Name)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (t *KubernetesTarget) runKubeCtl(stdin []byte, args ...string) (string, error) {
	return t.kubectl.Run(stdin, args...)
}
<commit_msg>Improve error<commit_after>package appdeploy

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"
	"time"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"

	"github.com/eapache/go-resiliency/retrier"
	"github.com/rubenv/kube-appdeploy/kubectl"
)

var CleanTypes = []string{
	"deployment",
	"service",
	"cronjob",
}

type Target interface {
	Prepare(vars *ProcessVariables) error
	Apply(m Manifest, data []byte) error
	Cleanup(items []Manifest) error
}

// ---------- Folder ----------

type FolderTarget struct {
	Path string
}

var _ Target = &FolderTarget{}

func NewFolderTarget(path string) *FolderTarget {
	return &FolderTarget{
		Path: path,
	}
}

func (t *FolderTarget) Prepare(vars *ProcessVariables) error {
	return os.MkdirAll(t.Path, 0755)
}

func (t *FolderTarget) Apply(m Manifest, data []byte) error {
	return ioutil.WriteFile(m.Filename(t.Path), data, 0644)
}

func (t *FolderTarget) Cleanup(items []Manifest) error {
	files, err := ioutil.ReadDir(t.Path)
	if err != nil {
		return err
	}

	filenames := make([]string, 0, len(items))
	for _, item := range items {
		filenames = append(filenames, item.Filename(""))
	}

	for _, file := range files {
		if file.IsDir() {
			continue
		}

		name := file.Name()
		sep := strings.Index(name, "--")
		if sep < 0 {
			continue
		}

		prefix := name[0:sep]
		found := false
		for _, t := range CleanTypes {
			if t == prefix {
				found = true
			}
		}
		if !found {
			continue
		}

		known := false
		for _, f := range filenames {
			if f == name {
				known = true
			}
		}

		if !known {
			err = os.Remove(path.Join(t.Path, name))
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// ---------- Kubernetes ----------

type KubernetesTarget struct {
	config         *rest.Config
	client         *kubernetes.Clientset
	kubectl        *kubectl.KubeCtl
	namespace      string
	manageCronjobs bool
}

var _ Target = &KubernetesTarget{}

func NewKubernetesTarget(config *rest.Config) *KubernetesTarget {
	return &KubernetesTarget{
		config: config,
	}
}

func (t *KubernetesTarget) Prepare(vars *ProcessVariables) error {
	client, err := kubernetes.NewForConfig(t.config)
	if err != nil {
		return err
	}

	t.client = client

	// Copy some vars
	t.namespace = vars.Namespace
	t.kubectl = kubectl.NewKubeCtl(t.config, t.namespace)
	t.manageCronjobs = vars.ManageCronjobs

	// Ensure we have the needed namespace
	nsClient := t.client.Core().Namespaces()

	create := false
	_, err = nsClient.Get(t.namespace, metav1.GetOptions{})
	if err != nil {
		ignore := false
		if e, ok := err.(*errors.StatusError); ok {
			if e.ErrStatus.Reason == "NotFound" {
				ignore = true
				create = true
			}
		}
		if !ignore {
			return err
		}
	}
	if create {
		_, err = nsClient.Create(&v1.Namespace{
			ObjectMeta: metav1.ObjectMeta{
				Name: t.namespace,
			},
		})
		if err != nil {
			return err
		}
	}

	// Add the image pull secrets
	if len(vars.ImagePullSecrets) > 0 {
		saClient := t.client.Core().ServiceAccounts(t.namespace)

		var sa *v1.ServiceAccount
		// Account isn't always available right away, but it gets created in the end, just wait for it
		r := retrier.New(retrier.ConstantBackoff(10, 1*time.Second), nil)
		err := r.Run(func() error {
			s, err := saClient.Get("default", metav1.GetOptions{})
			if err != nil {
				return err
			}
			if s == nil {
				return fmt.Errorf("Service account not found (yet)")
			}
			sa = s
			return nil
		})
		if err != nil {
			return err
		}

		secrets := make([]v1.LocalObjectReference, 0)
		for _, s := range vars.ImagePullSecrets {
			secrets = append(secrets, v1.LocalObjectReference{
				Name: s,
			})
		}

		sa.ImagePullSecrets = secrets
		_, err = saClient.Update(sa)
		if err != nil {
			return err
		}
	}

	return nil
}

func (t *KubernetesTarget) Apply(m Manifest, data []byte) error {
	// Temporary fix for https://github.com/kubernetes/kubernetes/issues/35149
	// If a cronjob is applied, an error occurs
	// Thus we delete the cronjob first if it exists
	if m.Kind == "CronJob" {
		out, err := t.runKubeCtl(nil, "get", "cronjob", "-o", "name")
		if err != nil {
			return fmt.Errorf("Cronjob fetch failed: %s\n\nFor manifest:\n%s", err, string(data))
		}
		lines := strings.Split(strings.TrimSpace(out), "\n")
		searchline := fmt.Sprintf("cronjob/%s", m.Metadata.Name)
		for _, line := range lines {
			if line == searchline {
				_, err := t.runKubeCtl(nil, "delete", "cronjob", m.Metadata.Name)
				if err != nil {
					return err
				}
				break
			}
		}
	}
	_, err := t.runKubeCtl(data, "apply", "-f", "-")
	return err
}

func (t *KubernetesTarget) Cleanup(items []Manifest) error {
	for _, ct := range CleanTypes {
		if ct == "cronjob" && !t.manageCronjobs {
			continue
		}
		err := t.cleanType(items, ct)
		if err != nil {
			return err
		}
	}
	return nil
}

type itemList struct {
	Items []struct {
		Kind     string `json:"kind"`
		Metadata struct {
			Name string `json:"name"`
		} `json:"metadata"`
	} `json:"items"`
}

func (t *KubernetesTarget) cleanType(items []Manifest, ct string) error {
	out, err := t.runKubeCtl(nil, "get", ct, "-o", "json")
	if err != nil {
		return err
	}

	known := []string{}
	for _, m := range items {
		if strings.ToLower(m.Kind) == ct {
			known = append(known, fmt.Sprintf("%s/%s", ct, m.Metadata.Name))
		}
	}

	seen := &itemList{}
	err = json.Unmarshal([]byte(out), &seen)
	if err != nil {
		return err
	}

	for _, item := range seen.Items {
		found := false
		key := fmt.Sprintf("%s/%s", strings.ToLower(item.Kind), item.Metadata.Name)
		for _, k := range known {
			if key == k {
				found = true
			}
		}

		if !found {
			log.Printf("Deleting %s", key)
			_, err := t.runKubeCtl(nil, "delete", item.Kind, item.Metadata.Name)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (t *KubernetesTarget) runKubeCtl(stdin []byte, args ...string) (string, error) {
	return t.kubectl.Run(stdin, args...)
}
<|endoftext|>


<commit_before>package caspercloud

import (
	"encoding/json"
	"github.com/PuerkitoBio/goquery"
	"github.com/xlvector/dlog"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"io/ioutil"
	"math/rand"
	"os"
	"strings"
	"time"
)

const (
	kMaxTryCount = 5
)

type Mail struct {
	From  string `json:"from"`
	Title string `json:"title"`
}

type CasperOutput struct {
	Downloads []string `json:"downloads"`
	Mails     []Mail   `json:"mails"`
	Status    string   `json:"status"`
}

func LoadDownloads(fs []string) {
	for _, fn := range fs {
		ParseFile(fn)
	}
}

func ParseFile(fn string) error {
	f, err := os.Open(fn)
	if err != nil {
		dlog.Warn("fail to load file:%s", err.Error())
		return err
	}
	defer f.Close()

	doc, err := goquery.NewDocumentFromReader(f)
	if err != nil {
		dlog.Warn("fail to get dom:%s", err.Error())
		return err
	}
	dlog.Info("file length:%d", len(doc.Text()))
	return nil
}

type Analyzer struct {
	ServerList []string `json:"server_list"`
	random     *rand.Rand
}

func NewAnalyzer(path string) *Analyzer {
	text, err := ioutil.ReadFile(path)
	if err != nil {
		dlog.Warn("read %s get error:%s", path, err.Error())
		return nil
	}
	ret := Analyzer{}
	err = json.Unmarshal(text, &ret)
	if err != nil {
		panic(err)
	}

	if len(ret.ServerList) == 0 {
		return nil
	}
	ret.random = rand.New(rand.NewSource(time.Now().UnixNano()))
	return &ret
}

func (p *Analyzer) SendReq(req *ParseRequest) bool {
	for i := 0; i < kMaxTryCount; i++ {
		index := p.random.Intn(len(p.ServerList))
		conn, err := grpc.Dial(p.ServerList[index])
		if err != nil {
			dlog.Warn("dial server get error:%s", err.Error())
			time.Sleep(1 * time.Second)
			continue
		}
		if conn != nil {
			defer conn.Close()
		}

		client := NewParserClient(conn)

		reply, err := client.ProcessParseRequest(context.Background(), req)
		if err != nil {
			dlog.Warn("call get error:%s", err.Error())
			time.Sleep(1 * time.Second)
			continue
		}
		dlog.Println("get server reply:", *reply)
		return true
	}
	return false
}

func (p *Analyzer) getPathLastPart(path string) string {
	segs := strings.Split(path, "/")
	if len(segs) >= 1 {
		return segs[len(segs)-1]
	}
	return path
}

func (p *Analyzer) Process(req *ParseRequest, downloads []string) bool {
	for _, fn := range downloads {

		f, err := os.Open(fn)
		if err != nil {
			dlog.Warn("open file get error:%s", err.Error())
			continue
		}

		fd, err := ioutil.ReadAll(f)
		if err != nil {
			dlog.Warn("read file get error:%s", err.Error())
			continue
		}
		fd = strings.Trim(fd, " \n\r\t")

		if strings.HasSuffix(fn, ".zip") {
			req.IsZip = true
		}

		req.Data = append(req.Data, string(fd))
		req.DataMetaInfo = append(req.DataMetaInfo, p.getPathLastPart(fn))

		f.Close()
	}
	return p.SendReq(req)
}
<commit_msg>trim file<commit_after>package caspercloud

import (
	"encoding/json"
	"github.com/PuerkitoBio/goquery"
	"github.com/xlvector/dlog"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"io/ioutil"
	"math/rand"
	"os"
	"strings"
	"time"
)

const (
	kMaxTryCount = 5
)

type Mail struct {
	From  string `json:"from"`
	Title string `json:"title"`
}

type CasperOutput struct {
	Downloads []string `json:"downloads"`
	Mails     []Mail   `json:"mails"`
	Status    string   `json:"status"`
}

func LoadDownloads(fs []string) {
	for _, fn := range fs {
		ParseFile(fn)
	}
}

func ParseFile(fn string) error {
	f, err := os.Open(fn)
	if err != nil {
		dlog.Warn("fail to load file:%s", err.Error())
		return err
	}
	defer f.Close()

	doc, err := goquery.NewDocumentFromReader(f)
	if err != nil {
		dlog.Warn("fail to get dom:%s", err.Error())
		return err
	}
	dlog.Info("file length:%d", len(doc.Text()))
	return nil
}

type Analyzer struct {
	ServerList []string `json:"server_list"`
	random     *rand.Rand
}

func NewAnalyzer(path string) *Analyzer {
	text, err := ioutil.ReadFile(path)
	if err != nil {
		dlog.Warn("read %s get error:%s", path, err.Error())
		return nil
	}
	ret := Analyzer{}
	err = json.Unmarshal(text, &ret)
	if err != nil {
		panic(err)
	}

	if len(ret.ServerList) == 0 {
		return nil
	}
	ret.random = rand.New(rand.NewSource(time.Now().UnixNano()))
	return &ret
}

func (p *Analyzer) SendReq(req *ParseRequest) bool {
	for i := 0; i < kMaxTryCount; i++ {
		index := p.random.Intn(len(p.ServerList))
		conn, err := grpc.Dial(p.ServerList[index])
		if err != nil {
			dlog.Warn("dial server get error:%s", err.Error())
			time.Sleep(1 * time.Second)
			continue
		}
		if conn != nil {
			defer conn.Close()
		}

		client := NewParserClient(conn)

		reply, err := client.ProcessParseRequest(context.Background(), req)
		if err != nil {
			dlog.Warn("call get error:%s", err.Error())
			time.Sleep(1 * time.Second)
			continue
		}
		dlog.Println("get server reply:", *reply)
		return true
	}
	return false
}

func (p *Analyzer) getPathLastPart(path string) string {
	segs := strings.Split(path, "/")
	if len(segs) >= 1 {
		return segs[len(segs)-1]
	}
	return path
}

func (p *Analyzer) Process(req *ParseRequest, downloads []string) bool {
	for _, fn := range downloads {

		f, err := os.Open(fn)
		if err != nil {
			dlog.Warn("open file get error:%s", err.Error())
			continue
		}

		fd, err := ioutil.ReadAll(f)
		if err != nil {
			dlog.Warn("read file get error:%s", err.Error())
			continue
		}
		fdstr = string(fd)
		fdstr = strings.Trim(fdstr, " \n\r\t")

		if strings.HasSuffix(fn, ".zip") {
			req.IsZip = true
		}

		req.Data = append(req.Data, fdstr)
		req.DataMetaInfo = append(req.DataMetaInfo, p.getPathLastPart(fn))

		f.Close()
	}
	return p.SendReq(req)
}
<|endoftext|>


<commit_before>package util

const (
	VERSION = "0.70 beta 20150414"
)
<commit_msg>Adjust versioning. Beta version should not have a date.<commit_after>package util

const (
	VERSION = "0.70 beta"
)
<|endoftext|>


<commit_before>// Package autosite defines Google App Engine sites automatically
// based on file structure.
//
// See https://github.com/hkjn/hkjnweb for a setup (that implements
// http://www.hkjn.me / http://blog.hkjn.me) using this package.
//
// Example usage:
//   mysite := New(
//     "Some title",   // for HTML <head>
//     "pages/*.tmpl", // pattern for pages on disk
//     "domain.com",   // live domain
//     []string{       // shared templates
//       "base.tmpl",
//       "other.tmpl",
//     },
//   )
//   mysite.Register()
//
// This will host pages like domain.com/Foo and /Bar if there's
// files pages/Foo.tmpl and pages/Bar.tmpl relative to the calling
// package, also using "base.tmpl" and "other.tmpl" to compile the
// templates for rendering those pages.
//
// The following data is available within each template:
//   {{.Title}}: The <title> of the page.
//   {{.Date.Year}}, {{.Date.Month}}: Year and month that the page was
//      published, if file pattern includes it.
//   {{.URI}}: URI to the page.
//   {{.IsLive}}: Whether the page is live, via !appengine.IsDevAppServer().
package autosite

import (
	"fmt"
	"html/template"
	"log"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"appengine"
)

// BaseTemplate is the name of the top-level template to invoke for each page.
var BaseTemplate = "base"

// New creates a new autosite.
//
// New panics on errors reading templates.
func New(title, glob, live string, templates []string) Site {
	s := Site{
		title:     title,
		live:      live,
		glob:      glob,
		templates: templates,
	}
	err := s.read()
	if err != nil {
		log.Fatalf(err.Error())
	}
	return s
}

// ChangeURI changes the URI a page will be served on.
//
// ChangeURI panics if the old URI is not registered.
func (s *Site) ChangeURI(uri, newURI string) {
	p, ok := s.pages[uri]
	if !ok {
		log.Fatalf("no page with URI %v\n", uri)
	}
	p.URI = newURI
	delete(s.pages, uri)
	s.pages[newURI] = p
	log.Printf("remapped %v to %v\n", p, newURI)
}

// Register registers the HTTP handlers for the site.
func (s Site) Register() {
	for uri, p := range s.pages {
		if appengine.IsDevAppServer() {
			http.Handle(uri, p)
		} else {
			http.Handle(fmt.Sprintf("%s%s", s.live, p.URI), p)
		}
		log.Printf("registered handler %s: %+v\n", p.URI, p)
	}
}

// Site represents an autosite.
type Site struct {
	live      string          // live domain
	title     string          // title of the domain, for HTML <head>
	glob      string          // file glob for page templates
	templates []string        // templates needed for all endpoints
	pages     map[string]page // URI -> page mapping
}

// page is a HTML resource.
type page struct {
	Title  string      // title, for <head>
	Date   date        // publishing date
	URI    string      // URI path
	IsLive bool        // true if the site is live
	Data   interface{} // custom data, if any

	tmpl *template.Template // backing template
}

type year int

// date is a rough point in time.
type date struct {
	Year  year
	Month time.Month
}

// before says whether this date is before other date.
func (d date) before(other date) bool {
	if d.Year < other.Year {
		return true
	} else if d.Year == other.Year {
		return d.Month < other.Month
	}
	return false
}

// ServeHTTP serves the page.
func (p page) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	c.Infof("%+v will ServeHTTP for URI %s\n", p, r.RequestURI)

	if p.URI != r.RequestURI {
		c.Errorf("bad request URI %s, want %s; serving 404\n", r.RequestURI, p.URI)
		http.NotFound(w, r)
		return
	}

	err := p.tmpl.ExecuteTemplate(w, BaseTemplate, p)
	if err != nil {
		http.Error(w, "Internal server error.", http.StatusInternalServerError)
		log.Fatal(err.Error())
		return
	}
}

// String provides a string representation of the page.
func (p page) String() string {
	r := fmt.Sprintf("page [%s]", p.URI)
	if p.Date.Year != 0 {
		r += fmt.Sprintf(", published on %v", p.Date.Year)
		if p.Date.Month != 0 {
			r += fmt.Sprintf(", %v", p.Date.Month)
		}
	}
	return r
}

// read reads pages to serve on the autosite from disk
func (s *Site) read() error {
	filePaths, err := s.getFiles()
	if err != nil {
		return err
	}
	s.pages = make(map[string]page)
	for _, tmplPath := range filePaths {
		uri, d, err := parsePath(tmplPath)
		if err != nil {
			return err
		}
		s.addPage(uri, d, nil, append(s.templates, tmplPath))
	}
	return nil
}

// getFiles retrieves all pages' file paths from disk.
func (s Site) getFiles() ([]string, error) {
	paths, err := filepath.Glob(s.glob)
	if err != nil {
		return []string{}, err
	}
	if len(paths) == 0 {
		return []string{}, fmt.Errorf("no pages found")
	}

	// Skip files with dot prefixes (e.g. .#foo.tmpl).
	r := make([]string, len(paths))
	i := 0
	for _, p := range paths {
		if strings.Contains(p, ".#") {
			continue
		}
		r[i] = p
		i++
	}
	return r[0:i], nil
}

// parsePath extracts URI and date from a template file path.
func parsePath(p string) (uri string, d date, err error) {
	parts := strings.Split(p, "/")
	if len(parts) == 2 {
		// Assumes [dir]/*.tmpl; i.e. no date.
		uri = fmt.Sprintf("/%s", strings.TrimSuffix(parts[1], ".tmpl"))
	} else if len(parts) == 4 {
		// Assumes [dir]/[yyyy]/[mm]/*.tmpl; i.e. date is present.
		uri = "/" + strings.Join([]string{
			parts[1],
			parts[2],
			strings.TrimSuffix(parts[3], ".tmpl")}, "/")
		d, err = getDate(parts[1], parts[2])
		if err != nil {
			return
		}
	} else {
		err = fmt.Errorf("bad template path: %s", p)
		return
	}
	return uri, d, nil
}

// getDate extracts the date of the post from year and month strings.
func getDate(y, m string) (date, error) {
	y64, err := strconv.ParseInt(y, 10, 0)
	if err != nil || y64 <= 1900 || y64 >= 99999 {
		return date{}, fmt.Errorf("bad year: %v", y)
	}
	month, err := strconv.ParseInt(m, 10, 0)
	if err != nil || month < 1 || month > 12 {
		return date{}, fmt.Errorf("bad month: %v", m)
	}
	return date{
		Year:  year(y64),
		Month: time.Month(month),
	}, nil
}

// addPage adds a page to the autosite.
func (s *Site) addPage(uri string, d date, data interface{}, tmpls []string) {
	t := template.Must(template.ParseFiles(tmpls...))
	p := page{
		Title:  s.title,
		URI:    uri,
		Data:   data,
		Date:   d,
		IsLive: !appengine.IsDevAppServer(),
		tmpl:   t,
	}
	s.pages[p.URI] = p
}
<commit_msg>makes 'live' + 'domain' template functions instead of page data (to not require passing them in to all templates)<commit_after>// Package autosite defines Google App Engine sites automatically
// based on file structure.
//
// See https://github.com/hkjn/hkjnweb for a setup (that implements
// http://www.hkjn.me / http://blog.hkjn.me) using this package.
//
// Example usage:
//   mysite := New(
//     "Some title",   // for HTML <head>
//     "pages/*.tmpl", // pattern for pages on disk
//     "domain.com",   // live domain
//     []string{       // shared templates
//       "base.tmpl",
//       "other.tmpl",
//     },
//   )
//   mysite.Register()
//
// This will host pages like domain.com/Foo and /Bar if there's
// files pages/Foo.tmpl and pages/Bar.tmpl relative to the calling
// package, also using "base.tmpl" and "other.tmpl" to compile the
// templates for rendering those pages.
//
// The following data is available within each template:
//   {{.Title}}: The <title> of the page.
//   {{.Date.Year}}, {{.Date.Month}}: Year and month that the page was
//      published, if file pattern includes it.
//   {{.URI}}: URI to the page.
//
// The following functions are available within templates, in addition
// to the usual ones:
//   {{live}}: Whether the page is live, via !appengine.IsDevAppServer().
//   {{domain}}: When live, the live domain of the page, otherwise empty string.
package autosite

import (
	"fmt"
	"html/template"
	"log"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"appengine"
)

// BaseTemplate is the name of the top-level template to invoke for each page.
var BaseTemplate = "base"

// New creates a new autosite.
//
// New panics on errors reading templates.
func New(title, glob, liveDomain string, templates []string) Site {
	s := Site{
		title:      title,
		liveDomain: liveDomain,
		glob:       glob,
		templates:  templates,
	}
	err := s.read()
	if err != nil {
		log.Fatalf(err.Error())
	}
	return s
}

// ChangeURI changes the URI a page will be served on.
//
// ChangeURI panics if the old URI is not registered.
func (s *Site) ChangeURI(uri, newURI string) {
	p, ok := s.pages[uri]
	if !ok {
		log.Fatalf("no page with URI %v\n", uri)
	}
	p.URI = newURI
	delete(s.pages, uri)
	s.pages[newURI] = p
	log.Printf("remapped %v to %v\n", p, newURI)
}

// Register registers the HTTP handlers for the site.
func (s Site) Register() {
	for uri, p := range s.pages {
		if appengine.IsDevAppServer() {
			http.Handle(uri, p)
		} else {
			http.Handle(fmt.Sprintf("%s%s", s.liveDomain, p.URI), p)
		}
		log.Printf("registered handler %s: %+v\n", p.URI, p)
	}
}

// Site represents an autosite.
type Site struct {
	liveDomain string          // live domain
	title      string          // title of the site, for HTML <head>
	glob       string          // file glob for page templates
	templates  []string        // templates needed for all endpoints
	pages      map[string]page // URI -> page mapping
}

// page is a HTML resource.
type page struct {
	Title string      // title, for <head>
	Date  date        // publishing date
	URI   string      // URI path
	Data  interface{} // custom data, if any

	tmpl *template.Template // backing template
}

type year int

// date is a rough point in time.
type date struct {
	Year  year
	Month time.Month
}

// before says whether this date is before other date.
func (d date) before(other date) bool {
	if d.Year < other.Year {
		return true
	} else if d.Year == other.Year {
		return d.Month < other.Month
	}
	return false
}

// ServeHTTP serves the page.
func (p page) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	c.Infof("%+v will ServeHTTP for URI %s\n", p, r.RequestURI)

	if p.URI != r.RequestURI {
		c.Errorf("bad request URI %s, want %s; serving 404\n", r.RequestURI, p.URI)
		http.NotFound(w, r)
		return
	}

	err := p.tmpl.ExecuteTemplate(w, BaseTemplate, p)
	if err != nil {
		http.Error(w, "Internal server error.", http.StatusInternalServerError)
		log.Fatal(err.Error())
		return
	}
}

// String provides a string representation of the page.
func (p page) String() string {
	r := fmt.Sprintf("page [%s]", p.URI)
	if p.Date.Year != 0 {
		r += fmt.Sprintf(", published on %v", p.Date.Year)
		if p.Date.Month != 0 {
			r += fmt.Sprintf(", %v", p.Date.Month)
		}
	}
	return r
}

// read reads pages to serve on the autosite from disk
func (s *Site) read() error {
	filePaths, err := s.getFiles()
	if err != nil {
		return err
	}
	s.pages = make(map[string]page)
	for _, tmplPath := range filePaths {
		uri, d, err := parsePath(tmplPath)
		if err != nil {
			return err
		}
		s.addPage(uri, d, nil, append(s.templates, tmplPath))
	}
	return nil
}

// getFiles retrieves all pages' file paths from disk.
func (s Site) getFiles() ([]string, error) {
	paths, err := filepath.Glob(s.glob)
	if err != nil {
		return []string{}, err
	}
	if len(paths) == 0 {
		return []string{}, fmt.Errorf("no pages found")
	}

	// Skip files with dot prefixes (e.g. .#foo.tmpl).
	r := make([]string, len(paths))
	i := 0
	for _, p := range paths {
		if strings.Contains(p, ".#") {
			continue
		}
		r[i] = p
		i++
	}
	return r[0:i], nil
}

// parsePath extracts URI and date from a template file path.
func parsePath(p string) (uri string, d date, err error) {
	parts := strings.Split(p, "/")
	if len(parts) == 2 {
		// Assumes [dir]/*.tmpl; i.e. no date.
		uri = fmt.Sprintf("/%s", strings.TrimSuffix(parts[1], ".tmpl"))
	} else if len(parts) == 4 {
		// Assumes [dir]/[yyyy]/[mm]/*.tmpl; i.e. date is present.
		uri = "/" + strings.Join([]string{
			parts[1],
			parts[2],
			strings.TrimSuffix(parts[3], ".tmpl")}, "/")
		d, err = getDate(parts[1], parts[2])
		if err != nil {
			return
		}
	} else {
		err = fmt.Errorf("bad template path: %s", p)
		return
	}
	return uri, d, nil
}

// getDate extracts the date of the post from year and month strings.
func getDate(y, m string) (date, error) {
	y64, err := strconv.ParseInt(y, 10, 0)
	if err != nil || y64 <= 1900 || y64 >= 99999 {
		return date{}, fmt.Errorf("bad year: %v", y)
	}
	month, err := strconv.ParseInt(m, 10, 0)
	if err != nil || month < 1 || month > 12 {
		return date{}, fmt.Errorf("bad month: %v", m)
	}
	return date{
		Year:  year(y64),
		Month: time.Month(month),
	}, nil
}

// getFuncs constructs a map for the extra template functions.
func (s Site) getFuncs() template.FuncMap {
	isLive := func() bool {
		return !appengine.IsDevAppServer()
	}
	return template.FuncMap{
		"live": isLive,
		"domain": func() string {
			if isLive() {
				return s.liveDomain
			}
			return ""
		},
	}
}

// addPage adds a page to the autosite.
func (s *Site) addPage(uri string, d date, data interface{}, tmpls []string) {
	t := template.Must(template.New(BaseTemplate).Funcs(s.getFuncs()).ParseFiles(tmpls...))
	p := page{
		Title: s.title,
		URI:   uri,
		Data:  data,
		Date:  d,
		tmpl:  t,
	}
	s.pages[p.URI] = p
}
<|endoftext|>


<commit_before>package main

import (
	"math"

	"github.com/peterstace/grayt"
	"github.com/peterstace/grayt/geometry"
)

func main() {
	r := grayt.NewRunner()
	r.PxWide = 320
	r.PxHigh = 320
	r.Quality = 100
	r.Run(scene())
}

// Cornell Box
//
// (0,y,-1) +---------+ (1,y,-1)
//          | BB      |
//          | BB  BB  |
//          |     BB  |
// (0,y,0)  +         + (1,y,0)
//            \     /
//             \   /
//              \ /
//               C (0.5,0.5,D)
//
//
// SINE(0.5*T) = 0.5 / SQRT(0.5^2 + D^2)
// T = 2*ARCSINE(0.5/SQRT(0.25 + D^2))

var (
	up    = grayt.Vect(0.0, 1.0, 0.0)
	down  = grayt.Vect(0.0, -1.0, 0.0)
	left  = grayt.Vect(-1.0, 0.0, 0.0)
	right = grayt.Vect(1.0, 0.0, 0.0)
	back  = grayt.Vect(0.0, 0.0, 1.0)
	zero  = grayt.Vect(0.0, 0.0, 0.0)
	one   = grayt.Vect(1.0, 1.0, -1.0)
)

var (
	white = grayt.Material{Colour: grayt.White, Emittance: 0.0}
	green = grayt.Material{Colour: grayt.Green, Emittance: 0.0}
	red   = grayt.Material{Colour: grayt.Red, Emittance: 0.0}
	blue  = grayt.Material{Colour: grayt.Blue, Emittance: 0.0}
)

func scene() grayt.Scene {
	ee := []grayt.Entity{
		{
			Material: grayt.Material{Colour: grayt.White, Emittance: 5},
			Surface: geometry.AlignedBox(
				grayt.Vect(0.4, 1.0, -0.4),
				grayt.Vect(0.6, 0.999, -0.6),
			),
		},
	}
	ee = append(ee, box()...)
	for _, s := range tallBlock() {
		ee = append(ee, grayt.Entity{Material: white, Surface: s})
	}
	for _, s := range shortBlock() {
		ee = append(ee, grayt.Entity{Material: white, Surface: s})
	}
	return grayt.Scene{Camera: cam(), Entities: ee}
}

func cam() grayt.Camera {
	const D = 1.3 // Estimated.
	return grayt.NewRectilinearCamera(grayt.CameraConfig{
		Location:      grayt.Vect(0.5, 0.5, D),
		ViewDirection: grayt.Vect(0.0, 0.0, -1.0),
		UpDirection:   up,
		FieldOfView:   2 * math.Asin(0.5/math.Sqrt(0.25+D*D)),
		FocalLength:   0.5 + D,
		FocalRatio:    math.Inf(+1),
	})
}

func box() []grayt.Entity {
	return []grayt.Entity{
		{
			Material: white,
			Surface:  geometry.Plane(up, zero),
		},
		{
			Material: white,
			Surface:  geometry.Plane(down, one),
		},
		{
			Material: red,
			Surface:  geometry.Plane(right, zero),
		},
		{
			Material: green,
			Surface:  geometry.Plane(left, one),
		},
		{
			Material: white,
			Surface:  geometry.Plane(back, one),
		},
	}
}

func shortBlock() []grayt.Surface {
	var (
		// Left/Right, Top/Bottom, Front/Back.
		LBF = grayt.Vect(0.76, 0.00, -0.12)
		LBB = grayt.Vect(0.85, 0.00, -0.41)
		RBF = grayt.Vect(0.47, 0.00, -0.21)
		RBB = grayt.Vect(0.56, 0.00, -0.49)
		LTF = grayt.Vect(0.76, 0.30, -0.12)
		LTB = grayt.Vect(0.85, 0.30, -0.41)
		RTF = grayt.Vect(0.47, 0.30, -0.21)
		RTB = grayt.Vect(0.56, 0.30, -0.49)
	)
	var ss []grayt.Surface
	ss = append(ss, geometry.Square(LTF, LTB, RTB, RTF)...)
	ss = append(ss, geometry.Square(LBF, RBF, RTF, LTF)...)
	ss = append(ss, geometry.Square(LBB, RBB, RTB, LTB)...)
	ss = append(ss, geometry.Square(LBF, LBB, LTB, LTF)...)
	ss = append(ss, geometry.Square(RBF, RBB, RTB, RTF)...)
	return ss
}

func tallBlock() []grayt.Surface {
	var (
		// Left/Right, Top/Bottom, Front/Back.
		LBF = grayt.Vect(0.52, 0.00, -0.54)
		LBB = grayt.Vect(0.43, 0.00, -0.83)
		RBF = grayt.Vect(0.23, 0.00, -0.45)
		RBB = grayt.Vect(0.14, 0.00, -0.74)
		LTF = grayt.Vect(0.52, 0.60, -0.54)
		LTB = grayt.Vect(0.43, 0.60, -0.83)
		RTF = grayt.Vect(0.23, 0.60, -0.45)
		RTB = grayt.Vect(0.14, 0.60, -0.74)
	)
	var ss []grayt.Surface
	ss = append(ss, geometry.Square(LTF, LTB, RTB, RTF)...)
	ss = append(ss, geometry.Square(LBF, RBF, RTF, LTF)...)
	ss = append(ss, geometry.Square(LBB, RBB, RTB, LTB)...)
	ss = append(ss, geometry.Square(LBF, LBB, LTB, LTF)...)
	ss = append(ss, geometry.Square(RBF, RBB, RTB, RTF)...)
	return ss
}
<commit_msg>Add BaseName to CornellBox<commit_after>package main

import (
	"math"

	"github.com/peterstace/grayt"
	"github.com/peterstace/grayt/geometry"
)

func main() {
	r := grayt.NewRunner()
	r.PxWide = 320
	r.PxHigh = 320
	r.Quality = 100
	r.BaseName = "CornellBox"
	r.Run(scene())
}

// Cornell Box
//
// (0,y,-1) +---------+ (1,y,-1)
//          | BB      |
//          | BB  BB  |
//          |     BB  |
// (0,y,0)  +         + (1,y,0)
//            \     /
//             \   /
//              \ /
//               C (0.5,0.5,D)
//
//
// SINE(0.5*T) = 0.5 / SQRT(0.5^2 + D^2)
// T = 2*ARCSINE(0.5/SQRT(0.25 + D^2))

var (
	up    = grayt.Vect(0.0, 1.0, 0.0)
	down  = grayt.Vect(0.0, -1.0, 0.0)
	left  = grayt.Vect(-1.0, 0.0, 0.0)
	right = grayt.Vect(1.0, 0.0, 0.0)
	back  = grayt.Vect(0.0, 0.0, 1.0)
	zero  = grayt.Vect(0.0, 0.0, 0.0)
	one   = grayt.Vect(1.0, 1.0, -1.0)
)

var (
	white = grayt.Material{Colour: grayt.White, Emittance: 0.0}
	green = grayt.Material{Colour: grayt.Green, Emittance: 0.0}
	red   = grayt.Material{Colour: grayt.Red, Emittance: 0.0}
	blue  = grayt.Material{Colour: grayt.Blue, Emittance: 0.0}
)

func scene() grayt.Scene {
	ee := []grayt.Entity{
		{
			Material: grayt.Material{Colour: grayt.White, Emittance: 5},
			Surface: geometry.AlignedBox(
				grayt.Vect(0.4, 1.0, -0.4),
				grayt.Vect(0.6, 0.999, -0.6),
			),
		},
	}
	ee = append(ee, box()...)
	for _, s := range tallBlock() {
		ee = append(ee, grayt.Entity{Material: white, Surface: s})
	}
	for _, s := range shortBlock() {
		ee = append(ee, grayt.Entity{Material: white, Surface: s})
	}
	return grayt.Scene{Camera: cam(), Entities: ee}
}

func cam() grayt.Camera {
	const D = 1.3 // Estimated.
	return grayt.NewRectilinearCamera(grayt.CameraConfig{
		Location:      grayt.Vect(0.5, 0.5, D),
		ViewDirection: grayt.Vect(0.0, 0.0, -1.0),
		UpDirection:   up,
		FieldOfView:   2 * math.Asin(0.5/math.Sqrt(0.25+D*D)),
		FocalLength:   0.5 + D,
		FocalRatio:    math.Inf(+1),
	})
}

func box() []grayt.Entity {
	return []grayt.Entity{
		{
			Material: white,
			Surface:  geometry.Plane(up, zero),
		},
		{
			Material: white,
			Surface:  geometry.Plane(down, one),
		},
		{
			Material: red,
			Surface:  geometry.Plane(right, zero),
		},
		{
			Material: green,
			Surface:  geometry.Plane(left, one),
		},
		{
			Material: white,
			Surface:  geometry.Plane(back, one),
		},
	}
}

func shortBlock() []grayt.Surface {
	var (
		// Left/Right, Top/Bottom, Front/Back.
		LBF = grayt.Vect(0.76, 0.00, -0.12)
		LBB = grayt.Vect(0.85, 0.00, -0.41)
		RBF = grayt.Vect(0.47, 0.00, -0.21)
		RBB = grayt.Vect(0.56, 0.00, -0.49)
		LTF = grayt.Vect(0.76, 0.30, -0.12)
		LTB = grayt.Vect(0.85, 0.30, -0.41)
		RTF = grayt.Vect(0.47, 0.30, -0.21)
		RTB = grayt.Vect(0.56, 0.30, -0.49)
	)
	var ss []grayt.Surface
	ss = append(ss, geometry.Square(LTF, LTB, RTB, RTF)...)
	ss = append(ss, geometry.Square(LBF, RBF, RTF, LTF)...)
	ss = append(ss, geometry.Square(LBB, RBB, RTB, LTB)...)
	ss = append(ss, geometry.Square(LBF, LBB, LTB, LTF)...)
	ss = append(ss, geometry.Square(RBF, RBB, RTB, RTF)...)
	return ss
}

func tallBlock() []grayt.Surface {
	var (
		// Left/Right, Top/Bottom, Front/Back.
		LBF = grayt.Vect(0.52, 0.00, -0.54)
		LBB = grayt.Vect(0.43, 0.00, -0.83)
		RBF = grayt.Vect(0.23, 0.00, -0.45)
		RBB = grayt.Vect(0.14, 0.00, -0.74)
		LTF = grayt.Vect(0.52, 0.60, -0.54)
		LTB = grayt.Vect(0.43, 0.60, -0.83)
		RTF = grayt.Vect(0.23, 0.60, -0.45)
		RTB = grayt.Vect(0.14, 0.60, -0.74)
	)
	var ss []grayt.Surface
	ss = append(ss, geometry.Square(LTF, LTB, RTB, RTF)...)
	ss = append(ss, geometry.Square(LBF, RBF, RTF, LTF)...)
	ss = append(ss, geometry.Square(LBB, RBB, RTB, LTB)...)
	ss = append(ss, geometry.Square(LBF, LBB, LTB, LTF)...)
	ss = append(ss, geometry.Square(RBF, RBB, RTB, RTF)...)
	return ss
}
<|endoftext|>


<commit_before>package gomkafka

import (
	kafka "github.com/Shopify/sarama"
	"time"
)

/*
KafkaConfig represents the required configuration settings to create a
Kafka client to send requests to a topic in a Kafka cluster.
*/
type KafkaConfig struct {
	ClientID string
	Hosts    []string
	Topic    string
}

/*
Gomkafka will Initialize a kafka client and producer based off of KafkaConfig.
*/
func Gomkafka(config *KafkaConfig) (*kafka.Client, *kafka.Producer, error) {
	client, err := kafka.NewClient(config.ClientID, config.Hosts, &kafka.ClientConfig{MetadataRetries: 1, WaitForElection: 250 * time.Millisecond})
	if err != nil {
		return nil, nil, err
	}

	producer, err := kafka.NewProducer(client, &kafka.ProducerConfig{RequiredAcks: kafka.WaitForLocal, MaxBufferedBytes: 1, MaxBufferTime: 1})
	if err != nil {
		return nil, nil, err
	}

	return client, producer, nil
}
<commit_msg>Fix bug in Kafka initialization. Fix tests.<commit_after>package gomkafka

import (
	"github.com/Shopify/sarama"
)

/*
KafkaConfig represents the required configuration settings to create a
Kafka client to send requests to a topic in a Kafka cluster.
*/
type KafkaConfig struct {
	ClientID string
	Hosts    []string
	Topic    string
}

/*
Gomkafka will Initialize a kafka client and producer based off of KafkaConfig.
*/
func Gomkafka(config *KafkaConfig) (*sarama.Client, *sarama.Producer, error) {
	client, err := sarama.NewClient(config.ClientID, config.Hosts, sarama.NewClientConfig())
	if err != nil {
		return nil, nil, err
	}

	producer, err := sarama.NewProducer(client, nil)
	if err != nil {
		return nil, nil, err
	}

	return client, producer, nil
}
<|endoftext|>


<commit_before>// REST APIs
// TODO: Add STATS command.

package api

type RequestType int
const (
    CREATE RequestType = iota   // /create
    DROP                        // /drop
    LIST                        // /list
    SCAN                        // /scan
    STATS                       // /stats
)

// URL encoded query params
type QueryParams struct {
    Low       Key
    High      Key
    Inclusion Inclusion
    Offset    int
    Limit     int
}

// All API accept IndexRequest structure and returns IndexResponse structure.
// If application is written in Go, and compiled with `indexing` package then
// they can choose the access the underlying interfaces directly.
type IndexRequest struct {
    Type       RequestType
    Indexinfo  IndexInfo
    Params     QueryParams
}

//RESPONSE DATA FORMATS
type ResponseStatus int
const (
    SUCCESS ResponseStatus = iota
    ERROR
)

type IndexRow struct {
    Key   string
    Value string
}

type IndexError struct {
    Code string
    Msg  string
}

type IndexMetaResponse struct {
    Status  ResponseStatus
    Indexes []IndexInfo
    Errors  []IndexError
}

type IndexScanResponse struct {
    Status    ResponseStatus
    TotalRows int64
    Rows      []IndexRow
    Errors    []IndexError
}
<commit_msg>Contains HTTP REST definitions for indexing.<commit_after>// REST API to access indexing.

// TODO: Add STATS command.

package api

type RequestType int
const (
    CREATE RequestType = iota   // /create
    DROP                        // /drop
    LIST                        // /list
    SCAN                        // /scan
    STATS                       // /stats
    NODES                       // /nodes
    NOTIFY                      // /notify
)

// URL encoded query params
type QueryParams struct {
    Low       Key
    High      Key
    Inclusion Inclusion
    Offset    int
    Limit     int
}

// All API accept IndexRequest structure and returns IndexResponse structure.
// If application is written in Go, and compiled with `indexing` package then
// they can choose the access the underlying interfaces directly.
type IndexRequest struct {
    Type       RequestType
    Indexinfo  IndexInfo
    ServerUuid string
    Params     QueryParams
}

//RESPONSE DATA FORMATS
type ResponseStatus int
const (
    SUCCESS ResponseStatus = iota
    ERROR
    INVALID_CACHE
)

type IndexRow struct {
    Key   string
    Value string
}

type IndexError struct {
    Code string
    Msg  string
}

type IndexMetaResponse struct {
    Status  ResponseStatus
    Indexes []IndexInfo
    ServerUuid    string
    Nodes   []string
    Errors  []IndexError
}

type IndexScanResponse struct {
    Status    ResponseStatus
    TotalRows int64
    Rows      []IndexRow
    Errors    []IndexError
}
<|endoftext|>


<commit_before>package api

import (
	"github.com/zakuro9715/AOJRESTAPI/aoj"
	timeutil "github.com/zakuro9715/AOJRESTAPI/util/time"
	"time"
)

type User struct {
	Id              string
	Name            string
	Affliation      string
	RegisteredAt    time.Time
	LastSubmittedAt time.Time
}

func NewUser(au *aoj.User) *User {
	registerD := time.Duration(au.RegisterDate) * time.Millisecond
	submitD := time.Duration(au.LastSubmitDate) * time.Millisecond
	u := new(User)
	u.Id = au.Id
	u.Name = au.Name
	u.Affliation = au.Affliation
	u.RegisteredAt = timeutil.FromUnixTime(&registerD, timeutil.JST)
	u.LastSubmittedAt = timeutil.FromUnixTime(&submitD, timeutil.JST)
	return u
}

func FetchUser(userId string) (*User, error) {
	aojUser, err := aoj.UserSearchApi(userId)
	return NewUser(aojUser), err
}
<commit_msg>Add api.UserCore<commit_after>package api

import (
	"github.com/zakuro9715/AOJRESTAPI/aoj"
	timeutil "github.com/zakuro9715/AOJRESTAPI/util/time"
	"time"
)

type UserCore struct {
	Id              string
	Name            string
	Affliation      string
	RegisteredAt    time.Time
	LastSubmittedAt time.Time
}

type User struct {
	*UserCore
}

func NewUserCore(au *aoj.User) *UserCore {
	registerD := time.Duration(au.RegisterDate) * time.Millisecond
	submitD := time.Duration(au.LastSubmitDate) * time.Millisecond
	return &UserCore{
		Id:              au.Id,
		Name:            au.Name,
		Affliation:      au.Affliation,
		RegisteredAt:    timeutil.FromUnixTime(&registerD, timeutil.JST),
		LastSubmittedAt: timeutil.FromUnixTime(&submitD, timeutil.JST),
	}
}

func NewUser(au *aoj.User) *User {
	return &User{NewUserCore(au)}
}

func FetchUser(userId string) (*User, error) {
	aojUser, err := aoj.UserSearchApi(userId)
	return NewUser(aojUser), err
}
<|endoftext|>