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

package util

import (
	"bytes"
	"log"
	"net"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/gogo/protobuf/types"

	"istio.io/istio/pilot/pkg/bootstrap"
	"istio.io/istio/pilot/pkg/proxy/envoy"
	"istio.io/istio/pilot/pkg/serviceregistry"
)

var (
	// MockTestServer is used for the unit tests. Will be started once, terminated at the
	// end of the suite.
	MockTestServer *bootstrap.Server

	// MockPilotURL is the URL for the pilot http endpoint
	MockPilotURL string

	// MockPilotGrpcAddr is the address to be used for grpc connections.
	MockPilotGrpcAddr string

	// MockPilotSecureAddr is the address to be used for secure grpc connections.
	MockPilotSecureAddr string

	// MockPilotSecurePort is the secure port
	MockPilotSecurePort int

	// MockPilotHTTPPort is the dynamic port for pilot http
	MockPilotHTTPPort int

	// MockPilotGrpcPort is the dynamic port for pilot grpc
	MockPilotGrpcPort int

	fsRoot string
	stop   chan struct{}
)

var (
	// IstioTop has the top of the istio tree, matches the env variable from make.
	IstioTop = os.Getenv("TOP")

	// IstioSrc is the location if istio source ($TOP/src/istio.io/istio
	IstioSrc = os.Getenv("ISTIO_GO")

	// IstioBin is the location of the binary output directory
	IstioBin = os.Getenv("ISTIO_BIN")

	// IstioOut is the location of the output directory ($TOP/out)
	IstioOut = os.Getenv("ISTIO_OUT")

	// EnvoyOutWriter captures envoy output
	// Redirect out and err from envoy to buffer - coverage tests get confused if we write to out.
	// TODO: use files
	EnvoyOutWriter bytes.Buffer

	// EnvoyErrWriter captures envoy errors
	EnvoyErrWriter bytes.Buffer
)

func init() {
	if IstioTop == "" {
		// Assume it is run inside istio.io/istio
		current, _ := os.Getwd()
		idx := strings.Index(current, "/src/istio.io/istio")
		if idx > 0 {
			IstioTop = current[0:idx]
		} else {
			IstioTop = current // launching from GOTOP (for example in goland)
		}
	}
	if IstioSrc == "" {
		IstioSrc = IstioTop + "/src/istio.io/istio"
	}
	if IstioOut == "" {
		IstioOut = IstioTop + "/out"
	}
	if IstioBin == "" {
		IstioBin = IstioTop + "/out/" + runtime.GOOS + "_" + runtime.GOARCH + "/release"
	}
}

// EnsureTestServer will ensure a pilot server is running in process and initializes
// the MockPilotUrl and MockPilotGrpcAddr to allow connections to the test pilot.
func EnsureTestServer() *bootstrap.Server {
	if MockTestServer == nil {
		err := setup()
		if err != nil {
			log.Fatal("Failed to start in-process server", err)
		}
	}
	return MockTestServer
}

func setup() error {
	// TODO: point to test data directory
	// Setting FileDir (--configDir) disables k8s client initialization, including for registries,
	// and uses a 100ms scan. Must be used with the mock registry (or one of the others)
	// This limits the options -
	stop = make(chan struct{})

	// When debugging a test or running locally it helps having a static port for /debug
	// "0" is used on shared environment (it's not actually clear if such thing exists since
	// we run the tests in isolated VMs)
	pilotHTTP := os.Getenv("PILOT_HTTP")
	if len(pilotHTTP) == 0 {
		pilotHTTP = "0"
	}
	httpAddr := ":" + pilotHTTP

	// Create a test pilot discovery service configured to watch the tempDir.
	args := bootstrap.PilotArgs{
		Namespace: "testing",
		DiscoveryOptions: envoy.DiscoveryServiceOptions{
			HTTPAddr:        httpAddr,
			GrpcAddr:        ":0",
			SecureGrpcAddr:  ":0",
			EnableCaching:   true,
			EnableProfiling: true,
		},
		//TODO: start mixer first, get its address
		Mesh: bootstrap.MeshArgs{
			MixerAddress:    "istio-mixer.istio-system:9091",
			RdsRefreshDelay: types.DurationProto(10 * time.Millisecond),
		},
		Config: bootstrap.ConfigArgs{
			KubeConfig: IstioSrc + "/.circleci/config",
		},
		Service: bootstrap.ServiceArgs{
			// Using the Mock service registry, which provides the hello and world services.
			Registries: []string{
				string(serviceregistry.MockRegistry)},
		},
	}
	// Static testdata, should include all configs we want to test.
	args.Config.FileDir = IstioSrc + "/tests/testdata/config"

	bootstrap.PilotCertDir = IstioSrc + "/tests/testdata/certs/pilot"

	// Create and setup the controller.
	s, err := bootstrap.NewServer(args)
	if err != nil {
		return err
	}

	MockTestServer = s

	// Start the server.
	if err := s.Start(stop); err != nil {
		return err
	}

	// Extract the port from the network address.
	_, port, err := net.SplitHostPort(s.HTTPListeningAddr.String())
	if err != nil {
		return err
	}
	MockPilotURL = "http://localhost:" + port
	MockPilotHTTPPort, _ = strconv.Atoi(port)

	_, port, err = net.SplitHostPort(s.GRPCListeningAddr.String())
	if err != nil {
		return err
	}
	MockPilotGrpcAddr = "localhost:" + port
	MockPilotGrpcPort, _ = strconv.Atoi(port)

	_, port, err = net.SplitHostPort(s.SecureGRPCListeningAddr.String())
	if err != nil {
		return err
	}
	MockPilotSecureAddr = "localhost:" + port
	MockPilotSecurePort, _ = strconv.Atoi(port)

	// Wait a bit for the server to come up.
	// TODO(nmittler): Change to polling health endpoint once https://github.com/istio/istio/pull/2002 lands.
	time.Sleep(time.Second)

	return nil
}

// Teardown will cleanup the temp dir and remove the test data.
func Teardown() {
	close(stop)

	// Remove the temp dir.
	_ = os.RemoveAll(fsRoot)
}
<commit_msg>poll /ready endpoint to wait for pilot server come up (#7974)<commit_after>// Copyright 2017 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package util

import (
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/gogo/protobuf/types"
	"k8s.io/apimachinery/pkg/util/wait"

	"istio.io/istio/pilot/pkg/bootstrap"
	"istio.io/istio/pilot/pkg/proxy/envoy"
	"istio.io/istio/pilot/pkg/serviceregistry"
)

var (
	// MockTestServer is used for the unit tests. Will be started once, terminated at the
	// end of the suite.
	MockTestServer *bootstrap.Server

	// MockPilotURL is the URL for the pilot http endpoint
	MockPilotURL string

	// MockPilotGrpcAddr is the address to be used for grpc connections.
	MockPilotGrpcAddr string

	// MockPilotSecureAddr is the address to be used for secure grpc connections.
	MockPilotSecureAddr string

	// MockPilotSecurePort is the secure port
	MockPilotSecurePort int

	// MockPilotHTTPPort is the dynamic port for pilot http
	MockPilotHTTPPort int

	// MockPilotGrpcPort is the dynamic port for pilot grpc
	MockPilotGrpcPort int

	stop chan struct{}
)

var (
	// IstioTop has the top of the istio tree, matches the env variable from make.
	IstioTop = os.Getenv("TOP")

	// IstioSrc is the location if istio source ($TOP/src/istio.io/istio
	IstioSrc = os.Getenv("ISTIO_GO")

	// IstioBin is the location of the binary output directory
	IstioBin = os.Getenv("ISTIO_BIN")

	// IstioOut is the location of the output directory ($TOP/out)
	IstioOut = os.Getenv("ISTIO_OUT")
)

func init() {
	if IstioTop == "" {
		// Assume it is run inside istio.io/istio
		current, _ := os.Getwd()
		idx := strings.Index(current, "/src/istio.io/istio")
		if idx > 0 {
			IstioTop = current[0:idx]
		} else {
			IstioTop = current // launching from GOTOP (for example in goland)
		}
	}
	if IstioSrc == "" {
		IstioSrc = IstioTop + "/src/istio.io/istio"
	}
	if IstioOut == "" {
		IstioOut = IstioTop + "/out"
	}
	if IstioBin == "" {
		IstioBin = IstioTop + "/out/" + runtime.GOOS + "_" + runtime.GOARCH + "/release"
	}
}

// EnsureTestServer will ensure a pilot server is running in process and initializes
// the MockPilotUrl and MockPilotGrpcAddr to allow connections to the test pilot.
func EnsureTestServer() *bootstrap.Server {
	if MockTestServer == nil {
		err := setup()
		if err != nil {
			log.Fatal("Failed to start in-process server", err)
		}
	}
	return MockTestServer
}

func setup() error {
	// TODO: point to test data directory
	// Setting FileDir (--configDir) disables k8s client initialization, including for registries,
	// and uses a 100ms scan. Must be used with the mock registry (or one of the others)
	// This limits the options -
	stop = make(chan struct{})

	// When debugging a test or running locally it helps having a static port for /debug
	// "0" is used on shared environment (it's not actually clear if such thing exists since
	// we run the tests in isolated VMs)
	pilotHTTP := os.Getenv("PILOT_HTTP")
	if len(pilotHTTP) == 0 {
		pilotHTTP = "0"
	}
	httpAddr := ":" + pilotHTTP

	// Create a test pilot discovery service configured to watch the tempDir.
	args := bootstrap.PilotArgs{
		Namespace: "testing",
		DiscoveryOptions: envoy.DiscoveryServiceOptions{
			HTTPAddr:        httpAddr,
			GrpcAddr:        ":0",
			SecureGrpcAddr:  ":0",
			EnableCaching:   true,
			EnableProfiling: true,
		},
		//TODO: start mixer first, get its address
		Mesh: bootstrap.MeshArgs{
			MixerAddress:    "istio-mixer.istio-system:9091",
			RdsRefreshDelay: types.DurationProto(10 * time.Millisecond),
		},
		Config: bootstrap.ConfigArgs{
			KubeConfig: IstioSrc + "/.circleci/config",
		},
		Service: bootstrap.ServiceArgs{
			// Using the Mock service registry, which provides the hello and world services.
			Registries: []string{
				string(serviceregistry.MockRegistry)},
		},
	}
	// Static testdata, should include all configs we want to test.
	args.Config.FileDir = IstioSrc + "/tests/testdata/config"

	bootstrap.PilotCertDir = IstioSrc + "/tests/testdata/certs/pilot"

	// Create and setup the controller.
	s, err := bootstrap.NewServer(args)
	if err != nil {
		return err
	}

	MockTestServer = s

	// Start the server.
	if err := s.Start(stop); err != nil {
		return err
	}

	// Extract the port from the network address.
	_, port, err := net.SplitHostPort(s.HTTPListeningAddr.String())
	if err != nil {
		return err
	}
	MockPilotURL = "http://localhost:" + port
	MockPilotHTTPPort, _ = strconv.Atoi(port)

	_, port, err = net.SplitHostPort(s.GRPCListeningAddr.String())
	if err != nil {
		return err
	}
	MockPilotGrpcAddr = "localhost:" + port
	MockPilotGrpcPort, _ = strconv.Atoi(port)

	_, port, err = net.SplitHostPort(s.SecureGRPCListeningAddr.String())
	if err != nil {
		return err
	}
	MockPilotSecureAddr = "localhost:" + port
	MockPilotSecurePort, _ = strconv.Atoi(port)

	// Wait a bit for the server to come up.
	err = wait.Poll(500*time.Millisecond, 5*time.Second, func() (bool, error) {
		client := &http.Client{Timeout: 1 * time.Second}
		resp, err := client.Get(MockPilotURL + "/ready")
		if err != nil {
			return false, nil
		}
		defer resp.Body.Close()
		ioutil.ReadAll(resp.Body)
		if resp.StatusCode == http.StatusOK {
			return true, nil
		}
		return false, nil
	})

	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	. "github.com/minhchuduc/hwinfo-lib/cpuinfo"
	. "github.com/minhchuduc/hwinfo-lib/mbinfo"
)

func main() {
	fmt.Println(Cpu)
	fmt.Println(Motherboard)
}
<commit_msg>Fix path because at the last moment, I decided to push to PiScale.<commit_after>package main

import (
	"fmt"
	. "github.com/PiScale/hwinfo-lib/cpuinfo"
	. "github.com/PiScale/hwinfo-lib/mbinfo"
)

func main() {
	fmt.Println(Cpu)
	fmt.Println(Motherboard)
}
<|endoftext|>


<commit_before>package main

import "fmt"
import "flag"
import "io/ioutil"
import "path"
import "encoding/json"
import "strings"
import "os"
import "os/exec"

import "github.com/termie/go-shutil"

var (
	registry_path = ""
	delete_path   = ""
	dry_run       = false
)

var used_images []string

type ancestry []string

type StringSet struct {
	set map[string]bool
}

type index_images []index_images_item

type index_images_item struct {
	Id string `json: "id"`
}

func main() {

	flag.Parse()

	fmt.Printf("%v\n", registry_path)
	fmt.Printf("%v\n", delete_path)

	initDeletePath()

	var image_list = getAllImageIds(registry_path)
	var image_names = getAllRepositories(registry_path)
	var used_images = getUsedImages(image_names)
	var all_used_images = NewSet()
	for _, i := range used_images {
		ids := getImageAncestry(i)
		for _, id := range ids {
			all_used_images.Add(id)
		}
	}

	var unused_images []string
	for _, i := range image_list {
		_, found := all_used_images.set[i]
		if !found {
			fmt.Printf("Unused image: %v\n", i)
			unused_images = append(unused_images, i)
			moveImage(i)
		}
	}

	for _, name := range image_names {
		_ = updateIndexImages(name, unused_images[0])
	}
	//	fmt.Println(all_images.Keys())
	fmt.Println(image_names)
	getUnusedSize()

}

func p(msg ...string) {
	if dry_run {
		fmt.Print("DRY RUN: ")
	}
	fmt.Println(strings.Join(msg, " "))
}

func init() {

	flag.StringVar(&registry_path, "registry-path", "/var/lib/docker-registry", "Path where your images and metadata are stored")
	flag.StringVar(&delete_path, "delete-path", "/var/lib/docker-registry-delete", "Path where deleted images and metadata will be stored")
	flag.BoolVar(&dry_run, "dry-run", false, "Don't perform any destructive changes on disk")

}

func initDeletePath() bool {

	deleted_image_path := path.Join(delete_path, "images")
	dp, err := os.Stat(deleted_image_path)
	if err != nil {
		err = os.MkdirAll(deleted_image_path, 0755)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		} else {
			fmt.Println("Created", delete_path)
		}

	} else if dp != nil && !dp.IsDir() {
		fmt.Println("Path", delete_path, "exists and is not a directory")
		os.Exit(1)
	}

	return true
}

