<commit_before>package main

import (
	"context"
	"log"
	"sort"
	"strings"
	"sync"
	"waze/terraformer/gcp_terraforming"

	"golang.org/x/oauth2/google"

	"github.com/deckarep/golang-set"
	"google.golang.org/api/compute/v1"
)

//var GCPProjects = []string{"waze-development", "waze-prod"}
var GCPProjects = []string{"waze-ci", "waze-development", "waze-prod"}

const gcpProviderVersion = "~>2.0.0"

type gcpImporter struct {
	project string
	name    string
}

func (gcpImporter) getIgnoreService() mapset.Set {
	return mapset.NewSetWith(
		"disks",
		"iam",
		"autoscalers",
		"instanceGroupManagers",
		"instances",
		"instanceGroups",
		"regionAutoscalers",
		"regionDisks",
		"regionInstanceGroupManagers",
		"regionAutoscalers",
		"instanceTemplates",
		"images",
		"addresses",
		"regionBackendServices",
		"backendServices",
		"healthChecks", //google_compute_http_health_check is a legacy health check https://www.terraform.io/docs/providers/google/r/compute_http_health_check.html
	)
}
func (gcpImporter) getRegionServices() mapset.Set {
	return mapset.NewSetWith(
		"disks",
		"autoscalers",
		"instanceGroupManagers",
		"instances",
		"instanceGroups",
	)
}

func (gcpImporter) getNotInfraService() mapset.Set {
	return mapset.NewSetWith(
		"urlMaps",
		"targetHttpProxies",
		"targetHttpsProxies",
		"targetSslProxies",
		"targetTcpProxies",
		"globalForwardingRules",
		"forwardingRules",
		"httpHealthChecks",
		"httpsHealthChecks",
	)
}

func (g gcpImporter) getProviderData(arg ...string) map[string]interface{} {
	return map[string]interface{}{
		"provider": map[string]interface{}{
			g.name: map[string]interface{}{
				"project": g.project,
				"version": gcpProviderVersion,
			},
		},
	}
}

func (gcpImporter) getResourceConnections() map[string]map[string][]string {
	return map[string]map[string][]string{
		"firewalls":             {"networks": []string{"network", "self_link"}},
		"routes":                {"networks": []string{"network", "self_link"}},
		"regionBackendServices": {"healthChecks": []string{"health_checks", "self_link"}},
		"backendBuckets":        {"gcs": []string{"bucket_name", "name"}},
	}
}

func (g gcpImporter) getAccount() string {
	return g.project
}

func (g gcpImporter) getName() string {
	return g.name
}

func (g gcpImporter) getGcpZonesForService(service string) []*compute.Zone {
	zones := []*compute.Zone{{Name: "europe-west1-b", Region: "europe-west1"}} //dummy region
	if g.getRegionServices().Contains(service) {
		zones = g.getZone()
	}
	return zones
}

func importGCP() {
	wg := sync.WaitGroup{}
	for _, project := range GCPProjects {
		wg.Add(1)
		go func(pj string) {
			log.Println(pj, runOnService)
			if runOnProject == "" || pj == runOnService {
				importer := gcpImporter{
					name:    "google",
					project: pj,
				}
				log.Println("Rrr")
				importedResources := importProject(pj, importer)
				importedResources = connectServices(importedResources, importer.getResourceConnections())
				generateFilesAndUploadState(importedResources, importer)
			}
			wg.Done()
		}(project)
	}
	wg.Wait()
}

func importProject(project string, importer gcpImporter) map[string]importedService {
	importResources := map[string]importedService{}
	resources := []importedResource{}
	for _, service := range importer.getService() {
		if project == "waze-ci" && service == "monitoring" {
			continue
		}
		zones := importer.getGcpZonesForService(service)
		for _, zone := range zones {
			provider := &gcp_terraforming.GCPProvider{}
			for _, r := range importResource(provider, service, zone.Name, project) {
				if strings.Contains(r.ResourceName, filters) {
					continue
				}
				delete(r.Item, "project")
				resources = append(resources, importedResource{
					region:      zone.Name,
					tfResource:  r,
					serviceName: service,
				})
			}
		}
	}

	for _, service := range importer.getService() {
		if project == "waze-ci" && service == "monitoring" {
			continue
		}
		ir := importedService{}
		for _, r := range resources {
			if r.serviceName == service {
				if importer.getRegionServices().Contains(service) {
					regionPath := strings.Split(r.region, "/")
					ir.region = regionPath[len(regionPath)-1]
				} else {
					ir.region = "global"
					r.region = "global"
				}
				ir.tfResources = append(ir.tfResources, r)
			}
			if _, exist := r.tfResource.Item["labels"]; exist {
				r.tfResource.Item["labels"].(map[string]interface{})[terraformTagName] = "true"
			}
			r.tfResource.Item["lifecycle"] = map[string]interface{}{
				"prevent_destroy": true,
			}
		}
		importResources[service] = ir
	}
	return importResources
	/*for _, microserviceName := range microserviceNameList {
		for cloudServiceName, value := range importResources {
			if notInfraServiceGcp.Contains(cloudServiceName) {
				continue
			}
			for _, obj := range value.tfResources {
				resourceName := strings.Replace(obj.tfResource.ResourceName, "_", "-", -1)
				ObjNamePrefix := strings.Split(resourceName, "-")[0]
				if ObjNamePrefix == microserviceName {
					log.Println(microserviceName, cloudServiceName)
				}
			}
		}
	}*/

}
func (g gcpImporter) getZone() []*compute.Zone {
	ctx := context.Background()
	c, err := google.DefaultClient(ctx, compute.CloudPlatformScope)
	if err != nil {
		log.Fatal(err)
	}
	computeService, err := compute.New(c)
	if err != nil {
		log.Fatal(err)
	}
	zones := []*compute.Zone{}
	for _, project := range GCPProjects {
		req := computeService.Zones.List(project)
		if err := req.Pages(ctx, func(page *compute.ZoneList) error {
			for _, zone := range page.Items {
				//if strings.Contains(zone.Region, "europe-west1") { // TODO for debug
				zones = append(zones, zone)
				//}
			}
			return nil
		}); err != nil {
			log.Fatal(err)
		}
	}
	return zones
}

func (g gcpImporter) getService() []string {
	services := []string{}
	provider := &gcp_terraforming.GCPProvider{}
	for service := range provider.GetGCPSupportService() {
		if !g.getIgnoreService().Contains(service) {
			if runOnService == "" || service == runOnService {
				services = append(services, service)
			}
		}
	}
	sort.Strings(services)
	return services
}
<commit_msg>google: parallel run on projects<commit_after>package main

import (
	"context"
	"log"
	"sort"
	"strings"
	"sync"
	"waze/terraformer/gcp_terraforming"

	"golang.org/x/oauth2/google"

	"github.com/deckarep/golang-set"
	"google.golang.org/api/compute/v1"
)

//var GCPProjects = []string{"waze-development", "waze-prod"}
var GCPProjects = []string{"waze-ci", "waze-development", "waze-prod"}

const gcpProviderVersion = "~>2.0.0"

type gcpImporter struct {
	project string
	name    string
}

func (gcpImporter) getIgnoreService() mapset.Set {
	return mapset.NewSetWith(
		"disks",
		"iam",
		"autoscalers",
		"instanceGroupManagers",
		"instances",
		"instanceGroups",
		"regionAutoscalers",
		"regionDisks",
		"regionInstanceGroupManagers",
		"regionAutoscalers",
		"instanceTemplates",
		"images",
		"addresses",
		"regionBackendServices",
		"backendServices",
		"healthChecks", //google_compute_http_health_check is a legacy health check https://www.terraform.io/docs/providers/google/r/compute_http_health_check.html
	)
}
func (gcpImporter) getRegionServices() mapset.Set {
	return mapset.NewSetWith(
		"disks",
		"autoscalers",
		"instanceGroupManagers",
		"instances",
		"instanceGroups",
	)
}

func (gcpImporter) getNotInfraService() mapset.Set {
	return mapset.NewSetWith(
		"urlMaps",
		"targetHttpProxies",
		"targetHttpsProxies",
		"targetSslProxies",
		"targetTcpProxies",
		"globalForwardingRules",
		"forwardingRules",
		"httpHealthChecks",
		"httpsHealthChecks",
	)
}

func (g gcpImporter) getProviderData(arg ...string) map[string]interface{} {
	return map[string]interface{}{
		"provider": map[string]interface{}{
			g.name: map[string]interface{}{
				"project": g.project,
				"version": gcpProviderVersion,
			},
		},
	}
}

func (gcpImporter) getResourceConnections() map[string]map[string][]string {
	return map[string]map[string][]string{
		"firewalls":             {"networks": []string{"network", "self_link"}},
		"routes":                {"networks": []string{"network", "self_link"}},
		"regionBackendServices": {"healthChecks": []string{"health_checks", "self_link"}},
		"backendBuckets":        {"gcs": []string{"bucket_name", "name"}},
	}
}

func (g gcpImporter) getAccount() string {
	return g.project
}

func (g gcpImporter) getName() string {
	return g.name
}

func (g gcpImporter) getGcpZonesForService(service string) []*compute.Zone {
	zones := []*compute.Zone{{Name: "europe-west1-b", Region: "europe-west1"}} //dummy region
	if g.getRegionServices().Contains(service) {
		zones = g.getZone()
	}
	return zones
}

func importGCP() {
	wg := sync.WaitGroup{}
	for _, project := range GCPProjects {
		wg.Add(1)
		go func(pj string) {
			if runOnProject == "" || pj == runOnService {
				importer := gcpImporter{
					name:    "google",
					project: pj,
				}
				importedResources := importProject(pj, importer)
				importedResources = connectServices(importedResources, importer.getResourceConnections())
				generateFilesAndUploadState(importedResources, importer)
			}
			wg.Done()
		}(project)
	}
	wg.Wait()
}

func importProject(project string, importer gcpImporter) map[string]importedService {
	importResources := map[string]importedService{}
	resources := []importedResource{}
	for _, service := range importer.getService() {
		if project == "waze-ci" && service == "monitoring" {
			continue
		}
		zones := importer.getGcpZonesForService(service)
		for _, zone := range zones {
			provider := &gcp_terraforming.GCPProvider{}
			for _, r := range importResource(provider, service, zone.Name, project) {
				if strings.Contains(r.ResourceName, filters) {
					continue
				}
				delete(r.Item, "project")
				resources = append(resources, importedResource{
					region:      zone.Name,
					tfResource:  r,
					serviceName: service,
				})
			}
		}
	}

	for _, service := range importer.getService() {
		if project == "waze-ci" && service == "monitoring" {
			continue
		}
		ir := importedService{}
		for _, r := range resources {
			if r.serviceName == service {
				if importer.getRegionServices().Contains(service) {
					regionPath := strings.Split(r.region, "/")
					ir.region = regionPath[len(regionPath)-1]
				} else {
					ir.region = "global"
					r.region = "global"
				}
				ir.tfResources = append(ir.tfResources, r)
			}
			if _, exist := r.tfResource.Item["labels"]; exist {
				r.tfResource.Item["labels"].(map[string]interface{})[terraformTagName] = "true"
			}
			r.tfResource.Item["lifecycle"] = map[string]interface{}{
				"prevent_destroy": true,
			}
		}
		importResources[service] = ir
	}
	return importResources
	/*for _, microserviceName := range microserviceNameList {
		for cloudServiceName, value := range importResources {
			if notInfraServiceGcp.Contains(cloudServiceName) {
				continue
			}
			for _, obj := range value.tfResources {
				resourceName := strings.Replace(obj.tfResource.ResourceName, "_", "-", -1)
				ObjNamePrefix := strings.Split(resourceName, "-")[0]
				if ObjNamePrefix == microserviceName {
					log.Println(microserviceName, cloudServiceName)
				}
			}
		}
	}*/

}
func (g gcpImporter) getZone() []*compute.Zone {
	ctx := context.Background()
	c, err := google.DefaultClient(ctx, compute.CloudPlatformScope)
	if err != nil {
		log.Fatal(err)
	}
	computeService, err := compute.New(c)
	if err != nil {
		log.Fatal(err)
	}
	zones := []*compute.Zone{}
	for _, project := range GCPProjects {
		req := computeService.Zones.List(project)
		if err := req.Pages(ctx, func(page *compute.ZoneList) error {
			for _, zone := range page.Items {
				//if strings.Contains(zone.Region, "europe-west1") { // TODO for debug
				zones = append(zones, zone)
				//}
			}
			return nil
		}); err != nil {
			log.Fatal(err)
		}
	}
	return zones
}

func (g gcpImporter) getService() []string {
	services := []string{}
	provider := &gcp_terraforming.GCPProvider{}
	for service := range provider.GetGCPSupportService() {
		if !g.getIgnoreService().Contains(service) {
			if runOnService == "" || service == runOnService {
				services = append(services, service)
			}
		}
	}
	sort.Strings(services)
	return services
}
<|endoftext|>


<commit_before>// This app is intented to be go-port of the defunckt's gist library in Ruby
// Currently, uploading single and multiple files are available.
// You can also create secret gists, and both anonymous and user gists.
//
// Author: Viyat Bhalodia
package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
)

//Defines the app version
const VERSION = "v0.1.0"

//#TODO: A list of clipboard commands with copy and paste support.
//This is intended for adding the gist URLs directly to the user clipboard,
//so that manual copying is not needed.
const (
	xclip   = "xclip -o"
	xsel    = "xsel -o"
	pbcopy  = "pbpaste"
	putclip = "getclip"
)

// Defines different constants used
// GIT_IO_URL is the Github's URL shortner
// API v3 is the current version of GitHub API
const (
	GITHUB_API_URL = "https://api.github.com/"
	GIT_IO_URL     = "http://git.io"
	GHE_BASE_PATH  = "/api/v3"
)

//User agent defines a custom agent (required by GitHub)
//`token` stores the GITHUB_TOKEN from the env variables
var (
	USER_AGENT = "gist/#" + VERSION //Github requires this, else rejects API request
	token      = os.Getenv("GITHUB_TOKEN")
)

// Variables used in `Gist` struct
var (
	publicFlag  bool
	description string
	anonymous   bool
	responseObj map[string]interface{}
)

//The top-level struct for a gist file
type GistFile struct {
	Content string `json:"content"`
}

//The required structure for POST data for API purposes
type Gist struct {
	Description string              `json:"description"`
	publicFile  bool                `json:"public"`
	GistFile    map[string]GistFile `json:"files"`
}

//This function loads the GITHUB_TOKEN from a '$HOME/.gist' file
//from the user's home directory.
func loadTokenFromFile() (token string) {
	//get the tokenfile
	file := os.Getenv("$HOME") + "/.gist"
	github_token, err := ioutil.ReadFile(file)
	if err != nil {
		log.Fatal(err)
	}
	return string(github_token)
}

//Defines basic usage when program is run with the help flag
func usage() {
	fmt.Fprintf(os.Stderr, "usage: gist [-p] [-d] [-u] example.go\n")
	flag.PrintDefaults()
	os.Exit(2)
}

//The main function parses the CLI args. It also checks the files, and
//loads them into an array.
//Then the files are separated into GistFile structs and collectively
//the files are saved in `files` field in the Gist struct.
//A request is then made to the GitHub api - it depends on whether it is
//anonymous gist or not.
//The response recieved is parsed and the Gist URL is printed to STDOUT.
func main() {
	flag.BoolVar(&publicFlag, "p", true, "Set to false for private gist.")
	flag.BoolVar(&anonymous, "u", true, "Set false if gist should be not anonymous")
	flag.StringVar(&description, "d", "This is a gist", "Description for gist.")
	flag.Usage = usage
	flag.Parse()

	files_list := flag.Args()
	if len(files_list) == 0 {
		log.Fatal("Error: No files specified.")
	}

	//fmt.Println(files_list)
	//fmt.Println(token)

	files := map[string]GistFile{}

	for _, filename := range files_list {
		fmt.Println("Checking file:", filename)
		content, err := ioutil.ReadFile(filename)
		if err != nil {
			log.Fatal("File Error: ", err)
		}
		files[filename] = GistFile{string(content)}
	}

	if description == "" {
		description = strings.Join(files_list, ", ")
	}

	//create a gist from the files array
	gist := Gist{
		description,
		publicFlag,
		files,
	}

	pfile, err := json.Marshal(gist)
	if err != nil {
		log.Fatal("Cannot marshal json: ", err)
	}

	//Check if JSON marshalling succeeds
	fmt.Println("OK")

	b := bytes.NewBuffer(pfile)
	fmt.Println("Uploading...")

	//Separate uploading methods depending on whether the gist is anonymous or not
	if anonymous == true {
		response, err := http.Post("https://api.github.com/gists", "application/json", b)
		if err != nil {
			log.Fatal("HTTP error: ", err)
		}

		err = json.NewDecoder(response.Body).Decode(&responseObj)
		if err != nil {
			log.Fatal("Response JSON error: ", err)
		}

		fmt.Println("--- Gist URL ---")
		fmt.Println(responseObj["html_url"])
	} else {
		req, err := http.NewRequest("POST", "https://api.github.com/gists", b)
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Accept", "application/json")
		req.SetBasicAuth(token, "x-oauth-basic")

		client := http.Client{}
		response, err := client.Do(req)
		if err != nil {
			log.Fatal("HTTP error: ", err)
		}
		err = json.NewDecoder(response.Body).Decode(&responseObj)
		if err != nil {
			log.Fatal("Response JSON error: ", err)
		}

		fmt.Println("--- Gist URL ---")
		fmt.Println(responseObj["html_url"])
	}
}
<commit_msg>Added it as a package<commit_after>// This app is intented to be go-port of the defunckt's gist library in Ruby
// Currently, uploading single and multiple files are available.
// You can also create secret gists, and both anonymous and user gists.
//
// Author: Viyat Bhalodia
package gister

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
)

//Defines the app version
const VERSION = "v0.1.0"

//#TODO: A list of clipboard commands with copy and paste support.
//This is intended for adding the gist URLs directly to the user clipboard,
//so that manual copying is not needed.
const (
	xclip   = "xclip -o"
	xsel    = "xsel -o"
	pbcopy  = "pbpaste"
	putclip = "getclip"
)

// Defines different constants used
// GIT_IO_URL is the Github's URL shortner
// API v3 is the current version of GitHub API
const (
	GITHUB_API_URL = "https://api.github.com/"
	GIT_IO_URL     = "http://git.io"
	GHE_BASE_PATH  = "/api/v3"
)

//User agent defines a custom agent (required by GitHub)
//`token` stores the GITHUB_TOKEN from the env variables
var (
	USER_AGENT = "gist/#" + VERSION //Github requires this, else rejects API request
	token      = os.Getenv("GITHUB_TOKEN")
)

// Variables used in `Gist` struct
var (
	publicFlag  bool
	description string
	anonymous   bool
	responseObj map[string]interface{}
)

//The top-level struct for a gist file
type GistFile struct {
	Content string `json:"content"`
}

//The required structure for POST data for API purposes
type Gist struct {
	Description string              `json:"description"`
	publicFile  bool                `json:"public"`
	GistFile    map[string]GistFile `json:"files"`
}

//This function loads the GITHUB_TOKEN from a '$HOME/.gist' file
//from the user's home directory.
func loadTokenFromFile() (token string) {
	//get the tokenfile
	file := os.Getenv("$HOME") + "/.gist"
	github_token, err := ioutil.ReadFile(file)
	if err != nil {
		log.Fatal(err)
	}
	return string(github_token)
}

//Defines basic usage when program is run with the help flag
func usage() {
	fmt.Fprintf(os.Stderr, "usage: gist [-p] [-d] [-u] example.go\n")
	flag.PrintDefaults()
	os.Exit(2)
}

//The main function parses the CLI args. It also checks the files, and
//loads them into an array.
//Then the files are separated into GistFile structs and collectively
//the files are saved in `files` field in the Gist struct.
//A request is then made to the GitHub api - it depends on whether it is
//anonymous gist or not.
//The response recieved is parsed and the Gist URL is printed to STDOUT.
func main() {
	flag.BoolVar(&publicFlag, "p", true, "Set to false for private gist.")
	flag.BoolVar(&anonymous, "u", true, "Set false if gist should be not anonymous")
	flag.StringVar(&description, "d", "This is a gist", "Description for gist.")
	flag.Usage = usage
	flag.Parse()

	files_list := flag.Args()
	if len(files_list) == 0 {
		log.Fatal("Error: No files specified.")
	}

	//fmt.Println(files_list)
	//fmt.Println(token)

	files := map[string]GistFile{}

	for _, filename := range files_list {
		fmt.Println("Checking file:", filename)
		content, err := ioutil.ReadFile(filename)
		if err != nil {
			log.Fatal("File Error: ", err)
		}
		files[filename] = GistFile{string(content)}
	}

	if description == "" {
		description = strings.Join(files_list, ", ")
	}

	//create a gist from the files array
	gist := Gist{
		description,
		publicFlag,
		files,
	}

	pfile, err := json.Marshal(gist)
	if err != nil {
		log.Fatal("Cannot marshal json: ", err)
	}

	//Check if JSON marshalling succeeds
	fmt.Println("OK")

	b := bytes.NewBuffer(pfile)
	fmt.Println("Uploading...")

	//Separate uploading methods depending on whether the gist is anonymous or not
	if anonymous == true {
		response, err := http.Post("https://api.github.com/gists", "application/json", b)
		if err != nil {
			log.Fatal("HTTP error: ", err)
		}

		err = json.NewDecoder(response.Body).Decode(&responseObj)
		if err != nil {
			log.Fatal("Response JSON error: ", err)
		}

		fmt.Println("--- Gist URL ---")
		fmt.Println(responseObj["html_url"])
	} else {
		req, err := http.NewRequest("POST", "https://api.github.com/gists", b)
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Accept", "application/json")
		req.SetBasicAuth(token, "x-oauth-basic")

		client := http.Client{}
		response, err := client.Do(req)
		if err != nil {
			log.Fatal("HTTP error: ", err)
		}
		err = json.NewDecoder(response.Body).Decode(&responseObj)
		if err != nil {
			log.Fatal("Response JSON error: ", err)
		}

		fmt.Println("--- Gist URL ---")
		fmt.Println(responseObj["html_url"])
	}
}
<|endoftext|>


<commit_before>package observers

import (
	"github.com/sirupsen/logrus"

	"github.com/ivan1993spb/snake-server/engine"
	"github.com/ivan1993spb/snake-server/objects/wall"
	"github.com/ivan1993spb/snake-server/world"
)

const ruinsFactor = 0.20

var dotsMaskOne = engine.NewDotsMask([][]uint8{{1}})

var ruins = []*engine.DotsMask{
	dotsMaskOne,
	engine.DotsMaskSquare2x2,
	engine.DotsMaskTank,
	engine.DotsMaskHome1,
	engine.DotsMaskHome2,
	engine.DotsMaskCross,
	engine.DotsMaskDiagonal,
	engine.DotsMaskCrossSmall,
	engine.DotsMaskDiagonalSmall,
	engine.DotsMaskLabyrinth,
	engine.DotsMaskTunnel1,
	engine.DotsMaskTunnel2,
	engine.DotsMaskBigHome,
}

func calcRuinsCount(size uint16) uint16 {
	return uint16(float32(size) * ruinsFactor)
}

type WallObserver struct{}

func (WallObserver) Observe(stop <-chan struct{}, w *world.World, logger logrus.FieldLogger) {
	go func() {
		area, err := engine.NewArea(w.Width(), w.Height())
		if err != nil {
			logger.WithError(err).Error("cannot create area in wall observer")
			return
		}

		size := area.Size()
		ruinsCount := calcRuinsCount(size)
		var counter uint16

		for counter < ruinsCount {
			for i := 0; i < len(ruins); i++ {
				mask := ruins[i].TurnRandom()

				if area.Width() >= mask.Width() && area.Height() >= mask.Height() {
					rect, err := area.NewRandomRect(mask.Width(), mask.Height(), 0, 0)
					if err != nil {
						continue
					}

					location := mask.Location(rect.X(), rect.Y())
					if location.DotCount() > ruinsCount-counter {
						location = location[:ruinsCount-counter]
					}

					if w.LocationOccupied(location) {
						continue
					}

					if _, err := wall.NewWallLocation(w, location); err != nil {
						logger.WithError(err).Error("error on wall creation")
					} else {
						counter += location.DotCount()
					}
				}
			}
		}
	}()
}
<commit_msg>Decrease wall count generation factor<commit_after>package observers

import (
	"github.com/sirupsen/logrus"

	"github.com/ivan1993spb/snake-server/engine"
	"github.com/ivan1993spb/snake-server/objects/wall"
	"github.com/ivan1993spb/snake-server/world"
)

const ruinsFactor = 0.15

var dotsMaskOne = engine.NewDotsMask([][]uint8{{1}})

var ruins = []*engine.DotsMask{
	dotsMaskOne,
	engine.DotsMaskSquare2x2,
	engine.DotsMaskTank,
	engine.DotsMaskHome1,
	engine.DotsMaskHome2,
	engine.DotsMaskCross,
	engine.DotsMaskDiagonal,
	engine.DotsMaskCrossSmall,
	engine.DotsMaskDiagonalSmall,
	engine.DotsMaskLabyrinth,
	engine.DotsMaskTunnel1,
	engine.DotsMaskTunnel2,
	engine.DotsMaskBigHome,
}