func NewSet() *StringSet {
	return &StringSet{make(map[string]bool)}
}

func (set *StringSet) Add(i string) bool {
	_, found := set.set[i]
	set.set[i] = true
	return !found //False if it existed already
}

func (set *StringSet) Keys() []string {
	keys := make([]string, len(set.set))
	for key := range set.set {
		keys = append(keys, key)
	}

	return keys
}

func getAllRepositories(registry_path string) (image_names []string) {
	repo_dir := path.Join(registry_path, "repositories")
	dirs, _ := ioutil.ReadDir(repo_dir)
	for _, repository := range dirs {
		images, _ := ioutil.ReadDir(path.Join(repo_dir, repository.Name()))
		for _, name := range images {
			image_names = append(image_names, path.Join(repository.Name(), name.Name()))
		}
	}

	return
}

func getUsedImages(image_names []string) (used_images []string) {
	for _, image := range image_names {
		tags, _ := ioutil.ReadDir(path.Join(registry_path, "repositories", image))
		for _, tag := range tags {
			if strings.HasPrefix(tag.Name(), "tag_") {
				data, _ := ioutil.ReadFile(path.Join(registry_path, "repositories", image, tag.Name()))
				used_images = append(used_images, string(data))
			}
		}
	}

	return
}

func getAllImageIds(registry_path string) (images []string) {
	//fmt.Println(registry_path)

	files, _ := ioutil.ReadDir(path.Join(registry_path, "images"))
	for _, f := range files {
		//       fmt.Printf("Visited: %s\n", f.Name())
		images = append(images, f.Name())
	}

	return
}

func getImageAncestry(image_path string) (ids []string) {
	data, _ := ioutil.ReadFile(path.Join(registry_path, "images", image_path, "ancestry"))
	_ = json.Unmarshal(data, &ids)

	return
}

func moveImage(image_path string) bool {
	src := path.Join(registry_path, "images", image_path)
	dst := path.Join(delete_path, "images", image_path)

	err := shutil.CopyTree(src, dst, nil)
	if err != nil {
		fmt.Println("Failed to move", image_path, ":", err)
		return false
	}
	if !dry_run {
		if err := os.RemoveAll(src); err != nil {
			fmt.Println("Failed to remove", src, ":", err)
		}
	} else {
		p("Skipping removal of", src)
	}

	return true
}

func updateIndexImages(repository string, image string) bool {
	index_path := path.Join(registry_path, "repositories", repository, "_index_images")
	index_stat, _ := os.Stat(index_path)
	data, _ := ioutil.ReadFile(index_path)
	index := index_images{}
	_ = json.Unmarshal(data, &index)

	remove := -1
	for k, i := range index {
		if len(i.Id) > 0 {
			if i.Id == image {
				remove = k
				println(k, " ", i.Id)
				break
			}
		}
	}

	var new_index []byte
	if remove >= 0 {
		if remove < len(index)-1 {
			index = append(index[:remove], index[remove+1:]...)
		} else {
			index = index[:remove]
		}
		new_index, _ = json.Marshal(index)
		fmt.Println(string(new_index))

		index_bckp := path.Join(delete_path, "repositories", repository)
		err := os.MkdirAll(index_bckp, 0755)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		} else {
			fmt.Println("Created", index_bckp)
		}

		err = shutil.CopyFile(index_path, path.Join(index_bckp, "_index_images"), false)
		if err != nil {
			fmt.Println("Couldn't backup _index_images for", repository, ":", err)
			return false
		}
		if !dry_run {
			err := ioutil.WriteFile(index_path, new_index, index_stat.Mode())
			if err != nil {
				fmt.Println("Failed to write _index_images for", repository, ":", err)
				return false
			}
		} else {
			p("Skipping write of new _index_images for", repository)
		}
	}

	return true
}

func getUnusedSize() {
	fmt.Println(delete_path)
	cmd := fmt.Sprintf("du -hc %v", delete_path)
	out, _ := exec.Command("sh", "-c", cmd).Output()

	out_arr := strings.Split(string(out), "\n")
	fmt.Println(string(out_arr[len(out_arr)-2]))
}
<commit_msg>Add option to load unused images from the delete path to fix previous bug with _index_images not being updated properly<commit_after>package main

import "fmt"
import "flag"
import "io/ioutil"
import "path"
import "encoding/json"
import "strings"
import "os"
import "os/exec"

import "github.com/termie/go-shutil"

var (
	registry_path = ""
	delete_path   = ""
	dry_run       = false
	load_unused   = false
)

var used_images []string

type ancestry []string

type StringSet struct {
	set map[string]bool
}

type index_images []index_images_item

type index_images_item struct {
	Id string `json: "id"`
}

func main() {

	flag.Parse()

	fmt.Printf("%v\n", registry_path)
	fmt.Printf("%v\n", delete_path)

	initDeletePath()

	var image_list = getAllImageIds(registry_path)
	var image_names = getAllRepositories(registry_path)
	var used_images = getUsedImages(image_names)
	var all_used_images = NewSet()
	for _, i := range used_images {
		ids := getImageAncestry(i)
		for _, id := range ids {
			all_used_images.Add(id)
		}
	}

	var unused_images []string
	for _, i := range image_list {
		_, found := all_used_images.set[i]
		if !found {
			fmt.Printf("Unused image: %v\n", i)
			unused_images = append(unused_images, i)
			moveImage(i)
		}
	}

	if len(unused_images) == 0 && load_unused {
		fmt.Println("Loading images from delete path")
		unused_images = getAllImageIds(delete_path)
	}

	if len(unused_images) > 0 {
		for _, name := range image_names {
			for _, image := range unused_images {
				_ = updateIndexImages(name, image)
			}
		}
	}
	//	fmt.Println(all_images.Keys())
	fmt.Println(image_names)
	getUnusedSize()

}

func p(msg ...string) {
	if dry_run {
		fmt.Print("DRY RUN: ")
	}
	fmt.Println(strings.Join(msg, " "))
}

func init() {

	flag.StringVar(&registry_path, "registry-path", "/var/lib/docker-registry", "Path where your images and metadata are stored")
	flag.StringVar(&delete_path, "delete-path", "/var/lib/docker-registry-delete", "Path where deleted images and metadata will be stored")

	flag.BoolVar(&load_unused, "load-from-delete", false, "Load unused images from delete path")
	flag.BoolVar(&dry_run, "dry-run", false, "Don't perform any destructive changes on disk")

}

func initDeletePath() bool {

	deleted_image_path := path.Join(delete_path, "images")
	dp, err := os.Stat(deleted_image_path)
	if err != nil {
		err = os.MkdirAll(deleted_image_path, 0755)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		} else {
			fmt.Println("Created", delete_path)
		}

	} else if dp != nil && !dp.IsDir() {
		fmt.Println("Path", delete_path, "exists and is not a directory")
		os.Exit(1)
	}

	return true
}

func NewSet() *StringSet {
	return &StringSet{make(map[string]bool)}
}

func (set *StringSet) Add(i string) bool {
	_, found := set.set[i]
	set.set[i] = true
	return !found //False if it existed already
}

func (set *StringSet) Keys() []string {
	keys := make([]string, len(set.set))
	for key := range set.set {
		keys = append(keys, key)
	}

	return keys
}

func getAllRepositories(registry_path string) (image_names []string) {
	repo_dir := path.Join(registry_path, "repositories")
	dirs, _ := ioutil.ReadDir(repo_dir)
	for _, repository := range dirs {
		images, _ := ioutil.ReadDir(path.Join(repo_dir, repository.Name()))
		for _, name := range images {
			image_names = append(image_names, path.Join(repository.Name(), name.Name()))
		}
	}

	return
}

func getUsedImages(image_names []string) (used_images []string) {
	for _, image := range image_names {
		tags, _ := ioutil.ReadDir(path.Join(registry_path, "repositories", image))
		for _, tag := range tags {
			if strings.HasPrefix(tag.Name(), "tag_") {
				data, _ := ioutil.ReadFile(path.Join(registry_path, "repositories", image, tag.Name()))
				used_images = append(used_images, string(data))
			}
		}
	}

	return
}

func getAllImageIds(registry_path string) (images []string) {
	//fmt.Println(registry_path)

	files, _ := ioutil.ReadDir(path.Join(registry_path, "images"))
	for _, f := range files {
		//       fmt.Printf("Visited: %s\n", f.Name())
		images = append(images, f.Name())
	}

	return
}

func getImageAncestry(image_path string) (ids []string) {
	data, _ := ioutil.ReadFile(path.Join(registry_path, "images", image_path, "ancestry"))
	_ = json.Unmarshal(data, &ids)

	return
}

func moveImage(image_path string) bool {
	src := path.Join(registry_path, "images", image_path)
	dst := path.Join(delete_path, "images", image_path)

	err := shutil.CopyTree(src, dst, nil)
	if err != nil {
		fmt.Println("Failed to move", image_path, ":", err)
		return false
	}
	if !dry_run {
		if err := os.RemoveAll(src); err != nil {
			fmt.Println("Failed to remove", src, ":", err)
		}
	} else {
		p("Skipping removal of", src)
	}

	return true
}

func updateIndexImages(repository string, image string) bool {
	index_path := path.Join(registry_path, "repositories", repository, "_index_images")
	index_stat, _ := os.Stat(index_path)
	data, _ := ioutil.ReadFile(index_path)
	index := index_images{}
	_ = json.Unmarshal(data, &index)

	remove := -1
	for k, i := range index {
		if len(i.Id) > 0 {
			if i.Id == image {
				remove = k
				println(k, " ", i.Id)
				break
			}
		}
	}

	var new_index []byte
	if remove >= 0 {
		if remove < len(index)-1 {
			index = append(index[:remove], index[remove+1:]...)
		} else {
			index = index[:remove]
		}
		new_index, _ = json.Marshal(index)
		fmt.Println(string(new_index))

		index_bckp := path.Join(delete_path, "repositories", repository)
		err := os.MkdirAll(index_bckp, 0755)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		} else {
			fmt.Println("Created", index_bckp)
		}

		err = shutil.CopyFile(index_path, path.Join(index_bckp, "_index_images"), false)
		if err != nil {
			fmt.Println("Couldn't backup _index_images for", repository, ":", err)
			return false
		}
		if !dry_run {
			p("Writing _indec_image for", repository)
			err := ioutil.WriteFile(index_path, new_index, index_stat.Mode())
			if err != nil {
				fmt.Println("Failed to write _index_images for", repository, ":", err)
				return false
			}
		} else {
			p("Skipping write of new _index_images for", repository)
		}
	}

	return true
}