func calcRuinsCount(size uint16) uint16 {
	return uint16(float32(size) * ruinsFactor)
}

type WallObserver struct{}

func (WallObserver) Observe(stop <-chan struct{}, w *world.World, logger logrus.FieldLogger) {
	go func() {
		area, err := engine.NewArea(w.Width(), w.Height())
		if err != nil {
			logger.WithError(err).Error("cannot create area in wall observer")
			return
		}

		size := area.Size()
		ruinsCount := calcRuinsCount(size)
		var counter uint16

		for counter < ruinsCount {
			for i := 0; i < len(ruins); i++ {
				mask := ruins[i].TurnRandom()

				if area.Width() >= mask.Width() && area.Height() >= mask.Height() {
					rect, err := area.NewRandomRect(mask.Width(), mask.Height(), 0, 0)
					if err != nil {
						continue
					}

					location := mask.Location(rect.X(), rect.Y())
					if location.DotCount() > ruinsCount-counter {
						location = location[:ruinsCount-counter]
					}

					if w.LocationOccupied(location) {
						continue
					}

					if _, err := wall.NewWallLocation(w, location); err != nil {
						logger.WithError(err).Error("error on wall creation")
					} else {
						counter += location.DotCount()
					}
				}
			}
		}
	}()
}
<|endoftext|>


<commit_before>package jira

import (
	// Stdlib
	"bytes"
	"fmt"
	"io"
	"text/tabwriter"

	// Internal
	"github.com/salsaflow/salsaflow/action"
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/log"
	"github.com/salsaflow/salsaflow/modules/common"
	"github.com/salsaflow/salsaflow/version"

	// Vendor
	"github.com/salsita/go-jira/v2/jira"
)

type runningRelease struct {
	tracker        *issueTracker
	releaseVersion *version.Version
	issues         []*jira.Issue
}

func newRunningRelease(
	tracker *issueTracker,
	releaseVersion *version.Version,
) (*runningRelease, error) {

	// Fetch relevant issues from JIRA.
	verString := releaseVersion.BaseString()
	task := fmt.Sprintf("Fetch JIRA issues for release %v", verString)
	log.Run(task)
	issues, err := tracker.issuesByRelease(releaseVersion)
	if err != nil {
		return nil, errs.NewError(task, err)
	}

	// Return a new release instance.
	return &runningRelease{tracker, releaseVersion, issues}, nil
}

func (release *runningRelease) Version() *version.Version {
	return release.releaseVersion
}

func (release *runningRelease) Stories() ([]common.Story, error) {
	return toCommonStories(release.issues, release.tracker), nil
}

func (release *runningRelease) EnsureStageable() error {
	versionString := release.releaseVersion.BaseString()

	var task = fmt.Sprintf(
		"Make sure that release %v can be staged", versionString)
	log.Run(task)

	var details bytes.Buffer
	tw := tabwriter.NewWriter(&details, 0, 8, 4, '\t', 0)
	io.WriteString(tw, "\n")
	io.WriteString(tw, "Issue Key\tError\n")
	io.WriteString(tw, "=========\t=====\n")

	var err error
	for _, issue := range release.issues {
		if ex := ensureStageableIssue(issue); ex != nil {
			fmt.Fprintf(tw, "%v\t%v\n", issue.Key, ex)
			err = common.ErrNotStageable
		}
	}

	if err != nil {
		io.WriteString(tw, "\n")
		tw.Flush()
		return errs.NewErrorWithHint(task, err, details.String())
	}
	return nil
}

func (release *runningRelease) Stage() (action.Action, error) {

	var (
		api           = newClient(release.tracker.config)
		versionString = release.releaseVersion.BaseString()
		stageTask     = fmt.Sprintf("Stage JIRA issues associated with release %v", versionString)
	)
	log.Run(stageTask)

	// Make sure we only try to stage the issues that are in Tested.
	var issuesToStage []*jira.Issue
	for _, issue := range release.issues {
		if issue.Fields.Status.Id == stateIdTested {
			issuesToStage = append(issuesToStage, issue)
		}
	}

	// Perform the transition.
	err := performBulkTransition(api, issuesToStage, transitionIdStage, transitionIdUnstage)
	if err != nil {
		return nil, errs.NewError(stageTask, err)
	}

	return action.ActionFunc(func() error {
		log.Rollback(stageTask)
		unstageTask := fmt.Sprintf("Unstage JIRA issues associated with release %v", versionString)
		if err := performBulkTransition(api, issuesToStage, transitionIdUnstage, ""); err != nil {
			return errs.NewError(unstageTask, err)
		}
		return nil
	}), nil
}

func (release *runningRelease) EnsureReleasable() error {
	// Drop accepted issues.
	var notAccepted []*jira.Issue
IssueLoop:
	for _, issue := range release.issues {
		for _, id := range acceptedStateIds {
			if id == issue.Fields.Status.Id {
				continue IssueLoop
			}
		}
		notAccepted = append(notAccepted, issue)
	}

	// In case there is no open story, we are done.
	if len(notAccepted) == 0 {
		return nil
	}

	// Generate the error hint.
	var hint bytes.Buffer
	tw := tabwriter.NewWriter(&hint, 0, 8, 2, '\t', 0)
	fmt.Fprintf(tw, "\nThe following issues cannot be released:\n\n")
	fmt.Fprintf(tw, "Issue Key\tStatus\n")
	fmt.Fprintf(tw, "=========\t======\n")
	for _, issue := range notAccepted {
		fmt.Fprintf(tw, "%v\t%v\n", issue.Key, issue.Fields.Status.Name)
	}
	fmt.Fprintf(tw, "\n")
	tw.Flush()

	versionString := release.releaseVersion.BaseString()
	return errs.NewErrorWithHint(
		fmt.Sprintf("Make sure release %v can be released", versionString),
		common.ErrNotReleasable,
		hint.String())
}

func (release *runningRelease) Release() error {
	// Release all issues that are accepted.
	issues := make([]*jira.Issue, 0, len(release.issues))
	for _, issue := range release.issues {
		if issue.Fields.Status.Id == stateIdAccepted {
			issues = append(issues, issue)
		}
	}
	if len(issues) == 0 {
		log.Warn("No accepted stories found in JIRA")
		return nil
	}

	return performBulkTransition(
		newClient(release.tracker.config), issues, transitionIdRelease, "")
}

func ensureStageableIssue(issue *jira.Issue) error {
	// Check subtasks recursively.
	for _, subtask := range issue.Fields.Subtasks {
		if err := ensureStageableIssue(subtask); err != nil {
			return err
		}
	}

	// Check the issue itself.
	for _, id := range stageableStateIds {
		if issue.Fields.Status.Id == id {
			return nil
		}
	}
	return fmt.Errorf("issue %v: invalid state: %v", issue.Key, issue.Fields.Status.Name)
}
<commit_msg>jira: Fix issue stageable check<commit_after>package jira

import (
	// Stdlib
	"bytes"
	"fmt"
	"io"
	"text/tabwriter"

	// Internal
	"github.com/salsaflow/salsaflow/action"
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/log"
	"github.com/salsaflow/salsaflow/modules/common"
	"github.com/salsaflow/salsaflow/version"

	// Vendor
	"github.com/salsita/go-jira/v2/jira"
)

type runningRelease struct {
	tracker        *issueTracker
	releaseVersion *version.Version
	issues         []*jira.Issue
}

func newRunningRelease(
	tracker *issueTracker,
	releaseVersion *version.Version,
) (*runningRelease, error) {

	// Fetch relevant issues from JIRA.
	verString := releaseVersion.BaseString()
	task := fmt.Sprintf("Fetch JIRA issues for release %v", verString)
	log.Run(task)
	issues, err := tracker.issuesByRelease(releaseVersion)
	if err != nil {
		return nil, errs.NewError(task, err)
	}

	// Return a new release instance.
	return &runningRelease{tracker, releaseVersion, issues}, nil
}

func (release *runningRelease) Version() *version.Version {
	return release.releaseVersion
}

func (release *runningRelease) Stories() ([]common.Story, error) {
	return toCommonStories(release.issues, release.tracker), nil
}

func (release *runningRelease) EnsureStageable() error {
	versionString := release.releaseVersion.BaseString()

	var task = fmt.Sprintf(
		"Make sure that release %v can be staged", versionString)
	log.Run(task)

	var details bytes.Buffer
	tw := tabwriter.NewWriter(&details, 0, 8, 4, '\t', 0)
	io.WriteString(tw, "\n")
	io.WriteString(tw, "Issue Key\tError\n")
	io.WriteString(tw, "=========\t=====\n")

	var err error
	for _, issue := range release.issues {
		if ex := ensureStageableIssue(issue); ex != nil {
			fmt.Fprintf(tw, "%v\t%v\n", issue.Key, ex)
			err = common.ErrNotStageable
		}
	}

	if err != nil {
		io.WriteString(tw, "\n")
		tw.Flush()
		return errs.NewErrorWithHint(task, err, details.String())
	}
	return nil
}

func (release *runningRelease) Stage() (action.Action, error) {

	var (
		api           = newClient(release.tracker.config)
		versionString = release.releaseVersion.BaseString()
		stageTask     = fmt.Sprintf("Stage JIRA issues associated with release %v", versionString)
	)
	log.Run(stageTask)

	// Make sure we only try to stage the issues that are in Tested.
	var issuesToStage []*jira.Issue
	for _, issue := range release.issues {
		if issue.Fields.Status.Id == stateIdTested {
			issuesToStage = append(issuesToStage, issue)
		}
	}

	// Perform the transition.
	err := performBulkTransition(api, issuesToStage, transitionIdStage, transitionIdUnstage)
	if err != nil {
		return nil, errs.NewError(stageTask, err)
	}

	return action.ActionFunc(func() error {
		log.Rollback(stageTask)
		unstageTask := fmt.Sprintf("Unstage JIRA issues associated with release %v", versionString)
		if err := performBulkTransition(api, issuesToStage, transitionIdUnstage, ""); err != nil {
			return errs.NewError(unstageTask, err)
		}
		return nil
	}), nil
}

func (release *runningRelease) EnsureReleasable() error {
	// Drop accepted issues.
	var notAccepted []*jira.Issue
IssueLoop:
	for _, issue := range release.issues {
		for _, id := range acceptedStateIds {
			if id == issue.Fields.Status.Id {
				continue IssueLoop
			}
		}
		notAccepted = append(notAccepted, issue)
	}

	// In case there is no open story, we are done.
	if len(notAccepted) == 0 {
		return nil
	}

	// Generate the error hint.
	var hint bytes.Buffer
	tw := tabwriter.NewWriter(&hint, 0, 8, 2, '\t', 0)
	fmt.Fprintf(tw, "\nThe following issues cannot be released:\n\n")
	fmt.Fprintf(tw, "Issue Key\tStatus\n")
	fmt.Fprintf(tw, "=========\t======\n")
	for _, issue := range notAccepted {
		fmt.Fprintf(tw, "%v\t%v\n", issue.Key, issue.Fields.Status.Name)
	}
	fmt.Fprintf(tw, "\n")
	tw.Flush()

	versionString := release.releaseVersion.BaseString()
	return errs.NewErrorWithHint(
		fmt.Sprintf("Make sure release %v can be released", versionString),
		common.ErrNotReleasable,
		hint.String())
}

func (release *runningRelease) Release() error {
	// Release all issues that are accepted.
	issues := make([]*jira.Issue, 0, len(release.issues))
	for _, issue := range release.issues {
		if issue.Fields.Status.Id == stateIdAccepted {
			issues = append(issues, issue)
		}
	}
	if len(issues) == 0 {
		log.Warn("No accepted stories found in JIRA")
		return nil
	}

	return performBulkTransition(
		newClient(release.tracker.config), issues, transitionIdRelease, "")
}

func ensureStageableIssue(issue *jira.Issue) error {
	// Make sure the issue is in one of the stageable stages.
	for _, id := range stageableStateIds {
		if issue.Fields.Status.Id == id {
			return nil
		}
	}
	return fmt.Errorf("issue %v: invalid state: %v", issue.Key, issue.Fields.Status.Name)
}
<|endoftext|>


<commit_before>// Package slowdown provides an implementation of net.Listener that limits
// bandwidth.
package slowdown

import (
	"io"
	"net"
	"time"

	"github.com/juju/ratelimit"
)

// The maximum rate you should specify for readrate or writerate.If this is too
// high, the token bucket implementation seems to break down.
var MaxRate uint = (1024 * 1024) * 1000

var blockSize = int64(1024)
var capacity = int64(blockSize * 4)

type slowReader struct {
	reader io.Reader
	bucket *ratelimit.Bucket
}

func (sr *slowReader) Read(b []byte) (n int, err error) {
	read := 0
	for read < len(b) {
		sr.bucket.Wait(blockSize)
		upper := int64(read) + blockSize
		if upper > int64(len(b)) {
			upper = int64(len(b))
		}
		slice := b[read:upper]
		n, err := sr.reader.Read(slice)
		read += n
		if err != nil || n < len(slice) {
			return read, err
		}
	}
	return read, nil
}

type slowWriter struct {
	writer io.Writer
	bucket *ratelimit.Bucket
}

func (w *slowWriter) Write(b []byte) (n int, err error) {
	written := 0
	for written < len(b) {
		w.bucket.Wait(blockSize)

		upper := int64(written) + blockSize
		if upper > int64(len(b)) {
			upper = int64(len(b))
		}
		n, err := w.writer.Write(b[written:upper])
		written += n
		if err != nil {
			return written, err
		}
	}
	return written, nil
}

// SlowConn is a slow connection
type SlowConn struct {
	conn     net.Conn
	listener *SlowListener
	reader   *slowReader
	writer   *slowWriter
}

func newSlowConn(conn net.Conn, listener *SlowListener) *SlowConn {
	return &SlowConn{
		conn,
		listener,
		&slowReader{conn, listener.readbucket},
		&slowWriter{conn, listener.writebucket},
	}
}

// Read reads data from the connection.
// Read can be made to time out and return a Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetReadDeadline.
func (sc *SlowConn) Read(b []byte) (n int, err error) {
	return sc.reader.reader.Read(b)
}

// Write writes data to the connection.
// Write can be made to time out and return a Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
func (sc *SlowConn) Write(b []byte) (n int, err error) {
	return sc.writer.Write(b)
}

// Close closes the connection.
// Any blocked Read or Write operations will be unblocked and return errors.
func (sc *SlowConn) Close() error {
	return sc.conn.Close()
}

// LocalAddr returns the local network address.
func (sc *SlowConn) LocalAddr() net.Addr {
	return sc.conn.LocalAddr()
}

// RemoteAddr returns the remote network address.
func (sc *SlowConn) RemoteAddr() net.Addr {
	return sc.conn.RemoteAddr()
}

// SetDeadline sets the read and write deadlines associated
// with the connection. It is equivalent to calling both
// SetReadDeadline and SetWriteDeadline.
//
// A deadline is an absolute time after which I/O operations
// fail with a timeout (see type Error) instead of
// blocking. The deadline applies to all future I/O, not just
// the immediately following call to Read or Write.
//
// An idle timeout can be implemented by repeatedly extending
// the deadline after successful Read or Write calls.
//
// A zero value for t means I/O operations will not time out.
func (sc *SlowConn) SetDeadline(t time.Time) error {
	return sc.conn.SetDeadline(t)
}

// SetReadDeadline sets the deadline for future Read calls.
// A zero value for t means Read will not time out.
func (sc *SlowConn) SetReadDeadline(t time.Time) error {
	return sc.conn.SetReadDeadline(t)
}

// SetWriteDeadline sets the deadline for future Write calls.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
// A zero value for t means Write will not time out.
func (sc *SlowConn) SetWriteDeadline(t time.Time) error {
	return sc.conn.SetWriteDeadline(t)
}

// SlowListener is a listener that limits global IO over all connections
type SlowListener struct {
	listener    net.Listener
	readbucket  *ratelimit.Bucket
	writebucket *ratelimit.Bucket
}

// NewSlowListener creates a SlowListener with specified read and write rates.
// Both the readrate and the writerate are specified in bytes per second. A
// value of 0 disables throttling.
func NewSlowListener(listener net.Listener, readrate uint, writerate uint) net.Listener {
	if readrate == 0 {
		readrate = MaxRate
	}
	if writerate == 0 {
		writerate = MaxRate
	}
	return &SlowListener{
		listener:    listener,
		readbucket:  ratelimit.NewBucketWithRate(float64(readrate), capacity),
		writebucket: ratelimit.NewBucketWithRate(float64(writerate), capacity),
	}
}

// Accept waits for and returns the next connection to the listener.
func (l *SlowListener) Accept() (net.Conn, error) {
	conn, err := l.listener.Accept()
	if err != nil {
		return nil, err
	}
	return newSlowConn(conn, l), nil
}

// Close closes the listener.
func (l *SlowListener) Close() error {
	return l.listener.Close()
}

// Addr returns the listener's network address.
func (l *SlowListener) Addr() net.Addr {
	return l.listener.Addr()
}
<commit_msg>Fix throttling of data upload<commit_after>// Package slowdown provides an implementation of net.Listener that limits
// bandwidth.
package slowdown

import (
	"io"
	"net"
	"time"

	"github.com/juju/ratelimit"
)

// The maximum rate you should specify for readrate or writerate.If this is too
// high, the token bucket implementation seems to break down.
var MaxRate uint = (1024 * 1024) * 1000

var blockSize = int64(1024)
var capacity = int64(blockSize * 4)

type slowReader struct {
	reader io.Reader
	bucket *ratelimit.Bucket
}

func (sr *slowReader) Read(b []byte) (n int, err error) {
	read := 0
	for read < len(b) {
		sr.bucket.Wait(blockSize)
		upper := int64(read) + blockSize
		if upper > int64(len(b)) {
			upper = int64(len(b))
		}
		slice := b[read:upper]
		n, err := sr.reader.Read(slice)
		read += n
		if err != nil || n < len(slice) {
			return read, err
		}
	}
	return read, nil
}

type slowWriter struct {
	writer io.Writer
	bucket *ratelimit.Bucket
}

func (w *slowWriter) Write(b []byte) (n int, err error) {
	written := 0
	for written < len(b) {
		w.bucket.Wait(blockSize)

		upper := int64(written) + blockSize
		if upper > int64(len(b)) {
			upper = int64(len(b))
		}
		n, err := w.writer.Write(b[written:upper])
		written += n
		if err != nil {
			return written, err
		}
	}
	return written, nil
}

// SlowConn is a slow connection
type SlowConn struct {
	conn     net.Conn
	listener *SlowListener
	reader   *slowReader
	writer   *slowWriter
}

func newSlowConn(conn net.Conn, listener *SlowListener) *SlowConn {
	return &SlowConn{
		conn,
		listener,
		&slowReader{conn, listener.readbucket},
		&slowWriter{conn, listener.writebucket},
	}
}

// Read reads data from the connection.
// Read can be made to time out and return a Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetReadDeadline.
func (sc *SlowConn) Read(b []byte) (n int, err error) {
	return sc.reader.Read(b)
}

// Write writes data to the connection.
// Write can be made to time out and return a Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
func (sc *SlowConn) Write(b []byte) (n int, err error) {
	return sc.writer.Write(b)
}

// Close closes the connection.
// Any blocked Read or Write operations will be unblocked and return errors.
func (sc *SlowConn) Close() error {
	return sc.conn.Close()
}

// LocalAddr returns the local network address.
func (sc *SlowConn) LocalAddr() net.Addr {
	return sc.conn.LocalAddr()
}

// RemoteAddr returns the remote network address.
func (sc *SlowConn) RemoteAddr() net.Addr {
	return sc.conn.RemoteAddr()
}

// SetDeadline sets the read and write deadlines associated
// with the connection. It is equivalent to calling both
// SetReadDeadline and SetWriteDeadline.
//
// A deadline is an absolute time after which I/O operations
// fail with a timeout (see type Error) instead of
// blocking. The deadline applies to all future I/O, not just
// the immediately following call to Read or Write.
//
// An idle timeout can be implemented by repeatedly extending
// the deadline after successful Read or Write calls.
//
// A zero value for t means I/O operations will not time out.
func (sc *SlowConn) SetDeadline(t time.Time) error {
	return sc.conn.SetDeadline(t)
}

// SetReadDeadline sets the deadline for future Read calls.
// A zero value for t means Read will not time out.
func (sc *SlowConn) SetReadDeadline(t time.Time) error {
	return sc.conn.SetReadDeadline(t)
}

// SetWriteDeadline sets the deadline for future Write calls.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
// A zero value for t means Write will not time out.
func (sc *SlowConn) SetWriteDeadline(t time.Time) error {
	return sc.conn.SetWriteDeadline(t)
}

// SlowListener is a listener that limits global IO over all connections
type SlowListener struct {
	listener    net.Listener
	readbucket  *ratelimit.Bucket
	writebucket *ratelimit.Bucket
}

// NewSlowListener creates a SlowListener with specified read and write rates.
// Both the readrate and the writerate are specified in bytes per second. A
// value of 0 disables throttling.
func NewSlowListener(listener net.Listener, readrate uint, writerate uint) net.Listener {
	if readrate == 0 {
		readrate = MaxRate
	}
	if writerate == 0 {
		writerate = MaxRate
	}
	return &SlowListener{
		listener:    listener,
		readbucket:  ratelimit.NewBucketWithRate(float64(readrate), capacity),
		writebucket: ratelimit.NewBucketWithRate(float64(writerate), capacity),
	}
}

// Accept waits for and returns the next connection to the listener.
func (l *SlowListener) Accept() (net.Conn, error) {
	conn, err := l.listener.Accept()
	if err != nil {
		return nil, err
	}
	return newSlowConn(conn, l), nil
}

// Close closes the listener.
func (l *SlowListener) Close() error {
	return l.listener.Close()
}

// Addr returns the listener's network address.
func (l *SlowListener) Addr() net.Addr {
	return l.listener.Addr()
}
<|endoftext|>


<commit_before>package arena

import "math/rand"

type Arena interface {
	State() State
	Tick()
	SetSnakeHeading(h Direction)
	AddSnake(x, y, size int, h Direction)
}

type Direction int

const (
	EAST = Direction(iota)
	NORTH
	WEST
	SOUTH
)

type Position struct {
	X, Y int
}

type State struct {
	Size       Position
	Snakes     []Snake
	PointItem  Position
	GameIsOver bool
}

func (s State) Equal(other State) bool {
	if s.Size != other.Size {
		return false
	}
	if !s.Snakes[0].Equal(other.Snakes[0]) {
		return false
	}
	if s.GameIsOver != other.GameIsOver {
		return false
	}
	if s.PointItem != other.PointItem {
		return false
	}
	return true
}

type Snake struct {
	Segments []Position
	Heading  Direction
}

func (s Snake) Equal(other Snake) bool {
	if s.Heading != other.Heading {
		return false
	}
	if s.Length() != other.Length() {
		return false
	}
	for i := range s.Segments {
		if s.Segments[i] != other.Segments[i] {
			return false
		}
	}
	return true
}

func (s Snake) Head() Position {
	return s.Segments[0]
}

func (s Snake) Length() int {
	return len(s.Segments)
}

func (s *Snake) moveHead() {
	switch s.Heading {
	case EAST:
		s.Segments[0].X += 1
	case NORTH:
		s.Segments[0].Y -= 1
	case WEST:
		s.Segments[0].X -= 1
	case SOUTH:
		s.Segments[0].Y += 1
	}
}

func (s *Snake) extrudeBody() {
	s.Segments = s.Segments[:len(s.Segments)+1]
	for i := len(s.Segments) - 1; i > 0; i-- {
		s.Segments[i] = s.Segments[i-1]
	}
}

func (s *Snake) contractBody() {
	s.Segments = s.Segments[:len(s.Segments)-1]
}

func (s *Snake) extrude() {
	s.extrudeBody()
	s.moveHead()
}

func (s Snake) Copy() Snake {
	segments := make([]Position, len(s.Segments))
	copy(segments, s.Segments)
	return Snake{Segments: segments, Heading: s.Heading}
}

type arena struct {
	size       Position
	snakes     []Snake
	pointItem  Position
	gameIsOver bool
}

func (a arena) copySnakes() []Snake {
	snakes := make([]Snake, len(a.snakes))
	for i, snake := range a.snakes {
		snakes[i] = snake.Copy()
	}
	return snakes
}

func (a arena) State() State {
	return State{
		Size:       a.size,
		Snakes:     a.copySnakes(),
		PointItem:  a.pointItem,
		GameIsOver: a.gameIsOver,
	}
}

func inSequence(p Position, sequence []Position) bool {
	for _, item := range sequence {
		if p == item {
			return true
		}
	}
	return false
}

func (a arena) insideArena(p Position) bool {
	if p.X < 0 || p.X >= a.size.X || p.Y < 0 || p.Y >= a.size.Y {
		return false
	}
	return true
}

func (a *arena) endGame() {
	a.gameIsOver = true
}

func (a *arena) Tick() {
	if a.gameIsOver {
		return
	}
	a.snakes[0].extrude()
	if a.snakes[0].Head() == a.pointItem {
		a.setRandomPositionForPointItem()
	} else {
		a.snakes[0].contractBody()
	}

	if inSequence(a.snakes[0].Head(), a.snakes[0].Segments[1:]) {
		a.endGame()
	}

	if !a.insideArena(a.snakes[0].Head()) {
		a.endGame()
	}
}