func getUnusedSize() {
	fmt.Println(delete_path)
	cmd := fmt.Sprintf("du -hc %v", delete_path)
	out, _ := exec.Command("sh", "-c", cmd).Output()

	out_arr := strings.Split(string(out), "\n")
	fmt.Println(string(out_arr[len(out_arr)-2]))
}
<|endoftext|>


<commit_before>package clui

import (
	clip "github.com/atotto/clipboard"
	xs "github.com/huandu/xstrings"
	term "github.com/nsf/termbox-go"
)

/*
EditField is a single-line text edit contol. Edit field consumes some keyboard
events when it is active: all printable charaters; Delete, BackSpace, Home,
End, left and right arrows; Ctrl+R to clear EditField.
Edit text can be limited. By default a user can enter text of any length.
Use SetMaxWidth to limit the maximum text length. If the text is longer than
maximun then the text is automatically truncated.
EditField calls onChage in case of its text is changed. Event field Msg contains the new text
*/
type EditField struct {
	ControlBase
	// cursor position in edit text
	cursorPos int
	// the number of the first displayed text character - it is used in case of text is longer than edit width
	offset   int
	readonly bool
	maxWidth int

	onChange   func(Event)
	onKeyPress func(term.Key) bool
}

// NewEditField creates a new EditField control
// view - is a View that manages the control
// parent - is container that keeps the control. The same View can be a view and a parent at the same time.
// width - is minimal width of the control.
// text - text to edit.
// scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
//  control should keep its original size.
func NewEditField(view View, parent Control, width int, text string, scale int) *EditField {
	e := new(EditField)
	e.onChange = nil
	e.SetTitle(text)
	e.SetEnabled(true)

	if width == AutoSize {
		width = xs.Len(text) + 1
	}

	e.SetSize(width, 1)
	e.cursorPos = xs.Len(text)
	e.offset = 0
	e.parent = parent
	e.view = view
	e.parent = parent
	e.readonly = false

	e.SetConstraints(width, 1)

	e.end()

	if parent != nil {
		parent.AddChild(e, scale)
	}

	return e
}

// OnChange sets the callback that is called when EditField content is changed
func (e *EditField) OnChange(fn func(Event)) {
	e.onChange = fn
}

// OnKeyPress sets the callback that is called when a user presses a Key while
// the controls is active. If a handler processes the key it should return
// true. If handler returns false it means that the default handler will
// process the key
func (e *EditField) OnKeyPress(fn func(term.Key) bool) {
	e.onKeyPress = fn
}

// SetTitle changes the EditField content and emits OnChage eventif the new value does not equal to old one
func (e *EditField) SetTitle(title string) {
	if e.title != title {
		e.title = title
		if e.onChange != nil {
			ev := Event{Msg: title, Sender: e}
			go e.onChange(ev)
		}
	}
}

// Repaint draws the control on its View surface
func (e *EditField) Repaint() {
	canvas := e.view.Canvas()

	x, y := e.Pos()
	w, _ := e.Size()

	tm := e.view.Screen().Theme()
	parts := []rune(tm.SysObject(ObjEdit))
	chLeft, chRight := string(parts[0]), string(parts[1])

	var textOut string
	curOff := 0
	if e.offset == 0 && xs.Len(e.title) < e.width {
		textOut = e.title
	} else {
		fromIdx := 0
		toIdx := 0
		if e.offset == 0 {
			toIdx = e.width - 1
			textOut = xs.Slice(e.title, 0, toIdx) + chRight
			curOff = -e.offset
		} else {
			curOff = 1 - e.offset
			fromIdx = e.offset
			if e.width-1 <= xs.Len(e.title)-e.offset {
				toIdx = e.offset + e.width - 2
				textOut = chLeft + xs.Slice(e.title, fromIdx, toIdx) + chRight
			} else {
				textOut = chLeft + xs.Slice(e.title, fromIdx, -1)
			}
		}
	}

	fg, bg := RealColor(tm, e.fg, ColorEditText), RealColor(tm, e.bg, ColorEditBack)
	if !e.Enabled() {
		fg, bg = RealColor(tm, e.fg, ColorDisabledText), RealColor(tm, e.fg, ColorDisabledBack)
	} else if e.Active() {
		fg, bg = RealColor(tm, e.fg, ColorEditActiveText), RealColor(tm, e.bg, ColorEditActiveBack)

	}

	canvas.FillRect(x, y, w, 1, term.Cell{Ch: ' ', Bg: bg})
	canvas.PutText(x, y, textOut, fg, bg)
	if e.active {
		wx, wy := e.view.Pos()
		canvas.SetCursorPos(e.cursorPos+curOff+wx+e.x, wy+e.y)
	}
}

func (e *EditField) insertRune(ch rune) {
	if e.readonly {
		return
	}

	if e.maxWidth > 0 && xs.Len(e.title) >= e.maxWidth {
		return
	}

	idx := e.cursorPos

	if idx == 0 {
		e.SetTitle(string(ch) + e.title)
	} else if idx >= xs.Len(e.title) {
		e.SetTitle(e.title + string(ch))
	} else {
		e.SetTitle(xs.Slice(e.title, 0, idx) + string(ch) + xs.Slice(e.title, idx, -1))
	}

	e.cursorPos++

	if e.cursorPos >= e.width {
		if e.offset == 0 {
			e.offset = 2
		} else {
			e.offset++
		}
	}
}

func (e *EditField) backspace() {
	if e.title == "" || e.cursorPos == 0 || e.readonly {
		return
	}

	length := xs.Len(e.title)
	if e.cursorPos >= length {
		e.cursorPos--
		e.SetTitle(xs.Slice(e.title, 0, length-1))
	} else if e.cursorPos == 1 {
		e.cursorPos = 0
		e.SetTitle(xs.Slice(e.title, 1, -1))
		e.offset = 0
	} else {
		e.cursorPos--
		e.SetTitle(xs.Slice(e.title, 0, e.cursorPos) + xs.Slice(e.title, e.cursorPos+1, -1))
	}

	if length-1 < e.width {
		e.offset = 0
	}
}

func (e *EditField) del() {
	length := xs.Len(e.title)

	if e.title == "" || e.cursorPos == length || e.readonly {
		return
	}

	if e.cursorPos == length-1 {
		e.SetTitle(xs.Slice(e.title, 0, length-1))
	} else {
		e.SetTitle(xs.Slice(e.title, 0, e.cursorPos) + xs.Slice(e.title, e.cursorPos+1, -1))
	}

	if length-1 < e.width {
		e.offset = 0
	}
}

func (e *EditField) charLeft() {
	if e.cursorPos == 0 || e.title == "" {
		return
	}

	if e.cursorPos == e.offset {
		e.offset--
	}

	e.cursorPos--
}

func (e *EditField) charRight() {
	length := xs.Len(e.title)
	if e.cursorPos == length || e.title == "" {
		return
	}

	e.cursorPos++
	if e.cursorPos != length && e.cursorPos >= e.offset+e.width-2 {
		e.offset++
	}
}

func (e *EditField) home() {
	e.offset = 0
	e.cursorPos = 0
}

func (e *EditField) end() {
	length := xs.Len(e.title)
	e.cursorPos = length

	if length < e.width {
		return
	}

	e.offset = length - (e.width - 2)
}

// Clear empties the EditField and emits OnChange event
func (e *EditField) Clear() {
	e.home()
	e.SetTitle("")
}

/*
ProcessEvent processes all events come from the control parent. If a control
processes an event it should return true. If the method returns false it means
that the control do not want or cannot process the event and the caller sends
the event to the control parent
*/
func (e *EditField) ProcessEvent(event Event) bool {
	if !e.Active() || !e.Enabled() {
		return false
	}

	if event.Type == EventActivate && event.X == 0 {
		term.HideCursor()
	}

	if event.Type == EventKey && event.Key != term.KeyTab {
		if e.onKeyPress != nil {
			res := e.onKeyPress(event.Key)
			if res {
				return true
			}
		}

		switch event.Key {
		case term.KeyEnter:
			return false
		case term.KeySpace:
			e.insertRune(' ')
			return true
		case term.KeyBackspace:
			e.backspace()
			return true
		case term.KeyDelete:
			e.del()
			return true
		case term.KeyArrowLeft:
			e.charLeft()
			return true
		case term.KeyHome:
			e.home()
			return true
		case term.KeyEnd:
			e.end()
			return true
		case term.KeyCtrlR:
			if !e.readonly {
				e.Clear()
			}
			return true
		case term.KeyArrowRight:
			e.charRight()
			return true
		case term.KeyCtrlC:
			clip.WriteAll(e.Title())
			return true
		case term.KeyCtrlV:
			s, _ := clip.ReadAll()
			e.SetTitle(s)
			return true
		default:
			if event.Ch != 0 {
				e.insertRune(event.Ch)
				return true
			}
		}
		return false
	}

	return false
}

// SetMaxWidth sets the maximum lenght of the EditField text. If the current text is longer it is truncated
func (e *EditField) SetMaxWidth(w int) {
	e.maxWidth = w
	if w > 0 && xs.Len(e.title) > w {
		e.title = xs.Slice(e.title, 0, w)
		e.end()
	}
}

// MaxWidth returns the current maximum text length. Zero means no limit
func (e *EditField) MaxWidth() int {
	return e.maxWidth
}

// SetSize changes control size. Constant DoNotChange can be
// used as placeholder to indicate that the control attrubute
// should be unchanged.
// Method does nothing if new size is less than minimal size
// EditField height cannot be changed - it equals 1 always
func (e *EditField) SetSize(width, height int) {
	if width != DoNotChange && (width > 1000 || width < e.minW) {
		return
	}
	if height != DoNotChange && (height > 200 || height < e.minH) {
		return
	}

	if width != DoNotChange {
		e.width = width
	}

	e.height = 1
}
<commit_msg>#42 - fix pasting text to EditField<commit_after>package clui

import (
	clip "github.com/atotto/clipboard"
	xs "github.com/huandu/xstrings"
	term "github.com/nsf/termbox-go"
)

/*
EditField is a single-line text edit contol. Edit field consumes some keyboard
events when it is active: all printable charaters; Delete, BackSpace, Home,
End, left and right arrows; Ctrl+R to clear EditField.
Edit text can be limited. By default a user can enter text of any length.
Use SetMaxWidth to limit the maximum text length. If the text is longer than
maximun then the text is automatically truncated.
EditField calls onChage in case of its text is changed. Event field Msg contains the new text
*/
type EditField struct {
	ControlBase
	// cursor position in edit text
	cursorPos int
	// the number of the first displayed text character - it is used in case of text is longer than edit width
	offset   int
	readonly bool
	maxWidth int

	onChange   func(Event)
	onKeyPress func(term.Key) bool
}

// NewEditField creates a new EditField control
// view - is a View that manages the control
// parent - is container that keeps the control. The same View can be a view and a parent at the same time.
// width - is minimal width of the control.
// text - text to edit.
// scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
//  control should keep its original size.
func NewEditField(view View, parent Control, width int, text string, scale int) *EditField {
	e := new(EditField)
	e.onChange = nil
	e.SetTitle(text)
	e.SetEnabled(true)

	if width == AutoSize {
		width = xs.Len(text) + 1
	}

	e.SetSize(width, 1)
	e.cursorPos = xs.Len(text)
	e.offset = 0
	e.parent = parent
	e.view = view
	e.parent = parent
	e.readonly = false

	e.SetConstraints(width, 1)

	e.end()

	if parent != nil {
		parent.AddChild(e, scale)
	}

	return e
}

// OnChange sets the callback that is called when EditField content is changed
func (e *EditField) OnChange(fn func(Event)) {
	e.onChange = fn
}

// OnKeyPress sets the callback that is called when a user presses a Key while
// the controls is active. If a handler processes the key it should return
// true. If handler returns false it means that the default handler will
// process the key
func (e *EditField) OnKeyPress(fn func(term.Key) bool) {
	e.onKeyPress = fn
}