func (a *arena) SetSnakeHeading(h Direction) {
	a.snakes[0].Heading = h
}

func (a arena) isValidPointItemPosition(p Position) bool {
	if p.X < 0 || p.X >= a.size.X {
		return false
	}
	if p.Y < 0 || p.Y >= a.size.Y {
		return false
	}
	if inSequence(p, a.snakes[0].Segments) {
		return false
	}
	return true
}

func (a arena) getValidPositions() []Position {
	valid_positions := make([]Position, 0, a.size.X*a.size.Y)
	for i := 0; i < a.size.X; i++ {
		for j := 0; j < a.size.Y; j++ {
			p := Position{i, j}
			if a.isValidPointItemPosition(p) {
				valid_positions = append(valid_positions, p)
			}
		}
	}
	return valid_positions

}

func (a *arena) setRandomPositionForPointItem() {
	valid_positions := a.getValidPositions()
	if len(valid_positions) == 0 {
		a.endGame()
	} else {
		a.pointItem = valid_positions[rand.Intn(len(valid_positions))]
	}
}

func (a* arena) AddSnake(x, y, size int, heading Direction) {
	if heading != EAST {
		panic("Other headings are not implemented.")
	}
	a.snakes = append(a.snakes, newSnake(x, y, size))
}

func newSnake(x, y int, size int) Snake {
	segments := make([]Position, size, size*10)
	s := Snake{Segments: segments}
	for i := 0; i < size; i++ {
		s.Segments[i] = Position{x - i, y}
	}
	return s
}

func New(width, height int) Arena {
	a := arena{size: Position{width, height}}
	a.AddSnake(width/2, height/2, 5, EAST)
	a.setRandomPositionForPointItem()
	return &a
}

<commit_msg>Modify isValidPointItemPosition to work with all snakes.<commit_after>package arena

import "math/rand"

type Arena interface {
	State() State
	Tick()
	SetSnakeHeading(h Direction)
	AddSnake(x, y, size int, h Direction)
}

type Direction int

const (
	EAST = Direction(iota)
	NORTH
	WEST
	SOUTH
)

type Position struct {
	X, Y int
}

type State struct {
	Size       Position
	Snakes     []Snake
	PointItem  Position
	GameIsOver bool
}

func (s State) Equal(other State) bool {
	if s.Size != other.Size {
		return false
	}
	if !s.Snakes[0].Equal(other.Snakes[0]) {
		return false
	}
	if s.GameIsOver != other.GameIsOver {
		return false
	}
	if s.PointItem != other.PointItem {
		return false
	}
	return true
}

type Snake struct {
	Segments []Position
	Heading  Direction
}

func (s Snake) Equal(other Snake) bool {
	if s.Heading != other.Heading {
		return false
	}
	if s.Length() != other.Length() {
		return false
	}
	for i := range s.Segments {
		if s.Segments[i] != other.Segments[i] {
			return false
		}
	}
	return true
}

func (s Snake) Head() Position {
	return s.Segments[0]
}

func (s Snake) Length() int {
	return len(s.Segments)
}

func (s *Snake) moveHead() {
	switch s.Heading {
	case EAST:
		s.Segments[0].X += 1
	case NORTH:
		s.Segments[0].Y -= 1
	case WEST:
		s.Segments[0].X -= 1
	case SOUTH:
		s.Segments[0].Y += 1
	}
}

func (s *Snake) extrudeBody() {
	s.Segments = s.Segments[:len(s.Segments)+1]
	for i := len(s.Segments) - 1; i > 0; i-- {
		s.Segments[i] = s.Segments[i-1]
	}
}

func (s *Snake) contractBody() {
	s.Segments = s.Segments[:len(s.Segments)-1]
}

func (s *Snake) extrude() {
	s.extrudeBody()
	s.moveHead()
}

func (s Snake) Copy() Snake {
	segments := make([]Position, len(s.Segments))
	copy(segments, s.Segments)
	return Snake{Segments: segments, Heading: s.Heading}
}

type arena struct {
	size       Position
	snakes     []Snake
	pointItem  Position
	gameIsOver bool
}

func (a arena) copySnakes() []Snake {
	snakes := make([]Snake, len(a.snakes))
	for i, snake := range a.snakes {
		snakes[i] = snake.Copy()
	}
	return snakes
}

func (a arena) State() State {
	return State{
		Size:       a.size,
		Snakes:     a.copySnakes(),
		PointItem:  a.pointItem,
		GameIsOver: a.gameIsOver,
	}
}

func inSequence(p Position, sequence []Position) bool {
	for _, item := range sequence {
		if p == item {
			return true
		}
	}
	return false
}

func (a arena) insideArena(p Position) bool {
	if p.X < 0 || p.X >= a.size.X || p.Y < 0 || p.Y >= a.size.Y {
		return false
	}
	return true
}

func (a *arena) endGame() {
	a.gameIsOver = true
}

func (a *arena) Tick() {
	if a.gameIsOver {
		return
	}
	a.snakes[0].extrude()
	if a.snakes[0].Head() == a.pointItem {
		a.setRandomPositionForPointItem()
	} else {
		a.snakes[0].contractBody()
	}

	if inSequence(a.snakes[0].Head(), a.snakes[0].Segments[1:]) {
		a.endGame()
	}

	if !a.insideArena(a.snakes[0].Head()) {
		a.endGame()
	}
}

func (a *arena) SetSnakeHeading(h Direction) {
	a.snakes[0].Heading = h
}

func (a arena) isValidPointItemPosition(p Position) bool {
	if p.X < 0 || p.X >= a.size.X {
		return false
	}
	if p.Y < 0 || p.Y >= a.size.Y {
		return false
	}
	for _, snake := range a.snakes {
		if inSequence(p, snake.Segments) {
			return false
		}
	}
	return true
}

func (a arena) getValidPositions() []Position {
	valid_positions := make([]Position, 0, a.size.X*a.size.Y)
	for i := 0; i < a.size.X; i++ {
		for j := 0; j < a.size.Y; j++ {
			p := Position{i, j}
			if a.isValidPointItemPosition(p) {
				valid_positions = append(valid_positions, p)
			}
		}
	}
	return valid_positions

}

func (a *arena) setRandomPositionForPointItem() {
	valid_positions := a.getValidPositions()
	if len(valid_positions) == 0 {
		a.endGame()
	} else {
		a.pointItem = valid_positions[rand.Intn(len(valid_positions))]
	}
}

func (a* arena) AddSnake(x, y, size int, heading Direction) {
	if heading != EAST {
		panic("Other headings are not implemented.")
	}
	a.snakes = append(a.snakes, newSnake(x, y, size))
}

func newSnake(x, y int, size int) Snake {
	segments := make([]Position, size, size*10)
	s := Snake{Segments: segments}
	for i := 0; i < size; i++ {
		s.Segments[i] = Position{x - i, y}
	}
	return s
}

func New(width, height int) Arena {
	a := arena{size: Position{width, height}}
	a.setRandomPositionForPointItem()
	return &a
}
<|endoftext|>


<commit_before>// snapshot package.
package snapshot

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/dao"
	"github.com/zenoss/serviced/volume"

	"errors"
	"fmt"
	"os/exec"
	"os/user"
	"time"
)

// getServiceDockerId returns the DockerId for the running container tied to the service
// Servicestate.DockerId is a one to one relationship to Service.Id
func getServiceDockerId(cpDao dao.ControlPlane, service *dao.Service) (string, error) {
	var states []*dao.ServiceState
	if err := cpDao.GetServiceStates(service.Id, &states); err != nil {
		return "", err
	}

	if len(states) > 1 {
		glog.Warningf("more than one ServiceState found for serviceId:%s ===> states:%+v", service.Id, states)
	}

	for _, state := range states {
		// return the DockerId of the first ServiceState
		if state.DockerId == "" {
			return "", errors.New(fmt.Sprintf("unable to find DockerId for service:%+v", service))
		}
		return state.DockerId, nil
	}

	return "", errors.New(fmt.Sprintf("unable to find DockerId for service:%+v", service))
}

// runCommandInServiceContainer runs a command in a running container
func runCommandInServiceContainer(serviceId string, dockerId string, command string) (string, error) {
	cmd := exec.Command("lxc-attach", "-n", dockerId, "--", command)
	output, err := cmd.CombinedOutput()
	if err != nil {
		glog.Errorf("Error running cmd:'%s' for serviceId:%s - error:%s", command, serviceId, err)
		return string(output), err
	}
	glog.V(0).Infof("Successfully ran cmd:'%s' for serviceId:%s - output: %s", command, serviceId, string(output))
	return string(output), nil
}

// ExecuteSnapshot is called by the Leader to perform the snapshot
func ExecuteSnapshot(cpDao dao.ControlPlane, serviceId string, label *string) error {
	glog.V(2).Infof("snapshot.ExecuteSnapshot service=%+v", serviceId)

	var tenantId string
	if err := cpDao.GetTenantId(serviceId, &tenantId); err != nil {
		glog.V(2).Infof("snapshot.ExecuteSnapshot cpDao.GetTenantId() service=%+v err=%s", serviceId, err)
		return err
	}
	var service dao.Service
	if err := cpDao.GetService(tenantId, &service); err != nil {
		glog.V(2).Infof("snapshot.ExecuteSnapshot cpDao.GetService() service=%+v err=%s", serviceId, err)
		return err
	}

	// simplest case - do everything here

	// call quiesce pause/resume for services with 'Snapshot' definition
	// only root can run lxc-attach
	if whoami, err := user.Current(); err != nil {
		glog.Errorf("Unable to pause service - not able to retrieve user info error: %v", err)
		return err
	} else if "root" != whoami.Username {
		glog.Warningf("Unable to pause service - Username is not root - whoami:%+v", whoami)
	} else {
		var request dao.EntityRequest
		var servicesList []*dao.Service
		if err := cpDao.GetServices(request, &servicesList); err != nil {
			return err
		}
		for _, service := range servicesList {
			dockerId, err := getServiceDockerId(cpDao, service)
			if err != nil {
				glog.Warningf("Unable to pause service - not able to get DockerId for service:%+v", service)
				continue
			}

			if service.Snapshot.Pause != "" && service.Snapshot.Resume != "" {
				_, err := runCommandInServiceContainer(service.Id, dockerId, service.Snapshot.Pause)
				defer runCommandInServiceContainer(service.Id, dockerId, service.Snapshot.Resume)
				if err != nil {
					return err
				}
			}
		}
	}

	// create a snapshot
	var theVolume *volume.Volume
	if err := cpDao.GetVolume(tenantId, &theVolume); err != nil {
		glog.V(2).Infof("snapshot.ExecuteSnapshot cpDao.GetVolume() service=%+v err=%s", serviceId, err)
		return err
	} else if theVolume == nil {
		glog.V(2).Infof("snapshot.ExecuteSnapshot cpDao.GetVolume() volume is nil service=%+v", serviceId)
		return errors.New(fmt.Sprintf("GetVolume() is nil - tenantId:%s", tenantId))
	} else {
		glog.V(2).Infof("snapshot.ExecuteSnapshot service=%+v theVolume=%+v", service, theVolume)
		snapLabel := snapShotName(theVolume.Name())
		if err := theVolume.Snapshot(snapLabel); err != nil {
			return err
		} else {
			*label = snapLabel
		}
	}

	glog.V(2).Infof("Successfully created snapshot for service:%s - label:%s", serviceId, label)
	return nil
}

func snapShotName(volumeName string) string {
	format := "20060102-150405"
	loc := time.Now()
	utc := loc.UTC()
	return volumeName + "_" + utc.Format(format)
}
<commit_msg>fixed getServiceDockerId(); use -e with lxc-attach to keep elevated privileges for quiesce-rabbitmq.sh<commit_after>// snapshot package.
package snapshot

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/dao"
	"github.com/zenoss/serviced/volume"

	"errors"
	"fmt"
	"os/exec"
	"os/user"
	"strings"
	"time"
)

// getServiceDockerId returns the DockerId for the running container tied to the service
// assumption: Servicestate.DockerId is a one to one relationship to ServiceId
func getServiceDockerId(cpDao dao.ControlPlane, serviceId string) (string, error) {
	var states []*dao.ServiceState
	if err := cpDao.GetServiceStates(serviceId, &states); err != nil {
		return "", err
	}

	if len(states) > 1 {
		glog.Warningf("more than one ServiceState found for serviceId:%s numServiceStates:%d", serviceId, len(states))
	}

	// return the DockerId of the first ServiceState that matches serviceId
	for i, state := range states {
		glog.V(3).Infof("DEBUG states[%d]: serviceId:%s state:%+v", i, serviceId, state)
		if state.DockerId != "" && state.ServiceId == serviceId {
			return state.DockerId, nil
		}
	}

	return "", errors.New(fmt.Sprintf("unable to find DockerId for serviceId:%s", serviceId))
}