// SetTitle changes the EditField content and emits OnChage eventif the new value does not equal to old one
func (e *EditField) SetTitle(title string) {
	if e.title != title {
		e.title = title
		if e.onChange != nil {
			ev := Event{Msg: title, Sender: e}
			go e.onChange(ev)
		}
	}
}

// Repaint draws the control on its View surface
func (e *EditField) Repaint() {
	canvas := e.view.Canvas()

	x, y := e.Pos()
	w, _ := e.Size()

	tm := e.view.Screen().Theme()
	parts := []rune(tm.SysObject(ObjEdit))
	chLeft, chRight := string(parts[0]), string(parts[1])

	var textOut string
	curOff := 0
	if e.offset == 0 && xs.Len(e.title) < e.width {
		textOut = e.title
	} else {
		fromIdx := 0
		toIdx := 0
		if e.offset == 0 {
			toIdx = e.width - 1
			textOut = xs.Slice(e.title, 0, toIdx) + chRight
			curOff = -e.offset
		} else {
			curOff = 1 - e.offset
			fromIdx = e.offset
			if e.width-1 <= xs.Len(e.title)-e.offset {
				toIdx = e.offset + e.width - 2
				textOut = chLeft + xs.Slice(e.title, fromIdx, toIdx) + chRight
			} else {
				textOut = chLeft + xs.Slice(e.title, fromIdx, -1)
			}
		}
	}

	fg, bg := RealColor(tm, e.fg, ColorEditText), RealColor(tm, e.bg, ColorEditBack)
	if !e.Enabled() {
		fg, bg = RealColor(tm, e.fg, ColorDisabledText), RealColor(tm, e.fg, ColorDisabledBack)
	} else if e.Active() {
		fg, bg = RealColor(tm, e.fg, ColorEditActiveText), RealColor(tm, e.bg, ColorEditActiveBack)

	}

	canvas.FillRect(x, y, w, 1, term.Cell{Ch: ' ', Bg: bg})
	canvas.PutText(x, y, textOut, fg, bg)
	if e.active {
		wx, wy := e.view.Pos()
		canvas.SetCursorPos(e.cursorPos+curOff+wx+e.x, wy+e.y)
	}
}

func (e *EditField) insertRune(ch rune) {
	if e.readonly {
		return
	}

	if e.maxWidth > 0 && xs.Len(e.title) >= e.maxWidth {
		return
	}

	idx := e.cursorPos

	if idx == 0 {
		e.SetTitle(string(ch) + e.title)
	} else if idx >= xs.Len(e.title) {
		e.SetTitle(e.title + string(ch))
	} else {
		e.SetTitle(xs.Slice(e.title, 0, idx) + string(ch) + xs.Slice(e.title, idx, -1))
	}

	e.cursorPos++

	if e.cursorPos >= e.width {
		if e.offset == 0 {
			e.offset = 2
		} else {
			e.offset++
		}
	}
}

func (e *EditField) backspace() {
	if e.title == "" || e.cursorPos == 0 || e.readonly {
		return
	}

	length := xs.Len(e.title)
	if e.cursorPos >= length {
		e.cursorPos--
		e.SetTitle(xs.Slice(e.title, 0, length-1))
	} else if e.cursorPos == 1 {
		e.cursorPos = 0
		e.SetTitle(xs.Slice(e.title, 1, -1))
		e.offset = 0
	} else {
		e.cursorPos--
		e.SetTitle(xs.Slice(e.title, 0, e.cursorPos) + xs.Slice(e.title, e.cursorPos+1, -1))
	}

	if length-1 < e.width {
		e.offset = 0
	}
}

func (e *EditField) del() {
	length := xs.Len(e.title)

	if e.title == "" || e.cursorPos == length || e.readonly {
		return
	}

	if e.cursorPos == length-1 {
		e.SetTitle(xs.Slice(e.title, 0, length-1))
	} else {
		e.SetTitle(xs.Slice(e.title, 0, e.cursorPos) + xs.Slice(e.title, e.cursorPos+1, -1))
	}

	if length-1 < e.width {
		e.offset = 0
	}
}

func (e *EditField) charLeft() {
	if e.cursorPos == 0 || e.title == "" {
		return
	}

	if e.cursorPos == e.offset {
		e.offset--
	}

	e.cursorPos--
}

func (e *EditField) charRight() {
	length := xs.Len(e.title)
	if e.cursorPos == length || e.title == "" {
		return
	}

	e.cursorPos++
	if e.cursorPos != length && e.cursorPos >= e.offset+e.width-2 {
		e.offset++
	}
}

func (e *EditField) home() {
	e.offset = 0
	e.cursorPos = 0
}

func (e *EditField) end() {
	length := xs.Len(e.title)
	e.cursorPos = length

	if length < e.width {
		return
	}

	e.offset = length - (e.width - 2)
}

// Clear empties the EditField and emits OnChange event
func (e *EditField) Clear() {
	e.home()
	e.SetTitle("")
}

/*
ProcessEvent processes all events come from the control parent. If a control
processes an event it should return true. If the method returns false it means
that the control do not want or cannot process the event and the caller sends
the event to the control parent
*/
func (e *EditField) ProcessEvent(event Event) bool {
	if !e.Active() || !e.Enabled() {
		return false
	}

	if event.Type == EventActivate && event.X == 0 {
		term.HideCursor()
	}

	if event.Type == EventKey && event.Key != term.KeyTab {
		if e.onKeyPress != nil {
			res := e.onKeyPress(event.Key)
			if res {
				return true
			}
		}

		switch event.Key {
		case term.KeyEnter:
			return false
		case term.KeySpace:
			e.insertRune(' ')
			return true
		case term.KeyBackspace:
			e.backspace()
			return true
		case term.KeyDelete:
			e.del()
			return true
		case term.KeyArrowLeft:
			e.charLeft()
			return true
		case term.KeyHome:
			e.home()
			return true
		case term.KeyEnd:
			e.end()
			return true
		case term.KeyCtrlR:
			if !e.readonly {
				e.Clear()
			}
			return true
		case term.KeyArrowRight:
			e.charRight()
			return true
		case term.KeyCtrlC:
			clip.WriteAll(e.Title())
			return true
		case term.KeyCtrlV:
			if !e.readonly {
				s, _ := clip.ReadAll()
				e.SetTitle(s)
				e.end()
			}
			return true
		default:
			if event.Ch != 0 {
				e.insertRune(event.Ch)
				return true
			}
		}
		return false
	}

	return false
}

// SetMaxWidth sets the maximum lenght of the EditField text. If the current text is longer it is truncated
func (e *EditField) SetMaxWidth(w int) {
	e.maxWidth = w
	if w > 0 && xs.Len(e.title) > w {
		e.title = xs.Slice(e.title, 0, w)
		e.end()
	}
}

// MaxWidth returns the current maximum text length. Zero means no limit
func (e *EditField) MaxWidth() int {
	return e.maxWidth
}

// SetSize changes control size. Constant DoNotChange can be
// used as placeholder to indicate that the control attrubute
// should be unchanged.
// Method does nothing if new size is less than minimal size
// EditField height cannot be changed - it equals 1 always
func (e *EditField) SetSize(width, height int) {
	if width != DoNotChange && (width > 1000 || width < e.minW) {
		return
	}
	if height != DoNotChange && (height > 200 || height < e.minH) {
		return
	}

	if width != DoNotChange {
		e.width = width
	}

	e.height = 1
}
<|endoftext|>


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

// Package chrometracing writes per-process Chrome trace_event files that can be
// loaded into chrome://tracing.
package chrometracing

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/google/chrometracing/traceinternal"
)

var trace = struct {
	start time.Time
	pid   uint64

	fileMu sync.Mutex
	file   *os.File
}{
	pid: uint64(os.Getpid()),
}

var out = setup()

// Path returns the full path of the chrome://tracing trace_event file for
// display in log messages.
func Path() string { return out }

func setup() string {
	var err error
	dir := os.Getenv("TEST_UNDECLARED_OUTPUTS_DIR")
	if dir == "" {
		dir = os.TempDir()
	}
	fn := filepath.Join(dir, fmt.Sprintf("%s.%d.trace", filepath.Base(os.Args[0]), trace.pid))
	trace.file, err = os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC|os.O_EXCL, 0644)
	if err != nil {
		// Using the log package from func init results in an error message
		// being printed.
		fmt.Fprintf(os.Stderr, "continuing without tracing: %v\n", err)
		return ""
	}

	// We only ever open a JSON array. Ending the array is optional as per
	// go/trace_event so that not cleanly finished traces can still be read.
	trace.file.Write([]byte{'['})
	trace.start = time.Now()

	writeEvent(&traceinternal.ViewerEvent{
		Name:  "process_name",
		Phase: "M", // Metadata Event
		Pid:   trace.pid,
		Tid:   trace.pid,
		Arg: struct {
			Name string `json:"name"`
		}{
			Name: strings.Join(os.Args, " "),
		},
	})
	return fn
}

func writeEvent(ev *traceinternal.ViewerEvent) {
	b, err := json.Marshal(&ev)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	trace.fileMu.Lock()
	defer trace.fileMu.Unlock()
	if _, err = trace.file.Write(b); err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	if _, err = trace.file.Write([]byte{',', '\n'}); err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
}

const (
	begin = "B"
	end   = "E"
)

// A PendingEvent represents an ongoing unit of work. The begin trace event has
// already been written, and calling Done will write the end trace event.
type PendingEvent struct {
	name string
	tid  uint64
}

// Done writes the end trace event for this unit of work.
func (pe *PendingEvent) Done() {
	if pe == nil || pe.name == "" || trace.file == nil {
		return
	}
	writeEvent(&traceinternal.ViewerEvent{
		Name:  pe.name,
		Phase: end,
		Pid:   trace.pid,
		Tid:   pe.tid,
		Time:  float64(time.Since(trace.start).Microseconds()),
	})
}

// Event logs a unit of work. To instrument a Go function, use e.g.:
//
//   func calcPi() {
//     defer chrometracing.Event("calculate pi").Done()
//     // …
//   }
//
// For more finely-granular traces, use e.g.:
//
//   for _, cmd := range commands {
//     ev := chrometracing.Event("initialize " + cmd.Name)
//     cmd.Init()
//     ev.Done()
//   }
func Event(name string, tid uint64) *PendingEvent {
	if trace.file == nil {
		return &PendingEvent{}
	}
	writeEvent(&traceinternal.ViewerEvent{
		Name:  name,
		Phase: begin,
		Pid:   trace.pid,
		Tid:   tid,
		Time:  float64(time.Since(trace.start).Microseconds()),
	})
	return &PendingEvent{name, tid}
}

// Flush should be called before your program terminates, and/or periodically
// for long-running programs, to flush any pending chrome://tracing events out
// to disk.
func Flush() error {
	if err := trace.file.Sync(); err != nil {
		return fmt.Errorf("flushing trace file: %v", err)
	}
	return nil
}
<commit_msg>allow turning on tracing explicitly<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package chrometracing writes per-process Chrome trace_event files that can be
// loaded into chrome://tracing.
package chrometracing

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/google/chrometracing/traceinternal"
)

var trace = struct {
	start time.Time
	pid   uint64

	fileMu sync.Mutex
	file   *os.File
}{
	pid: uint64(os.Getpid()),
}

var out = setup(false)

// Path returns the full path of the chrome://tracing trace_event file for
// display in log messages.
func Path() string { return out }

// EnableTracing turns on tracing, regardless of running in a test or
// not. Tracing is enabled by default if the CHROMETRACING_DIR environment
// variable is present and non-empty.
func EnableTracing() {
	trace.fileMu.Lock()
	alreadyEnabled := trace.file != nil
	trace.fileMu.Unlock()
	if alreadyEnabled {
		return
	}
	out = setup(true)
}