// runCommandInServiceContainer runs a command in a running container
func runCommandInServiceContainer(serviceId string, dockerId string, command string) (string, error) {
	dockerCommand := []string{"lxc-attach", "-n", dockerId, "-e", "--", command}
	cmd := exec.Command(dockerCommand[0], dockerCommand[1:len(dockerCommand)]...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		glog.Errorf("Error running cmd:'%s' for serviceId:%s - error:%s", strings.Join(dockerCommand, " "), serviceId, err)
		return string(output), err
	}
	glog.V(0).Infof("Successfully ran cmd:'%s' for serviceId:%s - output: %s", strings.Join(dockerCommand, " "), serviceId, string(output))
	return string(output), nil
}

// ExecuteSnapshot is called by the Leader to perform the snapshot
func ExecuteSnapshot(cpDao dao.ControlPlane, serviceId string, label *string) error {
	glog.V(2).Infof("snapshot.ExecuteSnapshot service=%+v", serviceId)

	var tenantId string
	if err := cpDao.GetTenantId(serviceId, &tenantId); err != nil {
		glog.V(2).Infof("snapshot.ExecuteSnapshot cpDao.GetTenantId() service=%+v err=%s", serviceId, err)
		return err
	}
	var service dao.Service
	if err := cpDao.GetService(tenantId, &service); err != nil {
		glog.V(2).Infof("snapshot.ExecuteSnapshot cpDao.GetService() service=%+v err=%s", serviceId, err)
		return err
	}

	// simplest case - do everything here

	// call quiesce for services with 'Snapshot.Pause' and 'Snapshot.Resume' definition
	// only root can run lxc-attach
	if whoami, err := user.Current(); err != nil {
		glog.Errorf("Unable to snapshot service - not able to retrieve user info error: %v", err)
		return err
	} else if "root" != whoami.Username {
		glog.Warningf("Unable to pause/resume service - Username is not root - whoami:%+v", whoami)
	} else {
		var request dao.EntityRequest
		var servicesList []*dao.Service
		if err := cpDao.GetServices(request, &servicesList); err != nil {
			return err
		}
		for _, service := range servicesList {
			if service.Snapshot.Pause == "" || service.Snapshot.Resume == "" {
				continue
			}

			dockerId, err := getServiceDockerId(cpDao, service.Id)
			if err != nil {
				glog.Warningf("Unable to pause service - not able to get DockerId for service.Id:%s service.Name:%s error:%s", service.Id, service.Name, err)
				continue
			}

			_, err = runCommandInServiceContainer(service.Id, dockerId, service.Snapshot.Pause)
			defer runCommandInServiceContainer(service.Id, dockerId, service.Snapshot.Resume)
			if err != nil {
				return err
			}
		}
	}

	// create a snapshot
	var theVolume *volume.Volume
	if err := cpDao.GetVolume(tenantId, &theVolume); err != nil {
		glog.V(2).Infof("snapshot.ExecuteSnapshot cpDao.GetVolume() service=%+v err=%s", service, err)
		return err
	} else if theVolume == nil {
		glog.V(2).Infof("snapshot.ExecuteSnapshot cpDao.GetVolume() volume is nil service=%+v", service)
		return errors.New(fmt.Sprintf("GetVolume() is nil - tenantId:%s", tenantId))
	} else {
		glog.V(2).Infof("snapshot.ExecuteSnapshot service=%+v theVolume=%+v", service, theVolume)
		snapLabel := snapShotName(theVolume.Name())
		if err := theVolume.Snapshot(snapLabel); err != nil {
			return err
		} else {
			*label = snapLabel
		}
	}

	glog.V(2).Infof("Successfully created snapshot for service Id:%s Name:%s Label:%s", service.Id, service.Name, label)
	return nil
}

func snapShotName(volumeName string) string {
	format := "20060102-150405"
	loc := time.Now()
	utc := loc.UTC()
	return volumeName + "_" + utc.Format(format)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/pressly/chi"
	"net/http"
	"os"
	"bufio"
	"fmt"
	"strings"
	"io"
	"time"
	"html/template"
	"io/ioutil"
	"flag"
	"log"
	"strconv"
)

func main() {
	r := chi.NewRouter()

	r.Get("/", showFiles)
	r.Get("/*", showFiles)
	r.With(statusCodeHandler).Post("/", handlePost)

	port := flag.String("port", "80", "Specifies the port to listen for incoming connections")
	useTls := flag.Bool("tls", false, "Tells gobble to listen for secure connections (ie. https)")
	tlsPort := flag.String("tlsPort", "443", "Specifies the port to listen for incoming secure connections")
	tlsCert := flag.String("tlsCert", "cert.pem", "Specifies the path to the x509 certificate")
	tlsKey := flag.String("tlsKey", "key.pem", "Specifies the path to the private key corresponding to the x509 certificate")

	homeDir := flag.String("dir", "public", "Specifies the root directory which all directories and requests will be stored under")
	flag.Parse()

	err := os.MkdirAll(*homeDir, 0644)
	if err != nil {
		panic("unable to create dir")
	}
	os.Chdir(*homeDir)

	if *useTls == true {
		go func(tlsPort *string, tlsCert *string, tlsKey *string) {
			log.Println("Starting secure server on port " + *tlsPort)
			log.Fatal(http.ListenAndServeTLS(":" + *tlsPort, *tlsCert, *tlsKey, r))
		}(tlsPort, tlsCert, tlsKey)
	}

	log.Println("Starting server on port " + *port)
	log.Fatal(http.ListenAndServe(":" + *port, r))
}

func showFiles(w http.ResponseWriter, r *http.Request) {

	t := template.Must(template.New("index").Parse(`{{define "index"}}
		{{range .Files}}
		{{.Name}}

		{{end}}
		{{end}}`))

	path := chi.URLParam(r, "*")

	if info, err := os.Stat("./" + path); err == nil {
		if info.IsDir() {
			files, err := ioutil.ReadDir("./" + path)
			if err != nil {
				panic("Unable to read directory")
			}

			templData := struct {
				Path  string
				Files []os.FileInfo
			}{
				info.Name(),
				files,
			}

			t.Execute(w, templData)
		} else {
			f, _ := ioutil.ReadFile(path)
			w.Write(f)
		}
	} else {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}
}

func handlePost(w http.ResponseWriter, r *http.Request) {
	t := time.Now()
	dir := r.URL.Query().Get("dir")
	if dir != "" {
		//Make sure the requested directory is around
		err := os.MkdirAll(dir, 0644)
		if err != nil {
			panic("unable to create dir")
		}
	} else {
		//No directory requested so we give them the default
		dir = t.Format("2006-01-02")
		err := os.MkdirAll(dir, 0644)
		if err != nil {
			panic("unable to create dir")
		}
	}

	//Create file which is named after the create time
	fo, err := os.Create("./" + dir + "/" + t.Format("15.04.05.0000"))
	if err != nil {
		panic(err)
	}
	// close fo on exit and check for its returned error
	defer func() {
		if err := fo.Close(); err != nil {
			panic(err)
		}
	}()

	writer := bufio.NewWriter(fo)
	defer writer.Flush()

	//Write headers to file
	for k, v := range r.Header {
		fmt.Fprintln(writer, k + ":", strings.Join(v, ","))
	}

	fmt.Fprintln(writer)
	//Write request body to file
	io.Copy(writer, r.Body)
	w.Write([]byte(fo.Name()[1:]))
}

func statusCodeHandler(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Query().Get("status_code") != "" {
			status, err := strconv.Atoi(r.URL.Query().Get("status_code"))
			if err == nil {
				w.WriteHeader(status)
			} else {
				log.Println("Invalid number in status_code field")
			}
		}
		next.ServeHTTP(w, r)
	})
}<commit_msg>Added basic auth for reading of post messages.<commit_after>package main

import (
	"github.com/pressly/chi"
	"net/http"
	"os"
	"bufio"
	"fmt"
	"strings"
	"io"
	"time"
	"html/template"
	"io/ioutil"
	"flag"
	"log"
	"strconv"
)

var (
	usernameFlag string
	passwordFlag string
)

func main() {
	r := chi.NewRouter()

	r.With(basicAuth).Get("/", showFiles)
	r.With(basicAuth).Get("/*", showFiles)
	r.With(statusCodeHandler).Post("/", handlePost)

	port := flag.String("port", "80", "Specifies the port to listen for incoming connections")
	useTls := flag.Bool("tls", false, "Tells gobble to listen for secure connections (ie. https)")
	tlsPort := flag.String("tlsPort", "443", "Specifies the port to listen for incoming secure connections")
	tlsCert := flag.String("tlsCert", "cert.pem", "Specifies the path to the x509 certificate")
	tlsKey := flag.String("tlsKey", "key.pem", "Specifies the path to the private key corresponding to the x509 certificate")
	usernameFlag = flag.String("username", "", "Specify a username to protect againt unauthorized reading of your requests")
	passwordFlag = flag.String("password", "", "Specify a password to protect against unauthorized reading of your requests")

	homeDir := flag.String("dir", "public", "Specifies the root directory which all directories and requests will be stored under")
	flag.Parse()

	err := os.MkdirAll(*homeDir, 0644)
	if err != nil {
		panic("unable to create dir")
	}
	os.Chdir(*homeDir)

	if *useTls == true {
		go func(tlsPort *string, tlsCert *string, tlsKey *string) {
			log.Println("Starting secure server on port " + *tlsPort)
			log.Fatal(http.ListenAndServeTLS(":" + *tlsPort, *tlsCert, *tlsKey, r))
		}(tlsPort, tlsCert, tlsKey)
	}

	log.Println("Starting server on port " + *port)
	log.Fatal(http.ListenAndServe(":" + *port, r))
}

func showFiles(w http.ResponseWriter, r *http.Request) {

	t := template.Must(template.New("index").Parse(`{{define "index"}}
		{{range .Files}}
		{{.Name}}

		{{end}}
		{{end}}`))

	path := chi.URLParam(r, "*")

	if info, err := os.Stat("./" + path); err == nil {
		if info.IsDir() {
			files, err := ioutil.ReadDir("./" + path)
			if err != nil {
				panic("Unable to read directory")
			}

			templData := struct {
				Path  string
				Files []os.FileInfo
			}{
				info.Name(),
				files,
			}

			t.Execute(w, templData)
		} else {
			f, _ := ioutil.ReadFile(path)
			w.Write(f)
		}
	} else {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}
}

func handlePost(w http.ResponseWriter, r *http.Request) {
	t := time.Now()
	dir := r.URL.Query().Get("dir")
	if dir != "" {
		//Make sure the requested directory is around
		err := os.MkdirAll(dir, 0644)
		if err != nil {
			panic("unable to create dir")
		}
	} else {
		//No directory requested so we give them the default
		dir = t.Format("2006-01-02")
		err := os.MkdirAll(dir, 0644)
		if err != nil {
			panic("unable to create dir")
		}
	}

	//Create file which is named after the create time
	fo, err := os.Create("./" + dir + "/" + t.Format("15.04.05.0000"))
	if err != nil {
		panic(err)
	}
	// close fo on exit and check for its returned error
	defer func() {
		if err := fo.Close(); err != nil {
			panic(err)
		}
	}()

	writer := bufio.NewWriter(fo)
	defer writer.Flush()

	//Write headers to file
	for k, v := range r.Header {
		fmt.Fprintln(writer, k + ":", strings.Join(v, ","))
	}

	fmt.Fprintln(writer)
	//Write request body to file
	io.Copy(writer, r.Body)
	w.Write([]byte(fo.Name()[1:]))
}

func statusCodeHandler(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Query().Get("status_code") != "" {
			status, err := strconv.Atoi(r.URL.Query().Get("status_code"))
			if err == nil {
				w.WriteHeader(status)
			} else {
				log.Println("Invalid number in status_code field")
			}
		}
		next.ServeHTTP(w, r)
	})
}

func basicAuth(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		username, password, _ := r.BasicAuth()

		//If no auth was set up then we just serve the page
		if usernameFlag == "" || passwordFlag == "" {
			h.ServeHTTP(w, r)
		}

		//Auth was configured so we check to make sure the user has the correct credentials
		if username != usernameFlag || password != passwordFlag {
			w.Header().Add("WWW-Authenticate", `Basic realm="Restricted"`)
			http.Error(w, "Not Authorized", http.StatusUnauthorized)
			return
		}
		h.ServeHTTP(w, r)
	})
}<|endoftext|>


<commit_before>/*
Copyright 2016 Google Inc. All Rights Reserved.
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.
*/