func setup(overrideEnable bool) string {
	inTest := os.Getenv("TEST_TMPDIR") != ""
	explicitlyEnabled := os.Getenv("CHROMETRACING_DIR") != ""
	enableTracing := inTest || explicitlyEnabled || overrideEnable
	if !enableTracing {
		return ""
	}

	var err error
	dir := os.Getenv("TEST_UNDECLARED_OUTPUTS_DIR")
	if dir == "" {
		dir = os.Getenv("CHROMETRACING_DIR")
	}
	if dir == "" {
		dir = os.TempDir()
	}
	fn := filepath.Join(dir, fmt.Sprintf("%s.%d.trace", filepath.Base(os.Args[0]), trace.pid))
	trace.file, err = os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC|os.O_EXCL, 0644)
	if err != nil {
		// Using the log package from func init results in an error message
		// being printed.
		fmt.Fprintf(os.Stderr, "continuing without tracing: %v\n", err)
		return ""
	}

	// We only ever open a JSON array. Ending the array is optional as per
	// go/trace_event so that not cleanly finished traces can still be read.
	trace.file.Write([]byte{'['})
	trace.start = time.Now()

	writeEvent(&traceinternal.ViewerEvent{
		Name:  "process_name",
		Phase: "M", // Metadata Event
		Pid:   trace.pid,
		Tid:   trace.pid,
		Arg: struct {
			Name string `json:"name"`
		}{
			Name: strings.Join(os.Args, " "),
		},
	})
	return fn
}

func writeEvent(ev *traceinternal.ViewerEvent) {
	b, err := json.Marshal(&ev)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	trace.fileMu.Lock()
	defer trace.fileMu.Unlock()
	if _, err = trace.file.Write(b); err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	if _, err = trace.file.Write([]byte{',', '\n'}); err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
}

const (
	begin = "B"
	end   = "E"
)

// A PendingEvent represents an ongoing unit of work. The begin trace event has
// already been written, and calling Done will write the end trace event.
type PendingEvent struct {
	name string
	tid  uint64
}

// Done writes the end trace event for this unit of work.
func (pe *PendingEvent) Done() {
	if pe == nil || pe.name == "" || trace.file == nil {
		return
	}
	writeEvent(&traceinternal.ViewerEvent{
		Name:  pe.name,
		Phase: end,
		Pid:   trace.pid,
		Tid:   pe.tid,
		Time:  float64(time.Since(trace.start).Microseconds()),
	})
}

// Event logs a unit of work. To instrument a Go function, use e.g.:
//
//   func calcPi() {
//     defer chrometracing.Event("calculate pi").Done()
//     // …
//   }
//
// For more finely-granular traces, use e.g.:
//
//   for _, cmd := range commands {
//     ev := chrometracing.Event("initialize " + cmd.Name)
//     cmd.Init()
//     ev.Done()
//   }
func Event(name string, tid uint64) *PendingEvent {
	if trace.file == nil {
		return &PendingEvent{}
	}
	writeEvent(&traceinternal.ViewerEvent{
		Name:  name,
		Phase: begin,
		Pid:   trace.pid,
		Tid:   tid,
		Time:  float64(time.Since(trace.start).Microseconds()),
	})
	return &PendingEvent{name, tid}
}

// Flush should be called before your program terminates, and/or periodically
// for long-running programs, to flush any pending chrome://tracing events out
// to disk.
func Flush() error {
	if err := trace.file.Sync(); err != nil {
		return fmt.Errorf("flushing trace file: %v", err)
	}
	return nil
}
<|endoftext|>


<commit_before>package romulus

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

var (
	bcknds       = "backends"
	frntnds      = "frontends"
	bckndDirFmt  = "backends/%s"
	frntndDirFmt = "frontends/%s"
	bckndFmt     = "backends/%s/backend"
	srvrDirFmt   = "backends/%s/servers"
	srvrFmt      = "backends/%s/servers/%s"
	frntndFmt    = "frontends/%s/frontend"

	annotationFmt = "romulus/%s%s"
	rteConv       = map[string]string{
		"host":         "Host(`%s`)",
		"method":       "Method(`%s`)",
		"path":         "Path(`%s`)",
		"header":       "Header(`%s`)",
		"hostRegexp":   "HostRegexp(`%s`)",
		"methodRegexp": "MethodRegexp(`%s`)",
		"pathRegexp":   "PathRegexp(`%s`)",
		"headerRegexp": "HeaderRegexp(`%s`)",
	}
)

// VulcanObject represents a vulcand component
type VulcanObject interface {
	// Key returns the etcd key for this object
	Key() string
	// Val returns the (JSON-ified) value to store in etcd
	Val() (string, error)
}

type BackendList map[int]*Backend

// Backend is a vulcand backend
type Backend struct {
	ID       string `json:"Id,omitempty"`
	Type     string
	Settings *BackendSettings `json:",omitempty"`
}

// BackendSettings is vulcand backend settings
type BackendSettings struct {
	Timeouts  *BackendSettingsTimeouts  `json:",omitempty"`
	KeepAlive *BackendSettingsKeepAlive `json:",omitempty"`
	TLS       *TLSSettings              `json:",omitempty"`
}

// BackendSettingsTimeouts is vulcand settings for backend timeouts
type BackendSettingsTimeouts struct {
	Read         time.Duration `json:",omitempty"`
	Dial         time.Duration `json:",omitempty"`
	TLSHandshake time.Duration `json:",omitempty"`
}

// BackendSettingsKeepAlive is vulcand settings for backend keep alive
type BackendSettingsKeepAlive struct {
	Period              time.Duration `json:",omitempty"`
	MaxIdleConnsPerHost int           `json:",omitempty"`
}

type TLSSettings struct {
	PreferServerCipherSuites bool          `json:",omitempty"`
	InsecureSkipVerify       bool          `json:",omitempty"`
	SessionTicketsDisabled   bool          `json:",omitempty"`
	SessionCache             *SessionCache `json:",omitempty"`
	CipherSuites             []string      `json:",omitempty"`
	MinVersion               string        `json:",omitempty"`
	MaxVersion               string        `json:",omitempty"`
}

type SessionCache struct {
	Type     string
	Settings *SessionCacheSettings
}

type SessionCacheSettings struct {
	Capacity int
}

// ServerMap is a map of IPs (string) -> Server
type ServerMap map[string]Server

// Server is a vulcand server
type Server struct {
	URL     *URL   `json:"URL"`
	Backend string `json:"-"`
}

// Frontend is a vulcand frontend
type Frontend struct {
	ID        string `json:"Id,omitempty"`
	Type      string
	BackendID string `json:"BackendId"`
	Route     string
	Settings  *FrontendSettings `json:",omitempty"`
}

// FrontendSettings is vulcand frontend settings
type FrontendSettings struct {
	FailoverPredicate  string                  `json:",omitempty"`
	Hostname           string                  `json:",omitempty"`
	TrustForwardHeader bool                    `json:",omitempty"`
	Limits             *FrontendSettingsLimits `json:",omitempty"`
}

// FrontendSettingsLimits is vulcand settings for frontend limits
type FrontendSettingsLimits struct {
	MaxMemBodyBytes int
	MaxBodyBytes    int
}

// NewBackend returns a ref to a Backend object
func NewBackend(id string) *Backend {
	return &Backend{
		ID: id,
	}
}

// NewFrontend returns a ref to a Frontend object
func NewFrontend(id, bid string) *Frontend {
	return &Frontend{
		ID:        id,
		BackendID: bid,
	}
}

// NewBackendSettings returns BackendSettings from raw JSON
func NewBackendSettings(p []byte) *BackendSettings {
	var ba BackendSettings
	b := bytes.NewBuffer(p)
	json.NewDecoder(b).Decode(&ba)
	return &ba
}

// NewFrontendSettings returns FrontendSettings from raw JSON
func NewFrontendSettings(p []byte) *FrontendSettings {
	var f FrontendSettings
	b := bytes.NewBuffer(p)
	json.NewDecoder(b).Decode(&f)
	return &f
}

func (b Backend) Key() string { return fmt.Sprintf(bckndFmt, b.ID) }
func (s Server) Key() string {
	return fmt.Sprintf(srvrFmt, s.Backend, s.URL.GetHost())
}
func (f Frontend) Key() string         { return fmt.Sprintf(frntndFmt, f.ID) }
func (f FrontendSettings) Key() string { return "" }
func (b BackendSettings) Key() string  { return "" }

func (b Backend) Val() (string, error)          { return encode(b) }
func (s Server) Val() (string, error)           { return encode(s) }
func (f Frontend) Val() (string, error)         { return encode(f) }
func (f FrontendSettings) Val() (string, error) { return "", nil }
func (b BackendSettings) Val() (string, error)  { return "", nil }

// DirKey returns the etcd directory key for this Backend
func (b Backend) DirKey() string { return fmt.Sprintf(bckndDirFmt, b.ID) }

// DirKey returns the etcd directory key for this Frontend
func (f Frontend) DirKey() string { return fmt.Sprintf(frntndDirFmt, f.ID) }

func (f *FrontendSettings) String() string {
	s, e := encode(f)
	if e != nil {
		return e.Error()
	}
	return s
}

func (b *BackendSettings) String() string {
	s, e := encode(b)
	if e != nil {
		return e.Error()
	}
	return s
}

// IPs returns the ServerMap IPs
func (s ServerMap) IPs() []string {
	st := []string{}
	for ip := range s {
		st = append(st, ip)
	}
	return st
}

func encode(v VulcanObject) (string, error) {
	b := new(bytes.Buffer)
	e := json.NewEncoder(b).Encode(v)
	return strings.TrimSpace(HTMLUnescape(b.String())), e
}

func buildRoute(ns string, a map[string]string) string {
	rt := []string{}
	if ns != "" {
		ns = fmt.Sprintf(".%s", ns)
	}
	for k, f := range rteConv {
		pk := fmt.Sprintf(annotationFmt, k, ns)
		if v, ok := a[pk]; ok {
			if k == "method" {
				v = strings.ToUpper(v)
			}
			rt = append(rt, fmt.Sprintf(f, v))
		}
	}
	if len(rt) < 1 {
		rt = []string{"Path('/')"}
	}
	return strings.Join(rt, " && ")
}
<commit_msg>romulus/vulcan.go: Swap ' for ` in default route<commit_after>package romulus

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

var (
	bcknds       = "backends"
	frntnds      = "frontends"
	bckndDirFmt  = "backends/%s"
	frntndDirFmt = "frontends/%s"
	bckndFmt     = "backends/%s/backend"
	srvrDirFmt   = "backends/%s/servers"
	srvrFmt      = "backends/%s/servers/%s"
	frntndFmt    = "frontends/%s/frontend"

	annotationFmt = "romulus/%s%s"
	rteConv       = map[string]string{
		"host":         "Host(`%s`)",
		"method":       "Method(`%s`)",
		"path":         "Path(`%s`)",
		"header":       "Header(`%s`)",
		"hostRegexp":   "HostRegexp(`%s`)",
		"methodRegexp": "MethodRegexp(`%s`)",
		"pathRegexp":   "PathRegexp(`%s`)",
		"headerRegexp": "HeaderRegexp(`%s`)",
	}
)

// VulcanObject represents a vulcand component
type VulcanObject interface {
	// Key returns the etcd key for this object
	Key() string
	// Val returns the (JSON-ified) value to store in etcd
	Val() (string, error)
}

type BackendList map[int]*Backend

// Backend is a vulcand backend
type Backend struct {
	ID       string `json:"Id,omitempty"`
	Type     string
	Settings *BackendSettings `json:",omitempty"`
}

// BackendSettings is vulcand backend settings
type BackendSettings struct {
	Timeouts  *BackendSettingsTimeouts  `json:",omitempty"`
	KeepAlive *BackendSettingsKeepAlive `json:",omitempty"`
	TLS       *TLSSettings              `json:",omitempty"`
}

// BackendSettingsTimeouts is vulcand settings for backend timeouts
type BackendSettingsTimeouts struct {
	Read         time.Duration `json:",omitempty"`
	Dial         time.Duration `json:",omitempty"`
	TLSHandshake time.Duration `json:",omitempty"`
}

// BackendSettingsKeepAlive is vulcand settings for backend keep alive
type BackendSettingsKeepAlive struct {
	Period              time.Duration `json:",omitempty"`
	MaxIdleConnsPerHost int           `json:",omitempty"`
}

type TLSSettings struct {
	PreferServerCipherSuites bool          `json:",omitempty"`
	InsecureSkipVerify       bool          `json:",omitempty"`
	SessionTicketsDisabled   bool          `json:",omitempty"`
	SessionCache             *SessionCache `json:",omitempty"`
	CipherSuites             []string      `json:",omitempty"`
	MinVersion               string        `json:",omitempty"`
	MaxVersion               string        `json:",omitempty"`
}

type SessionCache struct {
	Type     string
	Settings *SessionCacheSettings
}

type SessionCacheSettings struct {
	Capacity int
}

// ServerMap is a map of IPs (string) -> Server
type ServerMap map[string]Server

// Server is a vulcand server
type Server struct {
	URL     *URL   `json:"URL"`
	Backend string `json:"-"`
}