// The googet binary is the client for the GoGet packaging system, it performs the listing,
// getting, installing and removing functions on client machines.
package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/go-yaml/yaml"
	"github.com/google/googet/client"
	"github.com/google/googet/goolib"
	"github.com/google/googet/system"
	"github.com/google/logger"
	"github.com/google/subcommands"
	"github.com/olekukonko/tablewriter"
)

const (
	stateFile = "googet.state"
	confFile  = "googet.conf"
	logFile   = "googet.log"
	lockFile  = "googet.lock"
	cacheDir  = "cache"
	repoDir   = "repos"
	envVar    = "GooGetRoot"
	logSize   = 10 * 1024 * 1024
)

var (
	rootDir        string
	noConfirm      bool
	verbose        bool
	systemLog      bool
	showVer        bool
	version        string
	cacheLife      = 3 * time.Minute
	archs          []string
	proxyServer    string
	allowUnsafeURL bool
)

type packageMap map[string]string

// installedPackages returns a packagemap of all installed packages based on the
// googet state file given.
func installedPackages(state client.GooGetState) packageMap {
	pm := make(packageMap)
	for _, p := range state {
		pm[p.PackageSpec.Name+"."+p.PackageSpec.Arch] = p.PackageSpec.Version
	}
	return pm
}

type repoFile struct {
	fileName    string
	repoEntries []repoEntry
}

type repoEntry struct {
	LName string `yaml:"name,omitempty"`
	LURL  string `yaml:"url,omitempty"`
	Name  string `yaml:"Name,omitempty"`
	URL   string `yaml:"URL,omitempty"`
}

func writeRepoFile(rf repoFile) error {
	d, err := yaml.Marshal(rf.repoEntries)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(rf.fileName, d, 0664)
}

func unmarshalRepoFile(p string) (repoFile, error) {
	b, err := ioutil.ReadFile(p)
	if err != nil {
		return repoFile{}, err
	}

	// Don't try to unmarshal files with no YAML content
	var yml bool
	lns := strings.Split(string(b), "\n")
	for _, ln := range lns {
		ln = strings.TrimSpace(ln)
		if !strings.HasPrefix(ln, "#") && ln != "" {
			yml = true
			break
		}
	}
	if !yml {
		return repoFile{}, nil
	}

	// Both repoFile and []repoFile are valid for backwards compatibility.
	var re repoEntry
	if err := yaml.Unmarshal(b, &re); err == nil && (re.URL != "" || re.LURL != "") {
		return repoFile{fileName: p, repoEntries: []repoEntry{re}}, nil
	}

	var res []repoEntry
	if err := yaml.Unmarshal(b, &res); err != nil {
		return repoFile{}, err
	}
	return repoFile{fileName: p, repoEntries: res}, nil
}

type conf struct {
	Archs          []string
	CacheLife      string
	ProxyServer    string
	AllowUnsafeURL bool
}

func unmarshalConfFile(p string) (*conf, error) {
	b, err := ioutil.ReadFile(p)
	if err != nil {
		return nil, err
	}
	var cf conf
	return &cf, yaml.Unmarshal(b, &cf)
}

func repoList(dir string) ([]string, error) {
	rfs, err := repos(dir)
	if err != nil {
		return nil, err
	}
	var rl []string
	for _, rf := range rfs {
		for _, re := range rf.repoEntries {
			switch {
			case re.URL != "":
				rl = append(rl, re.URL)
			case re.LURL != "":
				rl = append(rl, re.LURL)
			}
		}
	}

	if !allowUnsafeURL {
		var srl []string
		for _, r := range rl {
			if strings.ToLower(r[0:5]) != "https" {
				logger.Errorf("%s will not be used as a repository, only https endpoints will be used unless AllowUnsafeURL is set to 'true' in googet.conf", r)
				continue
			}
			srl = append(srl, r)
		}
		return srl, nil
	}
	return rl, nil
}

func repos(dir string) ([]repoFile, error) {
	fl, err := filepath.Glob(filepath.Join(dir, "*.repo"))
	if err != nil {
		return nil, err
	}
	var rfs []repoFile
	for _, f := range fl {
		rf, err := unmarshalRepoFile(f)
		if err != nil {
			logger.Error(err)
			continue
		}
		if rf.fileName != "" {
			rfs = append(rfs, rf)
		}
	}
	return rfs, nil
}

func writeState(s *client.GooGetState, sf string) error {
	b, err := s.Marshal()
	if err != nil {
		return err
	}
	return ioutil.WriteFile(sf, b, 0664)
}

func readState(sf string) (*client.GooGetState, error) {
	b, err := ioutil.ReadFile(sf)
	if os.IsNotExist(err) {
		logger.Info("No state file found, assuming no packages installed.")
		return &client.GooGetState{}, nil
	}
	if err != nil {
		return nil, err
	}
	return client.UnmarshalState(b)
}

func buildSources(s string) ([]string, error) {
	if s != "" {
		srcs := strings.Split(s, ",")
		return srcs, nil
	}
	return repoList(filepath.Join(rootDir, repoDir))
}

func confirmation(msg string) bool {
	var c string
	fmt.Print(msg + " (y/N): ")
	fmt.Scanln(&c)
	c = strings.ToLower(c)
	return c == "y" || c == "yes"
}

func info(ps *goolib.PkgSpec, r string) {
	fmt.Println()

	pkgInfo := []struct {
		name, value string
	}{
		{"Name", ps.Name},
		{"Arch", ps.Arch},
		{"Version", ps.Version},
		{"Repo", path.Base(r)},
		{"Authors", ps.Authors},
		{"Owners", ps.Owners},
		{"Description", ps.Description},
		{"Dependencies", ""},
		{"ReleaseNotes", ""},
	}
	var w int
	for _, pi := range pkgInfo {
		if len(pi.name) > w {
			w = len(pi.name)
		}
	}
	wf := fmt.Sprintf("%%-%vs: %%s\n", w+1)

	for _, pi := range pkgInfo {
		if pi.name == "Dependencies" {
			var deps []string
			for p, v := range ps.PkgDependencies {
				deps = append(deps, p+" "+v)
			}
			if len(deps) == 0 {
				fmt.Printf(wf, pi.name, "None")
			} else {
				fmt.Printf(wf, pi.name, deps[0])
				for _, l := range deps[1:] {
					fmt.Printf(wf, "", l)
				}
			}
		} else if pi.name == "ReleaseNotes" && ps.ReleaseNotes != nil {
			sl, _ := tablewriter.WrapString(ps.ReleaseNotes[0], 76-w)
			fmt.Printf(wf, pi.name, sl[0])
			for _, l := range sl[1:] {
				fmt.Printf(wf, "", l)
			}
			for _, l := range ps.ReleaseNotes[1:] {
				sl, _ := tablewriter.WrapString(l, 76-w)
				fmt.Printf(wf, "", sl[0])
				for _, l := range sl[1:] {
					fmt.Printf(wf, "", l)
				}
			}
		} else {
			cl := strings.Split(strings.TrimSpace(pi.value), "\n")
			sl, _ := tablewriter.WrapString(cl[0], 76-w)
			fmt.Printf(wf, pi.name, sl[0])
			for _, l := range sl[1:] {
				fmt.Printf(wf, "", l)
			}
			for _, l := range cl[1:] {
				sl, _ := tablewriter.WrapString(l, 76-w)
				for _, l := range sl {
					fmt.Printf(wf, "", l)
				}
			}
		}
	}
}

func rotateLog(logPath string, ls int64) error {
	fi, err := os.Stat(logPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	if fi.Size() < ls {
		return nil
	}
	oldLog := logPath + ".old"
	if err := os.Rename(logPath, oldLog); err != nil {
		return fmt.Errorf("error moving log file: %v", err)
	}
	return nil
}

func lock(lf string) (*os.File, error) {
	// This locking process only works on Windows, on linux os.Remove will remove an open file.
	// This is not currently an issue as running googet on linux is only done for testing.
	// In the future using a semaphore for locking would be nice.
	// 90% of all GooGet runs happen in < 60s, we wait 70s.
	for i := 1; i < 15; i++ {
		// Try to remove any old lock file that may exist, ignore errors as we don't care if
		// we can't remove it or it does not exist.
		os.Remove(lf)
		if lk, err := os.OpenFile(lf, os.O_RDONLY|os.O_CREATE|os.O_EXCL, 0); err == nil {
			return lk, nil
		}
		if i == 1 {
			fmt.Fprintln(os.Stdout, "GooGet lock already held, waiting...")
		}
		time.Sleep(5 * time.Second)
	}
	return nil, errors.New("timed out waiting for lock")
}

func readConf(cf string) {
	gc, err := unmarshalConfFile(cf)
	if err != nil {
		if os.IsNotExist(err) {
			gc = &conf{}
		} else {
			logger.Errorf("Error unmarshalling conf file: %v", err)
		}
	}

	if gc.Archs != nil {
		archs = gc.Archs
	} else {
		archs, err = system.InstallableArchs()
		if err != nil {
			logger.Fatal(err)
		}
	}

	if gc.CacheLife != "" {
		cacheLife, err = time.ParseDuration(gc.CacheLife)
		if err != nil {
			logger.Error(err)
		}
	}

	if gc.ProxyServer != "" {
		proxyServer = gc.ProxyServer
	}

	allowUnsafeURL = gc.AllowUnsafeURL
}

func run() int {
	ggFlags := flag.NewFlagSet(filepath.Base(os.Args[0]), flag.ContinueOnError)
	ggFlags.StringVar(&rootDir, "root", os.Getenv(envVar), "googet root directory")
	ggFlags.BoolVar(&noConfirm, "noconfirm", false, "skip confirmation")
	ggFlags.BoolVar(&verbose, "verbose", false, "print info level logs to stdout")
	ggFlags.BoolVar(&systemLog, "system_log", true, "log to Linux Syslog or Windows Event Log")
	ggFlags.BoolVar(&showVer, "version", false, "display GooGet version and exit")

	if err := ggFlags.Parse(os.Args[1:]); err != nil && err != flag.ErrHelp {
		logger.Fatal(err)
	}

	if showVer {
		fmt.Println("GooGet version:", version)
		os.Exit(0)
	}

	cmdr := subcommands.NewCommander(ggFlags, "googet")
	cmdr.Register(cmdr.FlagsCommand(), "")
	cmdr.Register(cmdr.CommandsCommand(), "")
	cmdr.Register(cmdr.HelpCommand(), "")
	cmdr.Register(&installCmd{}, "package management")
	cmdr.Register(&downloadCmd{}, "package management")
	cmdr.Register(&removeCmd{}, "package management")
	cmdr.Register(&updateCmd{}, "package management")
	cmdr.Register(&installedCmd{}, "package query")
	cmdr.Register(&latestCmd{}, "package query")
	cmdr.Register(&availableCmd{}, "package query")
	cmdr.Register(&listReposCmd{}, "repository management")
	cmdr.Register(&addRepoCmd{}, "repository management")
	cmdr.Register(&rmRepoCmd{}, "repository management")
	cmdr.Register(&cleanCmd{}, "")

	cmdr.ImportantFlag("verbose")
	cmdr.ImportantFlag("noconfirm")

	nonLockingCommands := []string{"help", "commands", "flags"}
	if ggFlags.NArg() == 0 || goolib.ContainsString(ggFlags.Args()[0], nonLockingCommands) {
		return int(cmdr.Execute(context.Background()))
	}

	if rootDir == "" {
		logger.Fatalf("The environment variable %q not defined and no '-root' flag passed.", envVar)
	}
	if err := os.MkdirAll(rootDir, 0774); err != nil {
		logger.Fatalln("Error setting up root directory:", err)
	}

	readConf(filepath.Join(rootDir, confFile))

	lkf := filepath.Join(rootDir, lockFile)
	lk, err := lock(lkf)
	if err != nil {
		logger.Fatal(err)
	}
	defer os.Remove(lkf)
	defer lk.Close()

	logPath := filepath.Join(rootDir, logFile)
	if err := rotateLog(logPath, logSize); err != nil {
		logger.Error(err)
	}
	lf, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660)
	if err != nil {
		logger.Fatalln("Failed to open log file:", err)
	}
	defer lf.Close()

	logger.Init("GooGet", verbose, systemLog, lf)

	if err := os.MkdirAll(filepath.Join(rootDir, cacheDir), 0774); err != nil {
		logger.Fatalf("Error setting up cache directory: %v", err)
	}
	if err := os.MkdirAll(filepath.Join(rootDir, repoDir), 0774); err != nil {
		logger.Fatalf("Error setting up repo directory: %v", err)
	}

	return int(cmdr.Execute(context.Background()))
}

func main() {
	os.Exit(run())
}
<commit_msg>Use correct case for 'allowunsafeurl' in error (#34)<commit_after>/*
Copyright 2016 Google Inc. All Rights Reserved.
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.
*/

// The googet binary is the client for the GoGet packaging system, it performs the listing,
// getting, installing and removing functions on client machines.
package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/go-yaml/yaml"
	"github.com/google/googet/client"
	"github.com/google/googet/goolib"
	"github.com/google/googet/system"
	"github.com/google/logger"
	"github.com/google/subcommands"
	"github.com/olekukonko/tablewriter"
)

const (
	stateFile = "googet.state"
	confFile  = "googet.conf"
	logFile   = "googet.log"
	lockFile  = "googet.lock"
	cacheDir  = "cache"
	repoDir   = "repos"
	envVar    = "GooGetRoot"
	logSize   = 10 * 1024 * 1024
)

var (
	rootDir        string
	noConfirm      bool
	verbose        bool
	systemLog      bool
	showVer        bool
	version        string
	cacheLife      = 3 * time.Minute
	archs          []string
	proxyServer    string
	allowUnsafeURL bool
)

type packageMap map[string]string

// installedPackages returns a packagemap of all installed packages based on the
// googet state file given.
func installedPackages(state client.GooGetState) packageMap {
	pm := make(packageMap)
	for _, p := range state {
		pm[p.PackageSpec.Name+"."+p.PackageSpec.Arch] = p.PackageSpec.Version
	}
	return pm
}

type repoFile struct {
	fileName    string
	repoEntries []repoEntry
}

type repoEntry struct {
	LName string `yaml:"name,omitempty"`
	LURL  string `yaml:"url,omitempty"`
	Name  string `yaml:"Name,omitempty"`
	URL   string `yaml:"URL,omitempty"`
}

func writeRepoFile(rf repoFile) error {
	d, err := yaml.Marshal(rf.repoEntries)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(rf.fileName, d, 0664)
}

func unmarshalRepoFile(p string) (repoFile, error) {
	b, err := ioutil.ReadFile(p)
	if err != nil {
		return repoFile{}, err
	}

	// Don't try to unmarshal files with no YAML content
	var yml bool
	lns := strings.Split(string(b), "\n")
	for _, ln := range lns {
		ln = strings.TrimSpace(ln)
		if !strings.HasPrefix(ln, "#") && ln != "" {
			yml = true
			break
		}
	}
	if !yml {
		return repoFile{}, nil
	}

	// Both repoFile and []repoFile are valid for backwards compatibility.
	var re repoEntry
	if err := yaml.Unmarshal(b, &re); err == nil && (re.URL != "" || re.LURL != "") {
		return repoFile{fileName: p, repoEntries: []repoEntry{re}}, nil
	}

	var res []repoEntry
	if err := yaml.Unmarshal(b, &res); err != nil {
		return repoFile{}, err
	}
	return repoFile{fileName: p, repoEntries: res}, nil
}

type conf struct {
	Archs          []string
	CacheLife      string
	ProxyServer    string
	AllowUnsafeURL bool
}

func unmarshalConfFile(p string) (*conf, error) {
	b, err := ioutil.ReadFile(p)
	if err != nil {
		return nil, err
	}
	var cf conf
	return &cf, yaml.Unmarshal(b, &cf)
}

func repoList(dir string) ([]string, error) {
	rfs, err := repos(dir)
	if err != nil {
		return nil, err
	}
	var rl []string
	for _, rf := range rfs {
		for _, re := range rf.repoEntries {
			switch {
			case re.URL != "":
				rl = append(rl, re.URL)
			case re.LURL != "":
				rl = append(rl, re.LURL)
			}
		}
	}

	if !allowUnsafeURL {
		var srl []string
		for _, r := range rl {
			if strings.ToLower(r[0:5]) != "https" {
				logger.Errorf("%s will not be used as a repository, only https endpoints will be used unless 'allowunsafeurl' is set to 'true' in googet.conf", r)
				continue
			}
			srl = append(srl, r)
		}
		return srl, nil
	}
	return rl, nil
}

func repos(dir string) ([]repoFile, error) {
	fl, err := filepath.Glob(filepath.Join(dir, "*.repo"))
	if err != nil {
		return nil, err
	}
	var rfs []repoFile
	for _, f := range fl {
		rf, err := unmarshalRepoFile(f)
		if err != nil {
			logger.Error(err)
			continue
		}
		if rf.fileName != "" {
			rfs = append(rfs, rf)
		}
	}
	return rfs, nil
}

func writeState(s *client.GooGetState, sf string) error {
	b, err := s.Marshal()
	if err != nil {
		return err
	}
	return ioutil.WriteFile(sf, b, 0664)
}

func readState(sf string) (*client.GooGetState, error) {
	b, err := ioutil.ReadFile(sf)
	if os.IsNotExist(err) {
		logger.Info("No state file found, assuming no packages installed.")
		return &client.GooGetState{}, nil
	}
	if err != nil {
		return nil, err
	}
	return client.UnmarshalState(b)
}

func buildSources(s string) ([]string, error) {
	if s != "" {
		srcs := strings.Split(s, ",")
		return srcs, nil
	}
	return repoList(filepath.Join(rootDir, repoDir))
}

func confirmation(msg string) bool {
	var c string
	fmt.Print(msg + " (y/N): ")
	fmt.Scanln(&c)
	c = strings.ToLower(c)
	return c == "y" || c == "yes"
}

func info(ps *goolib.PkgSpec, r string) {
	fmt.Println()

	pkgInfo := []struct {
		name, value string
	}{
		{"Name", ps.Name},
		{"Arch", ps.Arch},
		{"Version", ps.Version},
		{"Repo", path.Base(r)},
		{"Authors", ps.Authors},
		{"Owners", ps.Owners},
		{"Description", ps.Description},
		{"Dependencies", ""},
		{"ReleaseNotes", ""},
	}
	var w int
	for _, pi := range pkgInfo {
		if len(pi.name) > w {
			w = len(pi.name)
		}
	}
	wf := fmt.Sprintf("%%-%vs: %%s\n", w+1)

	for _, pi := range pkgInfo {
		if pi.name == "Dependencies" {
			var deps []string
			for p, v := range ps.PkgDependencies {
				deps = append(deps, p+" "+v)
			}
			if len(deps) == 0 {
				fmt.Printf(wf, pi.name, "None")
			} else {
				fmt.Printf(wf, pi.name, deps[0])
				for _, l := range deps[1:] {
					fmt.Printf(wf, "", l)
				}
			}
		} else if pi.name == "ReleaseNotes" && ps.ReleaseNotes != nil {
			sl, _ := tablewriter.WrapString(ps.ReleaseNotes[0], 76-w)
			fmt.Printf(wf, pi.name, sl[0])
			for _, l := range sl[1:] {
				fmt.Printf(wf, "", l)
			}
			for _, l := range ps.ReleaseNotes[1:] {
				sl, _ := tablewriter.WrapString(l, 76-w)
				fmt.Printf(wf, "", sl[0])
				for _, l := range sl[1:] {
					fmt.Printf(wf, "", l)
				}
			}
		} else {
			cl := strings.Split(strings.TrimSpace(pi.value), "\n")
			sl, _ := tablewriter.WrapString(cl[0], 76-w)
			fmt.Printf(wf, pi.name, sl[0])
			for _, l := range sl[1:] {
				fmt.Printf(wf, "", l)
			}
			for _, l := range cl[1:] {
				sl, _ := tablewriter.WrapString(l, 76-w)
				for _, l := range sl {
					fmt.Printf(wf, "", l)
				}
			}
		}
	}
}

func rotateLog(logPath string, ls int64) error {
	fi, err := os.Stat(logPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	if fi.Size() < ls {
		return nil
	}
	oldLog := logPath + ".old"
	if err := os.Rename(logPath, oldLog); err != nil {
		return fmt.Errorf("error moving log file: %v", err)
	}
	return nil
}

func lock(lf string) (*os.File, error) {
	// This locking process only works on Windows, on linux os.Remove will remove an open file.
	// This is not currently an issue as running googet on linux is only done for testing.
	// In the future using a semaphore for locking would be nice.
	// 90% of all GooGet runs happen in < 60s, we wait 70s.
	for i := 1; i < 15; i++ {
		// Try to remove any old lock file that may exist, ignore errors as we don't care if
		// we can't remove it or it does not exist.
		os.Remove(lf)
		if lk, err := os.OpenFile(lf, os.O_RDONLY|os.O_CREATE|os.O_EXCL, 0); err == nil {
			return lk, nil
		}
		if i == 1 {
			fmt.Fprintln(os.Stdout, "GooGet lock already held, waiting...")
		}
		time.Sleep(5 * time.Second)
	}
	return nil, errors.New("timed out waiting for lock")
}

func readConf(cf string) {
	gc, err := unmarshalConfFile(cf)
	if err != nil {
		if os.IsNotExist(err) {
			gc = &conf{}
		} else {
			logger.Errorf("Error unmarshalling conf file: %v", err)
		}
	}

	if gc.Archs != nil {
		archs = gc.Archs
	} else {
		archs, err = system.InstallableArchs()
		if err != nil {
			logger.Fatal(err)
		}
	}

	if gc.CacheLife != "" {
		cacheLife, err = time.ParseDuration(gc.CacheLife)
		if err != nil {
			logger.Error(err)
		}
	}

	if gc.ProxyServer != "" {
		proxyServer = gc.ProxyServer
	}

	allowUnsafeURL = gc.AllowUnsafeURL
}

func run() int {
	ggFlags := flag.NewFlagSet(filepath.Base(os.Args[0]), flag.ContinueOnError)
	ggFlags.StringVar(&rootDir, "root", os.Getenv(envVar), "googet root directory")
	ggFlags.BoolVar(&noConfirm, "noconfirm", false, "skip confirmation")
	ggFlags.BoolVar(&verbose, "verbose", false, "print info level logs to stdout")
	ggFlags.BoolVar(&systemLog, "system_log", true, "log to Linux Syslog or Windows Event Log")
	ggFlags.BoolVar(&showVer, "version", false, "display GooGet version and exit")

	if err := ggFlags.Parse(os.Args[1:]); err != nil && err != flag.ErrHelp {
		logger.Fatal(err)
	}

	if showVer {
		fmt.Println("GooGet version:", version)
		os.Exit(0)
	}

	cmdr := subcommands.NewCommander(ggFlags, "googet")
	cmdr.Register(cmdr.FlagsCommand(), "")
	cmdr.Register(cmdr.CommandsCommand(), "")
	cmdr.Register(cmdr.HelpCommand(), "")
	cmdr.Register(&installCmd{}, "package management")
	cmdr.Register(&downloadCmd{}, "package management")
	cmdr.Register(&removeCmd{}, "package management")
	cmdr.Register(&updateCmd{}, "package management")
	cmdr.Register(&installedCmd{}, "package query")
	cmdr.Register(&latestCmd{}, "package query")
	cmdr.Register(&availableCmd{}, "package query")
	cmdr.Register(&listReposCmd{}, "repository management")
	cmdr.Register(&addRepoCmd{}, "repository management")
	cmdr.Register(&rmRepoCmd{}, "repository management")
	cmdr.Register(&cleanCmd{}, "")

	cmdr.ImportantFlag("verbose")
	cmdr.ImportantFlag("noconfirm")

	nonLockingCommands := []string{"help", "commands", "flags"}
	if ggFlags.NArg() == 0 || goolib.ContainsString(ggFlags.Args()[0], nonLockingCommands) {
		return int(cmdr.Execute(context.Background()))
	}

	if rootDir == "" {
		logger.Fatalf("The environment variable %q not defined and no '-root' flag passed.", envVar)
	}
	if err := os.MkdirAll(rootDir, 0774); err != nil {
		logger.Fatalln("Error setting up root directory:", err)
	}

	readConf(filepath.Join(rootDir, confFile))

	lkf := filepath.Join(rootDir, lockFile)
	lk, err := lock(lkf)
	if err != nil {
		logger.Fatal(err)
	}
	defer os.Remove(lkf)
	defer lk.Close()

	logPath := filepath.Join(rootDir, logFile)
	if err := rotateLog(logPath, logSize); err != nil {
		logger.Error(err)
	}
	lf, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660)
	if err != nil {
		logger.Fatalln("Failed to open log file:", err)
	}
	defer lf.Close()

	logger.Init("GooGet", verbose, systemLog, lf)

	if err := os.MkdirAll(filepath.Join(rootDir, cacheDir), 0774); err != nil {
		logger.Fatalf("Error setting up cache directory: %v", err)
	}
	if err := os.MkdirAll(filepath.Join(rootDir, repoDir), 0774); err != nil {
		logger.Fatalf("Error setting up repo directory: %v", err)
	}

	return int(cmdr.Execute(context.Background()))
}

func main() {
	os.Exit(run())
}
<|endoftext|>