// Frontend is a vulcand frontend
type Frontend struct {
	ID        string `json:"Id,omitempty"`
	Type      string
	BackendID string `json:"BackendId"`
	Route     string
	Settings  *FrontendSettings `json:",omitempty"`
}

// FrontendSettings is vulcand frontend settings
type FrontendSettings struct {
	FailoverPredicate  string                  `json:",omitempty"`
	Hostname           string                  `json:",omitempty"`
	TrustForwardHeader bool                    `json:",omitempty"`
	Limits             *FrontendSettingsLimits `json:",omitempty"`
}

// FrontendSettingsLimits is vulcand settings for frontend limits
type FrontendSettingsLimits struct {
	MaxMemBodyBytes int
	MaxBodyBytes    int
}

// NewBackend returns a ref to a Backend object
func NewBackend(id string) *Backend {
	return &Backend{
		ID: id,
	}
}

// NewFrontend returns a ref to a Frontend object
func NewFrontend(id, bid string) *Frontend {
	return &Frontend{
		ID:        id,
		BackendID: bid,
	}
}

// NewBackendSettings returns BackendSettings from raw JSON
func NewBackendSettings(p []byte) *BackendSettings {
	var ba BackendSettings
	b := bytes.NewBuffer(p)
	json.NewDecoder(b).Decode(&ba)
	return &ba
}

// NewFrontendSettings returns FrontendSettings from raw JSON
func NewFrontendSettings(p []byte) *FrontendSettings {
	var f FrontendSettings
	b := bytes.NewBuffer(p)
	json.NewDecoder(b).Decode(&f)
	return &f
}

func (b Backend) Key() string { return fmt.Sprintf(bckndFmt, b.ID) }
func (s Server) Key() string {
	return fmt.Sprintf(srvrFmt, s.Backend, s.URL.GetHost())
}
func (f Frontend) Key() string         { return fmt.Sprintf(frntndFmt, f.ID) }
func (f FrontendSettings) Key() string { return "" }
func (b BackendSettings) Key() string  { return "" }

func (b Backend) Val() (string, error)          { return encode(b) }
func (s Server) Val() (string, error)           { return encode(s) }
func (f Frontend) Val() (string, error)         { return encode(f) }
func (f FrontendSettings) Val() (string, error) { return "", nil }
func (b BackendSettings) Val() (string, error)  { return "", nil }

// DirKey returns the etcd directory key for this Backend
func (b Backend) DirKey() string { return fmt.Sprintf(bckndDirFmt, b.ID) }

// DirKey returns the etcd directory key for this Frontend
func (f Frontend) DirKey() string { return fmt.Sprintf(frntndDirFmt, f.ID) }

func (f *FrontendSettings) String() string {
	s, e := encode(f)
	if e != nil {
		return e.Error()
	}
	return s
}

func (b *BackendSettings) String() string {
	s, e := encode(b)
	if e != nil {
		return e.Error()
	}
	return s
}

// IPs returns the ServerMap IPs
func (s ServerMap) IPs() []string {
	st := []string{}
	for ip := range s {
		st = append(st, ip)
	}
	return st
}

func encode(v VulcanObject) (string, error) {
	b := new(bytes.Buffer)
	e := json.NewEncoder(b).Encode(v)
	return strings.TrimSpace(HTMLUnescape(b.String())), e
}

func buildRoute(ns string, a map[string]string) string {
	rt := []string{}
	if ns != "" {
		ns = fmt.Sprintf(".%s", ns)
	}
	for k, f := range rteConv {
		pk := fmt.Sprintf(annotationFmt, k, ns)
		if v, ok := a[pk]; ok {
			if k == "method" {
				v = strings.ToUpper(v)
			}
			rt = append(rt, fmt.Sprintf(f, v))
		}
	}
	if len(rt) < 1 {
		rt = []string{"Path(`/`)"}
	}
	return strings.Join(rt, " && ")
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/braintree/manners"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/meatballhat/negroni-logrus"
	"github.com/travis-ci/jupiter-brain"
	"github.com/travis-ci/jupiter-brain/server/jsonapi"
	"github.com/travis-ci/jupiter-brain/server/negroniraven"
	"golang.org/x/net/context"
)

type server struct {
	addr, authToken, sentryDSN string

	log *logrus.Logger

	i jupiterbrain.InstanceManager

	n *negroni.Negroni
	r *mux.Router
	s *manners.GracefulServer

	db database
}

func newServer(cfg *Config) (*server, error) {
	log := logrus.New()
	if cfg.Debug {
		log.Level = logrus.DebugLevel
	}

	log.Formatter = &logrus.TextFormatter{DisableColors: true}

	u, err := url.Parse(cfg.VSphereURL)
	if err != nil {
		return nil, err
	}

	if !u.IsAbs() {
		return nil, fmt.Errorf("vSphere API URL must be absolute")
	}

	db, err := newPGDatabase(cfg.DatabaseURL)
	if err != nil {
		return nil, err
	}

	paths := jupiterbrain.VSpherePaths{
		BasePath:    cfg.VSphereBasePath,
		VMPath:      cfg.VSphereVMPath,
		ClusterPath: cfg.VSphereClusterPath,
	}

	srv := &server{
		addr:      cfg.Addr,
		authToken: cfg.AuthToken,
		sentryDSN: cfg.SentryDSN,

		log: log,

		i: jupiterbrain.NewVSphereInstanceManager(log, u, paths),

		n: negroni.New(),
		r: mux.NewRouter(),
		s: manners.NewServer(),

		db: db,
	}

	return srv, nil
}

func (srv *server) Setup() {
	srv.setupRoutes()
	srv.setupMiddleware()
}

func (srv *server) Run() {
	srv.log.WithField("addr", srv.addr).Info("Listening")
	_ = srv.s.ListenAndServe(srv.addr, srv.n)
}

func (srv *server) setupRoutes() {
	srv.r.HandleFunc(`/instances`, srv.handleInstancesList).Methods("GET").Name("instances-list")
	srv.r.HandleFunc(`/instances`, srv.handleInstancesCreate).Methods("POST").Name("instances-create")
	srv.r.HandleFunc(`/instances/{id}`, srv.handleInstanceByIDFetch).Methods("GET").Name("instance-by-id")
	srv.r.HandleFunc(`/instances/{id}`, srv.handleInstanceByIDTerminate).Methods("DELETE").Name("instance-by-id-terminate")
	srv.r.HandleFunc(`/instance-syncs`, srv.handleInstanceSync).Methods("POST").Name("instance-syncs-create")
}

func (srv *server) setupMiddleware() {
	srv.n.Use(negroni.NewRecovery())
	srv.n.Use(negronilogrus.NewCustomMiddleware(srv.log.Level, srv.log.Formatter, "web"))
	srv.n.Use(negroni.HandlerFunc(srv.authMiddleware))
	nr, err := negroniraven.NewMiddleware(srv.sentryDSN)
	if err != nil {
		panic(err)
	}
	srv.n.Use(nr)
	srv.n.UseHandler(srv.r)
}

func (srv *server) authMiddleware(w http.ResponseWriter, req *http.Request, f http.HandlerFunc) {
	authHeader := req.Header.Get("Authorization")
	srv.log.WithField("authorization", authHeader).Debug("raw authorization header")

	if authHeader == "" {
		w.Header().Set("WWW-Authenticate", "token")
		srv.log.WithField("request_id", req.Header.Get("X-Request-ID")).Debug("responding 401 due to empty Authorization header")

		jsonapi.Error(w, errors.New("token is required"), http.StatusUnauthorized)
		return
	}

	if authHeader != ("token "+srv.authToken) && authHeader != ("token="+srv.authToken) {
		jsonapi.Error(w, errors.New("incorrect token"), http.StatusUnauthorized)
		return
	}

	f(w, req)
}

func (srv *server) handleInstancesList(w http.ResponseWriter, req *http.Request) {
	instances, err := srv.i.List(context.TODO())
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	dbInstanceIDs := []string{}
	dbInstanceIDCreatedMap := map[string]time.Time{}
	applyDBFilter := false

	if req.FormValue("min_age") != "" {
		dur, err := time.ParseDuration(req.FormValue("min_age"))
		if err != nil {
			jsonapi.Error(w, err, http.StatusBadRequest)
			return
		}

		res, err := srv.db.FetchInstances(&databaseQuery{MinAge: dur})
		if err != nil {
			jsonapi.Error(w, err, http.StatusBadRequest)
			return
		}

		srv.log.WithFields(logrus.Fields{
			"n": len(res),
		}).Debug("retrieved instances from database")

		for _, r := range res {
			dbInstanceIDCreatedMap[r.ID] = r.CreatedAt
			dbInstanceIDs = append(dbInstanceIDs, r.ID)
		}

		applyDBFilter = true
	}

	response := map[string][]interface{}{
		"data": make([]interface{}, 0),
	}

	if applyDBFilter {
		keptInstances := []*jupiterbrain.Instance{}
		for _, instance := range instances {
			for _, instID := range dbInstanceIDs {
				if instID == instance.ID {
					instance.CreatedAt = dbInstanceIDCreatedMap[instID]
					keptInstances = append(keptInstances, instance)
				}
			}
		}

		srv.log.WithFields(logrus.Fields{
			"pre_filter":  len(instances),
			"post_filter": len(keptInstances),
		}).Debug("applying known instance filter")

		instances = keptInstances
	}

	for _, instance := range instances {
		response["data"] = append(response["data"], MarshalInstance(instance))
	}

	b, err := json.MarshalIndent(response, "", "  ")
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, string(b)+"\n")
}

func (srv *server) handleInstancesCreate(w http.ResponseWriter, req *http.Request) {
	var requestBody map[string]map[string]string

	err := json.NewDecoder(req.Body).Decode(&requestBody)
	if err != nil {
		jsonapi.Error(w, err, http.StatusBadRequest)
		return
	}

	if requestBody["data"] == nil {
		jsonapi.Error(w, &jsonapi.JSONError{Status: "422", Code: "missing-field", Title: "root object must have data field"}, 422)
		return
	}

	if requestBody["data"]["type"] != "instances" {
		jsonapi.Error(w, &jsonapi.JSONError{Status: "409", Code: "incorrect-type", Title: "data must be of type instances"}, http.StatusConflict)
		return
	}

	if requestBody["data"]["base-image"] == "" {
		jsonapi.Error(w, &jsonapi.JSONError{Status: "422", Code: "missing-field", Title: "instance must have base-image field"}, 422)
		return
	}

	instance, err := srv.i.Start(context.TODO(), requestBody["data"]["base-image"])
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	recoverDelete := false
	defer func() {
		if recoverDelete && instance != nil {
			go func() { _ = srv.i.Terminate(context.TODO(), instance.ID) }()
		}
	}()

	instance.CreatedAt = time.Now().UTC()
	err = srv.db.SaveInstance(instance)
	if err != nil {
		recoverDelete = true
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	response := map[string][]interface{}{
		"data": {MarshalInstance(instance)},
	}

	b, err := json.MarshalIndent(response, "", "  ")
	if err != nil {
		recoverDelete = true
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.Header().Set("Location", fmt.Sprintf("/instances/%s", instance.ID))
	w.WriteHeader(http.StatusCreated)
	fmt.Fprintf(w, string(b)+"\n")
}

func (srv *server) handleInstanceByIDFetch(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	instance, err := srv.i.Fetch(context.TODO(), vars["id"])
	if err != nil {
		switch err.(type) {
		case jupiterbrain.VirtualMachineNotFoundError:
			jsonapi.Error(w, err, http.StatusNotFound)
			return
		default:
			srv.log.WithFields(logrus.Fields{
				"err": err,
				"id":  vars["id"],
			}).Error("failed to fetch instance")
			jsonapi.Error(w, err, http.StatusInternalServerError)
			return
		}
	}

	response := map[string][]interface{}{
		"data": {MarshalInstance(instance)},
	}

	b, err := json.MarshalIndent(response, "", "  ")
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, string(b)+"\n")
}

func (srv *server) handleInstanceByIDTerminate(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	err := srv.i.Terminate(context.TODO(), vars["id"])
	if err != nil {
		switch err.(type) {
		case jupiterbrain.VirtualMachineNotFoundError:
			jsonapi.Error(w, err, http.StatusNotFound)
			return
		default:
			srv.log.WithFields(logrus.Fields{
				"err": err,
				"id":  vars["id"],
			}).Error("failed to terminate instance")
			jsonapi.Error(w, err, http.StatusInternalServerError)
			return
		}
	}

	err = srv.db.DestroyInstance(vars["id"])
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (srv *server) handleInstanceSync(w http.ResponseWriter, req *http.Request) {
	instances, err := srv.i.List(context.TODO())
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	for _, instance := range instances {
		instance.CreatedAt = time.Now().UTC()
		err = srv.db.SaveInstance(instance)
		if err != nil {
			srv.log.WithFields(logrus.Fields{
				"err": err,
				"id":  instance.ID,
			}).Warn("failed to save instance")
			continue
		}

		srv.log.WithField("id", instance.ID).Debug("synced instance")
	}

	w.WriteHeader(http.StatusNoContent)
}
<commit_msg>Added process stats upon sending SIGUSR1.<commit_after>package server

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/braintree/manners"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/meatballhat/negroni-logrus"
	"github.com/travis-ci/jupiter-brain"
	"github.com/travis-ci/jupiter-brain/server/jsonapi"
	"github.com/travis-ci/jupiter-brain/server/negroniraven"
	"golang.org/x/net/context"
)

type server struct {
	addr, authToken, sentryDSN string

	log *logrus.Logger

	i jupiterbrain.InstanceManager

	n *negroni.Negroni
	r *mux.Router
	s *manners.GracefulServer

	db       database
	bootTime time.Time
}

func newServer(cfg *Config) (*server, error) {
	log := logrus.New()
	if cfg.Debug {
		log.Level = logrus.DebugLevel
	}

	log.Formatter = &logrus.TextFormatter{DisableColors: true}

	u, err := url.Parse(cfg.VSphereURL)
	if err != nil {
		return nil, err
	}

	if !u.IsAbs() {
		return nil, fmt.Errorf("vSphere API URL must be absolute")
	}

	db, err := newPGDatabase(cfg.DatabaseURL)
	if err != nil {
		return nil, err
	}

	paths := jupiterbrain.VSpherePaths{
		BasePath:    cfg.VSphereBasePath,
		VMPath:      cfg.VSphereVMPath,
		ClusterPath: cfg.VSphereClusterPath,
	}

	srv := &server{
		addr:      cfg.Addr,
		authToken: cfg.AuthToken,
		sentryDSN: cfg.SentryDSN,

		log: log,

		i: jupiterbrain.NewVSphereInstanceManager(log, u, paths),

		n: negroni.New(),
		r: mux.NewRouter(),
		s: manners.NewServer(),

		db:       db,
		bootTime: time.Now().UTC(),
	}

	return srv, nil
}

func (srv *server) Setup() {
	srv.setupRoutes()
	srv.setupMiddleware()
	go srv.signalHandler()
}

func (srv *server) Run() {
	srv.log.WithField("addr", srv.addr).Info("Listening")
	_ = srv.s.ListenAndServe(srv.addr, srv.n)
}

func (srv *server) setupRoutes() {
	srv.r.HandleFunc(`/instances`, srv.handleInstancesList).Methods("GET").Name("instances-list")
	srv.r.HandleFunc(`/instances`, srv.handleInstancesCreate).Methods("POST").Name("instances-create")
	srv.r.HandleFunc(`/instances/{id}`, srv.handleInstanceByIDFetch).Methods("GET").Name("instance-by-id")
	srv.r.HandleFunc(`/instances/{id}`, srv.handleInstanceByIDTerminate).Methods("DELETE").Name("instance-by-id-terminate")
	srv.r.HandleFunc(`/instance-syncs`, srv.handleInstanceSync).Methods("POST").Name("instance-syncs-create")
}

func (srv *server) setupMiddleware() {
	srv.n.Use(negroni.NewRecovery())
	srv.n.Use(negronilogrus.NewCustomMiddleware(srv.log.Level, srv.log.Formatter, "web"))
	srv.n.Use(negroni.HandlerFunc(srv.authMiddleware))
	nr, err := negroniraven.NewMiddleware(srv.sentryDSN)
	if err != nil {
		panic(err)
	}
	srv.n.Use(nr)
	srv.n.UseHandler(srv.r)
}

func (srv *server) authMiddleware(w http.ResponseWriter, req *http.Request, f http.HandlerFunc) {
	authHeader := req.Header.Get("Authorization")
	srv.log.WithField("authorization", authHeader).Debug("raw authorization header")

	if authHeader == "" {
		w.Header().Set("WWW-Authenticate", "token")
		srv.log.WithField("request_id", req.Header.Get("X-Request-ID")).Debug("responding 401 due to empty Authorization header")

		jsonapi.Error(w, errors.New("token is required"), http.StatusUnauthorized)
		return
	}

	if authHeader != ("token "+srv.authToken) && authHeader != ("token="+srv.authToken) {
		jsonapi.Error(w, errors.New("incorrect token"), http.StatusUnauthorized)
		return
	}

	f(w, req)
}

func (srv *server) handleInstancesList(w http.ResponseWriter, req *http.Request) {
	instances, err := srv.i.List(context.TODO())
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	dbInstanceIDs := []string{}
	dbInstanceIDCreatedMap := map[string]time.Time{}
	applyDBFilter := false

	if req.FormValue("min_age") != "" {
		dur, err := time.ParseDuration(req.FormValue("min_age"))
		if err != nil {
			jsonapi.Error(w, err, http.StatusBadRequest)
			return
		}

		res, err := srv.db.FetchInstances(&databaseQuery{MinAge: dur})
		if err != nil {
			jsonapi.Error(w, err, http.StatusBadRequest)
			return
		}

		srv.log.WithFields(logrus.Fields{
			"n": len(res),
		}).Debug("retrieved instances from database")

		for _, r := range res {
			dbInstanceIDCreatedMap[r.ID] = r.CreatedAt
			dbInstanceIDs = append(dbInstanceIDs, r.ID)
		}

		applyDBFilter = true
	}

	response := map[string][]interface{}{
		"data": make([]interface{}, 0),
	}

	if applyDBFilter {
		keptInstances := []*jupiterbrain.Instance{}
		for _, instance := range instances {
			for _, instID := range dbInstanceIDs {
				if instID == instance.ID {
					instance.CreatedAt = dbInstanceIDCreatedMap[instID]
					keptInstances = append(keptInstances, instance)
				}
			}
		}

		srv.log.WithFields(logrus.Fields{
			"pre_filter":  len(instances),
			"post_filter": len(keptInstances),
		}).Debug("applying known instance filter")

		instances = keptInstances
	}

	for _, instance := range instances {
		response["data"] = append(response["data"], MarshalInstance(instance))
	}

	b, err := json.MarshalIndent(response, "", "  ")
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, string(b)+"\n")
}

func (srv *server) handleInstancesCreate(w http.ResponseWriter, req *http.Request) {
	var requestBody map[string]map[string]string

	err := json.NewDecoder(req.Body).Decode(&requestBody)
	if err != nil {
		jsonapi.Error(w, err, http.StatusBadRequest)
		return
	}

	if requestBody["data"] == nil {
		jsonapi.Error(w, &jsonapi.JSONError{Status: "422", Code: "missing-field", Title: "root object must have data field"}, 422)
		return
	}

	if requestBody["data"]["type"] != "instances" {
		jsonapi.Error(w, &jsonapi.JSONError{Status: "409", Code: "incorrect-type", Title: "data must be of type instances"}, http.StatusConflict)
		return
	}

	if requestBody["data"]["base-image"] == "" {
		jsonapi.Error(w, &jsonapi.JSONError{Status: "422", Code: "missing-field", Title: "instance must have base-image field"}, 422)
		return
	}

	instance, err := srv.i.Start(context.TODO(), requestBody["data"]["base-image"])
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	recoverDelete := false
	defer func() {
		if recoverDelete && instance != nil {
			go func() { _ = srv.i.Terminate(context.TODO(), instance.ID) }()
		}
	}()

	instance.CreatedAt = time.Now().UTC()
	err = srv.db.SaveInstance(instance)
	if err != nil {
		recoverDelete = true
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	response := map[string][]interface{}{
		"data": {MarshalInstance(instance)},
	}

	b, err := json.MarshalIndent(response, "", "  ")
	if err != nil {
		recoverDelete = true
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.Header().Set("Location", fmt.Sprintf("/instances/%s", instance.ID))
	w.WriteHeader(http.StatusCreated)
	fmt.Fprintf(w, string(b)+"\n")
}

func (srv *server) handleInstanceByIDFetch(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	instance, err := srv.i.Fetch(context.TODO(), vars["id"])
	if err != nil {
		switch err.(type) {
		case jupiterbrain.VirtualMachineNotFoundError:
			jsonapi.Error(w, err, http.StatusNotFound)
			return
		default:
			srv.log.WithFields(logrus.Fields{
				"err": err,
				"id":  vars["id"],
			}).Error("failed to fetch instance")
			jsonapi.Error(w, err, http.StatusInternalServerError)
			return
		}
	}

	response := map[string][]interface{}{
		"data": {MarshalInstance(instance)},
	}

	b, err := json.MarshalIndent(response, "", "  ")
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, string(b)+"\n")
}

func (srv *server) handleInstanceByIDTerminate(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	err := srv.i.Terminate(context.TODO(), vars["id"])
	if err != nil {
		switch err.(type) {
		case jupiterbrain.VirtualMachineNotFoundError:
			jsonapi.Error(w, err, http.StatusNotFound)
			return
		default:
			srv.log.WithFields(logrus.Fields{
				"err": err,
				"id":  vars["id"],
			}).Error("failed to terminate instance")
			jsonapi.Error(w, err, http.StatusInternalServerError)
			return
		}
	}

	err = srv.db.DestroyInstance(vars["id"])
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (srv *server) handleInstanceSync(w http.ResponseWriter, req *http.Request) {
	instances, err := srv.i.List(context.TODO())
	if err != nil {
		jsonapi.Error(w, err, http.StatusInternalServerError)
		return
	}

	for _, instance := range instances {
		instance.CreatedAt = time.Now().UTC()
		err = srv.db.SaveInstance(instance)
		if err != nil {
			srv.log.WithFields(logrus.Fields{
				"err": err,
				"id":  instance.ID,
			}).Warn("failed to save instance")
			continue
		}

		srv.log.WithField("id", instance.ID).Debug("synced instance")
	}

	w.WriteHeader(http.StatusNoContent)
}

func (srv *server) signalHandler() {
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGINT, syscall.SIGUSR1)
	for {
		select {
		case sig := <-signalChan:
			switch sig {
			case syscall.SIGTERM:
				srv.log.Info("Received SIGTERM, shutting down now.")
				os.Exit(0)
			case syscall.SIGINT:
				srv.log.Info("Received SIGINT, shutting down now.")
				os.Exit(0)
			case syscall.SIGUSR1:
				srv.log.WithFields(logrus.Fields{
					"version":   os.Getenv("VERSION"),
					"revision":  os.Getenv("REVISION"),
					"boot_time": srv.bootTime,
					"uptime":    time.Since(srv.bootTime),
				}).Info("Received SIGUSR1.")
			default:
				log.Print("ignoring unknown signal")
			}
		default:
			time.Sleep(time.Second)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
)

const (
	VERSION = "0.2.0"
)

var options struct {
	Path  string
	Host  string
	Port  int
	Token string
	Auth  bool
}

var services []Service

func initOptions() {
	flag.StringVar(&options.Path, "c", "", "Path to config directory")
	flag.StringVar(&options.Host, "h", "0.0.0.0", "Host to bind to")
	flag.IntVar(&options.Port, "p", 3050, "Port to listen on")
	flag.StringVar(&options.Token, "t", "", "Authentication token")

	flag.Parse()

	if options.Path == "" {
		options.Path = "./config"
	}

	// Load token from environment variable if not set
	if options.Token == "" {
		options.Token = os.Getenv("TOKEN")
	}

	// Do not require authentication if token is not set
	if options.Token == "" {
		options.Auth = false
	} else {
		options.Auth = true
	}
}

func main() {
	initOptions()

	var err error
	services, err = readServices(options.Path)

	if err != nil {
		fmt.Println("Error:", err.Error())
		os.Exit(1)
	}

	fmt.Printf("envd v%s\n", VERSION)
	fmt.Println("config path:", options.Path)
	fmt.Println("services detected:", len(services))

	startServer()
}
<commit_msg>Make config path mandatory<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
)

const (
	VERSION = "0.2.0"
)

var options struct {
	Path  string
	Host  string
	Port  int
	Token string
	Auth  bool
}

var services []Service

func initOptions() {
	flag.StringVar(&options.Path, "c", "", "Path to config directory")
	flag.StringVar(&options.Host, "h", "0.0.0.0", "Host to bind to")
	flag.IntVar(&options.Port, "p", 3050, "Port to listen on")
	flag.StringVar(&options.Token, "t", "", "Authentication token")

	flag.Parse()

	if options.Path == "" {
		fmt.Println("Please specify -c option")
		os.Exit(1)
	}

	// Load token from environment variable if not set
	if options.Token == "" {
		options.Token = os.Getenv("TOKEN")
	}

	// Do not require authentication if token is not set
	if options.Token == "" {
		options.Auth = false
	} else {
		options.Auth = true
	}
}

func main() {
	initOptions()

	var err error
	services, err = readServices(options.Path)

	if err != nil {
		fmt.Println("Error:", err.Error())
		os.Exit(1)
	}

	fmt.Printf("envd v%s\n", VERSION)
	fmt.Println("config path:", options.Path)
	fmt.Println("services detected:", len(services))

	startServer()
}
<|endoftext|>


<commit_before>package proto

import (
	"errors"
)

// A TransportData is the transport-layer protocol data unit (TPDU) within a L_Data frame.
type TransportData []byte

var (
	ErrTransportDataTooShort = errors.New("Given TPDU is too short")
)

// CheckTransportData validates the length of the given slice.
func CheckTransportData(data []byte) (TransportData, error) {
	if len(data) < 1 {
		return nil, ErrTransportDataTooShort
	}

	tpdu := TransportData(data)

	switch tpdu.ControlInfo() {
	case UnnumberedDataPacket, NumberedDataPacket:
		if len(data) < 2 {
			return nil, ErrTransportDataTooShort
		}
	}

	return tpdu, nil
}

// A TransportControlInfo is the transport-layer protocol control information (TPCI).
type TransportControlInfo uint8

const (
	UnnumberedDataPacket    TransportControlInfo = 0
	NumberedDataPacket      TransportControlInfo = 1
	UnnumberedControlPacket TransportControlInfo = 2
	NumberedControlPacket   TransportControlInfo = 3
)

// ControlInfo returns the type of packet in the TPDU.
func (tpdu TransportData) ControlInfo() TransportControlInfo {
	return TransportControlInfo((tpdu[0] >> 6) & 3)
}

// SeqNumber retrieves the sequence number.
func (tpdu TransportData) SeqNumber() uint8 {
	return (tpdu[0] >> 2) & 15
}

// Data parses the application-layer protocol data unit in order to provide control information
// and the actual data.
func (tpdu TransportData) Data() (AppControlInfo, []byte, error) {
	switch tpdu.ControlInfo() {
	case UnnumberedDataPacket, NumberedDataPacket:
		apci := AppControlInfo(((tpdu[0] & 3) << 2) | ((tpdu[1] >> 6) & 3))

		var data []byte

		if len(tpdu) > 2 {
			data = make([]byte, len(tpdu) - 2)
			copy(data, tpdu[2:])
		} else {
			data = make([]byte, 1)
			data[0] = tpdu[1] & 63
		}

		return apci, data, nil
	default:
		return 0, nil, errors.New("TransportControlInfo does not indicate a data packet")
	}
}

// An AppControlInfo is the application-layer protocol control information (APCI).
type AppControlInfo uint8

const (
	GroupValueRead         AppControlInfo = 0
	GroupValueResponse     AppControlInfo = 1
	GroupValueWrite        AppControlInfo = 2
	IndividualAddrWrite    AppControlInfo = 3
	IndividualAddrRequest  AppControlInfo = 4
	IndividualAddrResponse AppControlInfo = 5
	AdcRead                AppControlInfo = 6
	AdcResponse            AppControlInfo = 7
	MemoryRead             AppControlInfo = 8
	MemoryResponse         AppControlInfo = 9
	MemoryWrite            AppControlInfo = 10
	UserMessage            AppControlInfo = 11
	MaskVersionRead        AppControlInfo = 12
	MaskVersionResponse    AppControlInfo = 13
	Restart                AppControlInfo = 14
	Escape                 AppControlInfo = 15
)
<commit_msg>Rework TPDU names<commit_after>package proto

import (
	"errors"
)

// A TPDU is the transport-layer protocol data unit within a L_Data frame.
type TPDU []byte

var (
	ErrTransportDataTooShort = errors.New("Given TPDU is too short")
	ErrTransportNotData      = errors.New("TPCI does not indicate a data packet")
	ErrTransportNotControl   = errors.New("TPCI does not indicate a control packet")
)

// CheckTPDU validates the length of the given slice.
func CheckTPDU(data []byte) (TPDU, error) {
	if len(data) < 1 {
		return nil, ErrTransportDataTooShort
	}

	tpdu := TPDU(data)

	switch tpdu.PacketType() {
	case UnnumberedDataPacket, NumberedDataPacket:
		if len(data) < 2 {
			return nil, ErrTransportDataTooShort
		}
	}

	return tpdu, nil
}

// A TPCI is the transport-layer protocol control information (TPCI).
type TPCI uint8

const (
	UnnumberedDataPacket    TPCI = 0
	NumberedDataPacket      TPCI = 1
	UnnumberedControlPacket TPCI = 2
	NumberedControlPacket   TPCI = 3
)

// PacketType returns the type of packet in the TPDU.
func (tpdu TPDU) PacketType() TPCI {
	return TPCI((tpdu[0] >> 6) & 3)
}

// SeqNumber retrieves the sequence number.
func (tpdu TPDU) SeqNumber() uint8 {
	return (tpdu[0] >> 2) & 15
}

// AppData parses the application-layer protocol data unit in order to provide control information
// and the actual data.
func (tpdu TPDU) AppData() (APCI, []byte, error) {
	switch tpdu.PacketType() {
	case UnnumberedDataPacket, NumberedDataPacket:
		apci := APCI(((tpdu[0] & 3) << 2) | ((tpdu[1] >> 6) & 3))

		var data []byte

		if len(tpdu) > 2 {
			data = make([]byte, len(tpdu) - 2)
			copy(data, tpdu[2:])
		} else {
			data = make([]byte, 1)
			data[0] = tpdu[1] & 63
		}

		return apci, data, nil

	default:
		return 0, nil, ErrTransportNotData
	}
}

// ControlData retrieves the control data within the data unit.
func (tpdu TPDU) ControlData() (uint8, error) {
	switch tpdu.PacketType() {
	case UnnumberedControlPacket, NumberedControlPacket:
		return tpdu[0] & 3, nil

	default:
		return 0, ErrTransportNotControl
	}
}

// An APCI is the application-layer protocol control information (APCI).
type APCI uint8

const (
	GroupValueRead         APCI = 0
	GroupValueResponse     APCI = 1
	GroupValueWrite        APCI = 2
	IndividualAddrWrite    APCI = 3
	IndividualAddrRequest  APCI = 4
	IndividualAddrResponse APCI = 5
	AdcRead                APCI = 6
	AdcResponse            APCI = 7
	MemoryRead             APCI = 8
	MemoryResponse         APCI = 9
	MemoryWrite            APCI = 10
	UserMessage            APCI = 11
	MaskVersionRead        APCI = 12
	MaskVersionResponse    APCI = 13
	Restart                APCI = 14
	Escape                 APCI = 15
)
<|endoftext|>


<commit_before>package main

import (
	"github.com/dobrite/gusher/go/gusher"
	"log"
	"net/http"
	"os"
)

func main() {
	gmux := gusher.NewServeMux("/gusher")
	gmux.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("./public"))))
	gmux.HandleFunc("/", IndexHandler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "3000"
	}

	log.Println("Server started")
	log.Fatal(http.ListenAndServe(":"+port, gmux))
}

func IndexHandler(w http.ResponseWriter, req *http.Request) {
	http.ServeFile(w, req, "./public/index.html")
}
<commit_msg>more log format<commit_after>package main

import (
	"github.com/dobrite/gusher/go/gusher"
	"log"
	"net/http"
	"os"
)

func main() {
	gmux := gusher.NewServeMux("/gusher")
	gmux.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("./public"))))
	gmux.HandleFunc("/", IndexHandler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "3000"
	}

	log.Println("server started")
	log.Fatal(http.ListenAndServe(":"+port, gmux))
}

func IndexHandler(w http.ResponseWriter, req *http.Request) {
	http.ServeFile(w, req, "./public/index.html")
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/veandco/go-sdl2/sdl"
	"log"
)

func main() {
	// Initialize
	if sdl.Init(sdl.INIT_EVERYTHING) != 0 {
		log.Fatalf("SDL_Init Error: %s\n", sdl.GetError())
	}
	// Make sure to quit when the function returns
	defer sdl.Quit()

	// Prepare the window and load the image
	win := sdl.CreateWindow("Hello World!", 100, 100, 960, 540, sdl.WINDOW_SHOWN)
	defer win.Destroy()

	// Prepare a renderer for the window
	ren := sdl.CreateRenderer(win, -1, sdl.RENDERER_ACCELERATED|sdl.RENDERER_PRESENTVSYNC)
	defer ren.Destroy()

	// Load the image and prepare to use it as a texture
	bmp := sdl.LoadBMP("../img/boxes.bmp")
	tex := ren.CreateTextureFromSurface(bmp)
	defer tex.Destroy()

	// Show the texture
	ren.Clear()
	ren.Copy(tex, nil, nil)
	ren.Present()

	// Wait 2 seconds
	sdl.Delay(2000)
}
<commit_msg>Better error handling<commit_after>package main

import (
	"github.com/veandco/go-sdl2/sdl"
	"log"
)

// Note that the SDL2 package could have been more ideomatic by
// returning error values that could contain error messages instead
// of having to use "if == nil", "sdl.GetError()" and the .Destroy() functions.

func main() {
	// Initialize
	if sdl.Init(sdl.INIT_EVERYTHING) != 0 {
		log.Fatalf("Init Error: %s\n", sdl.GetError())
	}
	// Make sure to quit when the function returns
	defer sdl.Quit()

	// Create the window
	win := sdl.CreateWindow("Hello World!", 100, 100, 960, 540, sdl.WINDOW_SHOWN)
	if win == nil {
		log.Fatalf("CreateWindow Error: %s\n", sdl.GetError())
	}
	defer win.Destroy()

	// Create a renderer
	ren := sdl.CreateRenderer(win, -1, sdl.RENDERER_ACCELERATED|sdl.RENDERER_PRESENTVSYNC)
	if win == nil {
		log.Fatalf("CreateRenderer Error: %s\n", sdl.GetError())
	}
	defer ren.Destroy()

	// Load the image
	bmp := sdl.LoadBMP("../img/boxes.bmp")
	if bmp == nil {
		log.Fatalf("LoadBMP Error: %s\n", sdl.GetError())
	}

	// Use the image as a texture
	tex := ren.CreateTextureFromSurface(bmp)
	if tex == nil {
		log.Fatalf("CreateTextureFromSurface Error: %s\n", sdl.GetError())
	}
	defer tex.Destroy()

	// Show the texture
	ren.Clear()
	ren.Copy(tex, nil, nil)
	ren.Present()

	// Wait 2 seconds
	sdl.Delay(2000)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"

	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
		panic(fmt.Sprintf("%s: %s", msg, err))
	}
}

func main() {
	conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	q, err := ch.QueueDeclare(
		"hello", // name
		false,   // durable
		false,   // delete when unused
		false,   // exclusive
		false,   // no-wait
		nil,     // arguments
	)
	failOnError(err, "Failed to declare a queue")

	body := "hello"
	err = ch.Publish(
		"",     // exchange
		q.Name, // routing key
		false,  // mandatory
		false,  // immediate
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        []byte(body),
		})
  log.Printf(" [x] Sent %s", body)
	failOnError(err, "Failed to publish a message")
}
<commit_msg>proper go indentation<commit_after>package main

import (
	"fmt"
	"log"

	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
		panic(fmt.Sprintf("%s: %s", msg, err))
	}
}

func main() {
	conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	q, err := ch.QueueDeclare(
		"hello", // name
		false,   // durable
		false,   // delete when unused
		false,   // exclusive
		false,   // no-wait
		nil,     // arguments
	)
	failOnError(err, "Failed to declare a queue")

	body := "hello"
	err = ch.Publish(
		"",     // exchange
		q.Name, // routing key
		false,  // mandatory
		false,  // immediate
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        []byte(body),
		})
	log.Printf(" [x] Sent %s", body)
	failOnError(err, "Failed to publish a message")
}
<|endoftext|>