<commit_before>package server

import (
	"context"
	"flag"
	"fmt"
	net2 "net"
	"os"
	"path/filepath"
	"strings"

	"github.com/docker/docker/pkg/reexec"
	"github.com/natefinch/lumberjack"
	"github.com/pkg/errors"
	"github.com/rancher/k3s/pkg/agent"
	"github.com/rancher/k3s/pkg/cli/cmds"
	"github.com/rancher/k3s/pkg/server"
	"github.com/rancher/norman/signal"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	"k8s.io/apimachinery/pkg/util/net"

	_ "github.com/mattn/go-sqlite3" // ensure we have sqlite
)

func setupLogging(app *cli.Context) {
	if !app.GlobalBool("debug") {
		flag.Set("stderrthreshold", "3")
		flag.Set("alsologtostderr", "false")
		flag.Set("logtostderr", "false")
	}
}

func runWithLogging(app *cli.Context, cfg *cmds.Server) error {
	l := &lumberjack.Logger{
		Filename:   cfg.Log,
		MaxSize:    50,
		MaxBackups: 3,
		MaxAge:     28,
		Compress:   true,
	}

	args := append([]string{"k3s"}, os.Args[1:]...)
	cmd := reexec.Command(args...)
	cmd.Env = os.Environ()
	cmd.Env = append(cmd.Env, "_RIO_REEXEC_=true")
	cmd.Stderr = l
	cmd.Stdout = l
	cmd.Stdin = os.Stdin
	return cmd.Run()
}

func Run(app *cli.Context) error {
	return run(app, &cmds.ServerConfig)
}

func run(app *cli.Context, cfg *cmds.Server) error {
	var (
		err error
	)

	if cfg.Log != "" && os.Getenv("_RIO_REEXEC_") == "" {
		return runWithLogging(app, cfg)
	}

	setupLogging(app)

	if !cfg.DisableAgent && os.Getuid() != 0 {
		return fmt.Errorf("must run as root unless --disable-agent is specified")
	}

	serverConfig := server.Config{}
	serverConfig.ControlConfig.ClusterSecret = cfg.ClusterSecret
	serverConfig.ControlConfig.DataDir = cfg.DataDir
	serverConfig.ControlConfig.KubeConfigOutput = cfg.KubeConfigOutput
	serverConfig.ControlConfig.KubeConfigMode = cfg.KubeConfigMode
	serverConfig.TLSConfig.HTTPSPort = cfg.HTTPSPort
	serverConfig.TLSConfig.HTTPPort = cfg.HTTPPort
	serverConfig.TLSConfig.KnownIPs = knownIPs()

	_, serverConfig.ControlConfig.ClusterIPRange, err = net2.ParseCIDR(cfg.ClusterCIDR)
	if err != nil {
		return errors.Wrapf(err, "Invalid CIDR %s: %v", cfg.ClusterCIDR, err)
	}

	// TODO: support etcd
	serverConfig.ControlConfig.NoLeaderElect = true

	for _, noDeploy := range app.StringSlice("no-deploy") {
		if noDeploy == "servicelb" {
			serverConfig.DisableServiceLB = true
			continue
		}

		if !strings.HasSuffix(noDeploy, ".yaml") {
			noDeploy = noDeploy + ".yaml"
		}
		serverConfig.ControlConfig.Skips = append(serverConfig.ControlConfig.Skips, noDeploy)
	}

	logrus.Info("Starting k3s ", app.App.Version)
	ctx := signal.SigTermCancelContext(context.Background())
	certs, err := server.StartServer(ctx, &serverConfig)
	if err != nil {
		return err
	}

	logrus.Info("k3s is up and running")

	if cfg.DisableAgent {
		<-ctx.Done()
		return nil
	}

	url := fmt.Sprintf("https://localhost:%d", serverConfig.TLSConfig.HTTPSPort)
	token := server.FormatToken(serverConfig.ControlConfig.Runtime.NodeToken, certs)

	agentConfig := cmds.AgentConfig
	agentConfig.Debug = app.GlobalBool("bool")
	agentConfig.DataDir = filepath.Dir(serverConfig.ControlConfig.DataDir)
	agentConfig.ServerURL = url
	agentConfig.Token = token

	return agent.Run(ctx, agentConfig)
}

func knownIPs() []string {
	ips := []string{
		"127.0.0.1",
	}
	ip, err := net.ChooseHostInterface()
	if err == nil {
		ips = append(ips, ip.String())
	}
	return ips
}
<commit_msg>Enable systemd ready notification for k3s server<commit_after>package server

import (
	"context"
	"flag"
	"fmt"
	net2 "net"
	"os"
	"path/filepath"
	"strings"

	systemd "github.com/coreos/go-systemd/daemon"
	"github.com/docker/docker/pkg/reexec"
	"github.com/natefinch/lumberjack"
	"github.com/pkg/errors"
	"github.com/rancher/k3s/pkg/agent"
	"github.com/rancher/k3s/pkg/cli/cmds"
	"github.com/rancher/k3s/pkg/server"
	"github.com/rancher/norman/signal"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	"k8s.io/apimachinery/pkg/util/net"

	_ "github.com/mattn/go-sqlite3" // ensure we have sqlite
)

func setupLogging(app *cli.Context) {
	if !app.GlobalBool("debug") {
		flag.Set("stderrthreshold", "3")
		flag.Set("alsologtostderr", "false")
		flag.Set("logtostderr", "false")
	}
}

func runWithLogging(app *cli.Context, cfg *cmds.Server) error {
	l := &lumberjack.Logger{
		Filename:   cfg.Log,
		MaxSize:    50,
		MaxBackups: 3,
		MaxAge:     28,
		Compress:   true,
	}

	args := append([]string{"k3s"}, os.Args[1:]...)
	cmd := reexec.Command(args...)
	cmd.Env = os.Environ()
	cmd.Env = append(cmd.Env, "_RIO_REEXEC_=true")
	cmd.Stderr = l
	cmd.Stdout = l
	cmd.Stdin = os.Stdin
	return cmd.Run()
}

func Run(app *cli.Context) error {
	return run(app, &cmds.ServerConfig)
}

func run(app *cli.Context, cfg *cmds.Server) error {
	var (
		err error
	)

	if cfg.Log != "" && os.Getenv("_RIO_REEXEC_") == "" {
		return runWithLogging(app, cfg)
	}

	setupLogging(app)

	if !cfg.DisableAgent && os.Getuid() != 0 {
		return fmt.Errorf("must run as root unless --disable-agent is specified")
	}

	serverConfig := server.Config{}
	serverConfig.ControlConfig.ClusterSecret = cfg.ClusterSecret
	serverConfig.ControlConfig.DataDir = cfg.DataDir
	serverConfig.ControlConfig.KubeConfigOutput = cfg.KubeConfigOutput
	serverConfig.ControlConfig.KubeConfigMode = cfg.KubeConfigMode
	serverConfig.TLSConfig.HTTPSPort = cfg.HTTPSPort
	serverConfig.TLSConfig.HTTPPort = cfg.HTTPPort
	serverConfig.TLSConfig.KnownIPs = knownIPs()

	_, serverConfig.ControlConfig.ClusterIPRange, err = net2.ParseCIDR(cfg.ClusterCIDR)
	if err != nil {
		return errors.Wrapf(err, "Invalid CIDR %s: %v", cfg.ClusterCIDR, err)
	}

	// TODO: support etcd
	serverConfig.ControlConfig.NoLeaderElect = true

	for _, noDeploy := range app.StringSlice("no-deploy") {
		if noDeploy == "servicelb" {
			serverConfig.DisableServiceLB = true
			continue
		}

		if !strings.HasSuffix(noDeploy, ".yaml") {
			noDeploy = noDeploy + ".yaml"
		}
		serverConfig.ControlConfig.Skips = append(serverConfig.ControlConfig.Skips, noDeploy)
	}

	logrus.Info("Starting k3s ", app.App.Version)
	notifySocket := os.Getenv("NOTIFY_SOCKET")
	os.Unsetenv("NOTIFY_SOCKET")

	ctx := signal.SigTermCancelContext(context.Background())
	certs, err := server.StartServer(ctx, &serverConfig)
	if err != nil {
		return err
	}

	logrus.Info("k3s is up and running")
	if notifySocket != "" {
		os.Setenv("NOTIFY_SOCKET", notifySocket)
		systemd.SdNotify(true, "READY=1")
	}

	if cfg.DisableAgent {
		<-ctx.Done()
		return nil
	}

	url := fmt.Sprintf("https://localhost:%d", serverConfig.TLSConfig.HTTPSPort)
	token := server.FormatToken(serverConfig.ControlConfig.Runtime.NodeToken, certs)

	agentConfig := cmds.AgentConfig
	agentConfig.Debug = app.GlobalBool("bool")
	agentConfig.DataDir = filepath.Dir(serverConfig.ControlConfig.DataDir)
	agentConfig.ServerURL = url
	agentConfig.Token = token

	return agent.Run(ctx, agentConfig)
}

func knownIPs() []string {
	ips := []string{
		"127.0.0.1",
	}
	ip, err := net.ChooseHostInterface()
	if err == nil {
		ips = append(ips, ip.String())
	}
	return ips
}
<|endoftext|>


<commit_before>// +build darwin freebsd openbsd netbsd dragonfly windows

/*
 * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
 *
 * 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 cpu

import (
	"fmt"
	"runtime"
	"time"
)

func newCounter() (counter, error) {
	return counter{}, fmt.Errorf("cpu metrics not implemented for %s platform", runtime.GOOS)
}

func (c counter) now() time.Time {
	return time.Time{}
}
<commit_msg>cpu package fails to build on illumos (#9036)<commit_after>// +build !linux

/*
 * MinIO Cloud Storage, (C) 2019-2020 MinIO, Inc.
 *
 * 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 cpu

import (
	"fmt"
	"runtime"
	"time"
)

func newCounter() (counter, error) {
	return counter{}, fmt.Errorf("cpu metrics not implemented for %s platform", runtime.GOOS)
}

func (c counter) now() time.Time {
	return time.Time{}
}
<|endoftext|>


<commit_before>/*
Copyright © 2014–5 Brad Ackerman.

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 eveapi

import (
	"encoding/xml"
	"net/url"
	"strconv"

	"github.com/backerman/evego"
)

const (
	blueprintStr = "Blueprint"
	blueprintLen = len(blueprintStr)
)

type assetsList []evego.InventoryItem
type blueprintsList []evego.BlueprintItem

type assetsResponse struct {
	CurrentTime string     `xml:"currentTime"`
	Assets      assetsList `xml:"result>rowset>row"`
	CachedUntil string     `xml:"cachedUntil"`
}

type blueprintsResponse struct {
	CurrentTime string         `xml:"currentTime"`
	Blueprints  blueprintsList `xml:"result>rowset>row"`
	CachedUntil string         `xml:"cachedUntil"`
}

func (x *xmlAPI) processAssets(assets []evego.InventoryItem) error {
	for i := range assets {
		asset := &assets[i]
		thisAsset, err := x.db.ItemForID(asset.TypeID)
		if err != nil {
			return err
		}
		startIndex := len(thisAsset.Name) - blueprintLen
		var endOfName string
		if startIndex > 0 {
			endOfName = thisAsset.Name[startIndex:]
		}
		if endOfName != "Blueprint" {
			asset.BlueprintType = evego.NotBlueprint
		} else if !asset.Unpackaged {
			// This is a blueprint, but it's packaged, and therefore cannot be
			// a copy.
			asset.BlueprintType = evego.BlueprintOriginal
		}
		if asset.Quantity == 0 {
			// The default quantity is 1, and our default is 0, so fix.
			asset.Quantity = 1
		}
		if asset.Contents == nil {
			// No contents, but we want to make sure there's a slice here (rather than
			// just a nil) for consistency.
			asset.Contents = make([]evego.InventoryItem, 0, 0)
		} else if len(asset.Contents) > 0 {
			err = x.processAssets(asset.Contents)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (x *xmlAPI) Assets(key *evego.XMLKey, characterID int) ([]evego.InventoryItem, error) {
	params := url.Values{}
	params.Set("keyID", strconv.Itoa(key.KeyID))
	params.Set("characterID", strconv.Itoa(characterID))
	params.Set("vcode", key.VerificationCode)
	xmlBytes, err := x.get(characterAssets, params)
	if err != nil {
		return nil, err
	}
	var response assetsResponse
	xml.Unmarshal(xmlBytes, &response)
	assets := []evego.InventoryItem(response.Assets)
	err = x.processAssets(assets)
	return assets, err
}

func (x *xmlAPI) processBlueprints(blueprints []evego.BlueprintItem, assetsIn []evego.InventoryItem) error {
	// Set up our hash for mapping itemID to item.
	assets := make(map[int]*evego.InventoryItem)
	for _, item := range assetsIn {
		assets[item.ItemID] = &item
	}
	for i := range blueprints {
		bp := &blueprints[i]
		// Set flag for originalness
		if bp.NumRuns == -1 {
			bp.IsOriginal = true
		}
		// Demangle quantity
		if bp.Quantity < 0 {
			bp.Quantity = 1
		}
		// Identify location by walking the locationID attribute up to the point
		// where we don't have an asset with that ID, meaning that the ID belongs
		// to a station, outpost, or (?) solar system.
		containerID := bp.LocationID
		var foundTop bool
		for !foundTop {
			parent, found := assets[containerID]
			if found {
				// This item is inside a container.
				containerID = parent.LocationID
			} else {
				// This item is not inside a container.
				foundTop = true
			}
		}
		bp.StationID = containerID
	}
	return nil
}

func (x *xmlAPI) Blueprints(key *evego.XMLKey, characterID int) ([]evego.BlueprintItem, error) {
	params := url.Values{}
	params.Set("keyID", strconv.Itoa(key.KeyID))
	params.Set("characterID", strconv.Itoa(characterID))
	params.Set("vcode", key.VerificationCode)
	xmlBytes, err := x.get(characterBlueprints, params)
	if err != nil {
		return nil, err
	}
	var response blueprintsResponse
	xml.Unmarshal(xmlBytes, &response)
	blueprints := []evego.BlueprintItem(response.Blueprints)
	// To process the blueprints, we also need the assets list so that we can
	// identify containers' locations.
	assets, err := x.Assets(key, characterID)
	if err != nil {
		return nil, err
	}
	err = x.processBlueprints(blueprints, assets)
	return blueprints, err
}
<commit_msg>Improved some error reporting.<commit_after>/*
Copyright © 2014–5 Brad Ackerman.

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 eveapi

import (
	"database/sql"
	"encoding/xml"
	"fmt"
	"net/url"
	"strconv"

	"github.com/backerman/evego"
)

const (
	blueprintStr = "Blueprint"
	blueprintLen = len(blueprintStr)
)

type assetsList []evego.InventoryItem
type blueprintsList []evego.BlueprintItem

type assetsResponse struct {
	CurrentTime string     `xml:"currentTime"`
	Assets      assetsList `xml:"result>rowset>row"`
	CachedUntil string     `xml:"cachedUntil"`
}

type blueprintsResponse struct {
	CurrentTime string         `xml:"currentTime"`
	Blueprints  blueprintsList `xml:"result>rowset>row"`
	CachedUntil string         `xml:"cachedUntil"`
}

func (x *xmlAPI) processAssets(assets []evego.InventoryItem) error {
	for i := range assets {
		asset := &assets[i]
		thisAsset, err := x.db.ItemForID(asset.TypeID)
		if err != nil {
			if err == sql.ErrNoRows {
				return fmt.Errorf("Unable to identify item with type ID %v: %+v", asset.TypeID, asset)
			}
			return err
		}
		startIndex := len(thisAsset.Name) - blueprintLen
		var endOfName string
		if startIndex > 0 {
			endOfName = thisAsset.Name[startIndex:]
		}
		if endOfName != "Blueprint" {
			asset.BlueprintType = evego.NotBlueprint
		} else if !asset.Unpackaged {
			// This is a blueprint, but it's packaged, and therefore cannot be
			// a copy.
			asset.BlueprintType = evego.BlueprintOriginal
		}
		if asset.Quantity == 0 {
			// The default quantity is 1, and our default is 0, so fix.
			asset.Quantity = 1
		}
		if asset.Contents == nil {
			// No contents, but we want to make sure there's a slice here (rather than
			// just a nil) for consistency.
			asset.Contents = make([]evego.InventoryItem, 0, 0)
		} else if len(asset.Contents) > 0 {
			err = x.processAssets(asset.Contents)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (x *xmlAPI) Assets(key *evego.XMLKey, characterID int) ([]evego.InventoryItem, error) {
	params := url.Values{}
	params.Set("keyID", strconv.Itoa(key.KeyID))
	params.Set("characterID", strconv.Itoa(characterID))
	params.Set("vcode", key.VerificationCode)
	xmlBytes, err := x.get(characterAssets, params)
	if err != nil {
		return nil, err
	}
	var response assetsResponse
	xml.Unmarshal(xmlBytes, &response)
	assets := []evego.InventoryItem(response.Assets)
	err = x.processAssets(assets)
	return assets, err
}

func (x *xmlAPI) processBlueprints(blueprints []evego.BlueprintItem, assetsIn []evego.InventoryItem) error {
	// Set up our hash for mapping itemID to item.
	assets := make(map[int]*evego.InventoryItem)
	for _, item := range assetsIn {
		assets[item.ItemID] = &item
	}
	for i := range blueprints {
		bp := &blueprints[i]
		// Set flag for originalness
		if bp.NumRuns == -1 {
			bp.IsOriginal = true
		}
		// Demangle quantity
		if bp.Quantity < 0 {
			bp.Quantity = 1
		}
		// Identify location by walking the locationID attribute up to the point
		// where we don't have an asset with that ID, meaning that the ID belongs
		// to a station, outpost, or (?) solar system.
		containerID := bp.LocationID
		var foundTop bool
		for !foundTop {
			parent, found := assets[containerID]
			if found {
				// This item is inside a container.
				containerID = parent.LocationID
			} else {
				// This item is not inside a container.
				foundTop = true
			}
		}
		bp.StationID = containerID
	}
	return nil
}

func (x *xmlAPI) Blueprints(key *evego.XMLKey, characterID int) ([]evego.BlueprintItem, error) {
	params := url.Values{}
	params.Set("keyID", strconv.Itoa(key.KeyID))
	params.Set("characterID", strconv.Itoa(characterID))
	params.Set("vcode", key.VerificationCode)
	xmlBytes, err := x.get(characterBlueprints, params)
	if err != nil {
		return nil, err
	}
	var response blueprintsResponse
	xml.Unmarshal(xmlBytes, &response)
	blueprints := []evego.BlueprintItem(response.Blueprints)
	// To process the blueprints, we also need the assets list so that we can
	// identify containers' locations.
	assets, err := x.Assets(key, characterID)
	if err != nil {
		return nil, err
	}
	err = x.processBlueprints(blueprints, assets)
	return blueprints, err
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

// Watches etcd and gets the full configuration on preset intervals.
// It expects the list of exposed services to live under:
// registry/services
// which in etcd is exposed like so:
// http://<etcd server>/v2/keys/registry/services
//
// The port that proxy needs to listen in for each service is a value in:
// registry/services/<service>
//
// The endpoints for each of the services found is a json string
// representing that service at:
// /registry/services/<service>/endpoint
// and the format is:
// '[ { "machine": <host>, "name": <name", "port": <port> },
//    { "machine": <host2>, "name": <name2", "port": <port2> }
//  ]',

package config

import (
	"fmt"
	"strings"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
	"github.com/coreos/go-etcd/etcd"
	"github.com/golang/glog"
)

// registryRoot is the key prefix for service configs in etcd.
const registryRoot = "registry/services"

// ConfigSourceEtcd communicates with a etcd via the client, and sends the change notification of services and endpoints to the specified channels.
type ConfigSourceEtcd struct {
	client           *etcd.Client
	serviceChannel   chan ServiceUpdate
	endpointsChannel chan EndpointsUpdate
	interval         time.Duration
}

// NewConfigSourceEtcd creates a new ConfigSourceEtcd and immediately runs the created ConfigSourceEtcd in a goroutine.
func NewConfigSourceEtcd(client *etcd.Client, serviceChannel chan ServiceUpdate, endpointsChannel chan EndpointsUpdate) ConfigSourceEtcd {
	config := ConfigSourceEtcd{
		client:           client,
		serviceChannel:   serviceChannel,
		endpointsChannel: endpointsChannel,
		interval:         2 * time.Second,
	}
	go config.Run()
	return config
}

// Run begins watching for new services and their endpoints on etcd.
func (s ConfigSourceEtcd) Run() {
	// Initially, just wait for the etcd to come up before doing anything more complicated.
	var services []api.Service
	var endpoints []api.Endpoints
	var err error
	for {
		services, endpoints, err = s.GetServices()
		if err == nil {
			break
		}
		glog.V(1).Infof("Failed to get any services: %v", err)
		time.Sleep(s.interval)
	}

	if len(services) > 0 {
		serviceUpdate := ServiceUpdate{Op: SET, Services: services}
		s.serviceChannel <- serviceUpdate
	}
	if len(endpoints) > 0 {
		endpointsUpdate := EndpointsUpdate{Op: SET, Endpoints: endpoints}
		s.endpointsChannel <- endpointsUpdate
	}

	// Ok, so we got something back from etcd. Let's set up a watch for new services, and
	// their endpoints
	go s.WatchForChanges()

	for {
		services, endpoints, err = s.GetServices()
		if err != nil {
			glog.Errorf("ConfigSourceEtcd: Failed to get services: %v", err)
		} else {
			if len(services) > 0 {
				serviceUpdate := ServiceUpdate{Op: SET, Services: services}
				s.serviceChannel <- serviceUpdate
			}
			if len(endpoints) > 0 {
				endpointsUpdate := EndpointsUpdate{Op: SET, Endpoints: endpoints}
				s.endpointsChannel <- endpointsUpdate
			}
		}
		time.Sleep(30 * time.Second)
	}
}

// GetServices finds the list of services and their endpoints from etcd.
// This operation is akin to a set a known good at regular intervals.
func (s ConfigSourceEtcd) GetServices() ([]api.Service, []api.Endpoints, error) {
	response, err := s.client.Get(registryRoot+"/specs", true, false)
	if err != nil {
		glog.V(1).Infof("Failed to get the key %s: %v", registryRoot, err)
		if tools.IsEtcdNotFound(err) {
			return []api.Service{}, []api.Endpoints{}, err
		}
	}
	if response.Node.Dir == true {
		retServices := make([]api.Service, len(response.Node.Nodes))
		retEndpoints := make([]api.Endpoints, len(response.Node.Nodes))
		// Ok, so we have directories, this list should be the list
		// of services. Find the local port to listen on and remote endpoints
		// and create a Service entry for it.
		for i, node := range response.Node.Nodes {
			var svc api.Service
			err = api.DecodeInto([]byte(node.Value), &svc)
			if err != nil {
				glog.Errorf("Failed to load Service: %s (%#v)", node.Value, err)
				continue
			}
			retServices[i] = svc
			endpoints, err := s.GetEndpoints(svc.ID)
			if err != nil {
				if tools.IsEtcdNotFound(err) {
					glog.V(1).Infof("Unable to get endpoints for %s : %v", svc.ID, err)
				}
				glog.Errorf("Couldn't get endpoints for %s : %v skipping", svc.ID, err)
				endpoints = api.Endpoints{}
			} else {
				glog.Infof("Got service: %s on localport %d mapping to: %s", svc.ID, svc.Port, endpoints)
			}
			retEndpoints[i] = endpoints
		}
		return retServices, retEndpoints, err
	}
	return nil, nil, fmt.Errorf("did not get the root of the registry %s", registryRoot)
}

// GetEndpoints finds the list of endpoints of the service from etcd.
func (s ConfigSourceEtcd) GetEndpoints(service string) (api.Endpoints, error) {
	key := fmt.Sprintf(registryRoot + "/endpoints/" + service)
	response, err := s.client.Get(key, true, false)
	if err != nil {
		glog.Errorf("Failed to get the key: %s %v", key, err)
		return api.Endpoints{}, err
	}
	// Parse all the endpoint specifications in this value.
	var e api.Endpoints
	err = api.DecodeInto([]byte(response.Node.Value), &e)
	return e, err
}

// etcdResponseToService takes an etcd response and pulls it apart to find service.
func etcdResponseToService(response *etcd.Response) (*api.Service, error) {
	if response.Node == nil {
		return nil, fmt.Errorf("invalid response from etcd: %#v", response)
	}
	var svc api.Service
	err := api.DecodeInto([]byte(response.Node.Value), &svc)
	if err != nil {
		return nil, err
	}
	return &svc, err
}

func (s ConfigSourceEtcd) WatchForChanges() {
	glog.Info("Setting up a watch for new services")
	watchChannel := make(chan *etcd.Response)
	go s.client.Watch("/registry/services/", 0, true, watchChannel, nil)
	for {
		watchResponse := <-watchChannel
		s.ProcessChange(watchResponse)
	}
}

func (s ConfigSourceEtcd) ProcessChange(response *etcd.Response) {
	glog.Infof("Processing a change in service configuration... %s", *response)

	// If it's a new service being added (signified by a localport being added)
	// then process it as such
	if strings.Contains(response.Node.Key, "/endpoints/") {
		s.ProcessEndpointResponse(response)
	} else if response.Action == "set" {
		service, err := etcdResponseToService(response)
		if err != nil {
			glog.Errorf("Failed to parse %s Port: %s", response, err)
			return
		}

		glog.Infof("New service added/updated: %#v", service)
		serviceUpdate := ServiceUpdate{Op: ADD, Services: []api.Service{*service}}
		s.serviceChannel <- serviceUpdate
		return
	}
	if response.Action == "delete" {
		parts := strings.Split(response.Node.Key[1:], "/")
		if len(parts) == 4 {
			glog.Infof("Deleting service: %s", parts[3])
			serviceUpdate := ServiceUpdate{Op: REMOVE, Services: []api.Service{{JSONBase: api.JSONBase{ID: parts[3]}}}}
			s.serviceChannel <- serviceUpdate
			return
		}
		glog.Infof("Unknown service delete: %#v", parts)
	}
}

func (s ConfigSourceEtcd) ProcessEndpointResponse(response *etcd.Response) {
	glog.Infof("Processing a change in endpoint configuration... %s", *response)
	var endpoints api.Endpoints
	err := api.DecodeInto([]byte(response.Node.Value), &endpoints)
	if err != nil {
		glog.Errorf("Failed to parse service out of etcd key: %v : %+v", response.Node.Value, err)
		return
	}
	endpointsUpdate := EndpointsUpdate{Op: ADD, Endpoints: []api.Endpoints{endpoints}}
	s.endpointsChannel <- endpointsUpdate
}
<commit_msg>Etcd can close the watch channel for services<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

// Watches etcd and gets the full configuration on preset intervals.
// It expects the list of exposed services to live under:
// registry/services
// which in etcd is exposed like so:
// http://<etcd server>/v2/keys/registry/services
//
// The port that proxy needs to listen in for each service is a value in:
// registry/services/<service>
//
// The endpoints for each of the services found is a json string
// representing that service at:
// /registry/services/<service>/endpoint
// and the format is:
// '[ { "machine": <host>, "name": <name", "port": <port> },
//    { "machine": <host2>, "name": <name2", "port": <port2> }
//  ]',

package config

import (
	"fmt"
	"strings"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/coreos/go-etcd/etcd"
	"github.com/golang/glog"
)

// registryRoot is the key prefix for service configs in etcd.
const registryRoot = "registry/services"

// ConfigSourceEtcd communicates with a etcd via the client, and sends the change notification of services and endpoints to the specified channels.
type ConfigSourceEtcd struct {
	client           *etcd.Client
	serviceChannel   chan ServiceUpdate
	endpointsChannel chan EndpointsUpdate
	interval         time.Duration
}

// NewConfigSourceEtcd creates a new ConfigSourceEtcd and immediately runs the created ConfigSourceEtcd in a goroutine.
func NewConfigSourceEtcd(client *etcd.Client, serviceChannel chan ServiceUpdate, endpointsChannel chan EndpointsUpdate) ConfigSourceEtcd {
	config := ConfigSourceEtcd{
		client:           client,
		serviceChannel:   serviceChannel,
		endpointsChannel: endpointsChannel,
		interval:         2 * time.Second,
	}
	go config.Run()
	return config
}

// Run begins watching for new services and their endpoints on etcd.
func (s ConfigSourceEtcd) Run() {
	// Initially, just wait for the etcd to come up before doing anything more complicated.
	var services []api.Service
	var endpoints []api.Endpoints
	var err error
	for {
		services, endpoints, err = s.GetServices()
		if err == nil {
			break
		}
		glog.V(1).Infof("Failed to get any services: %v", err)
		time.Sleep(s.interval)
	}

	if len(services) > 0 {
		serviceUpdate := ServiceUpdate{Op: SET, Services: services}
		s.serviceChannel <- serviceUpdate
	}
	if len(endpoints) > 0 {
		endpointsUpdate := EndpointsUpdate{Op: SET, Endpoints: endpoints}
		s.endpointsChannel <- endpointsUpdate
	}

	// Ok, so we got something back from etcd. Let's set up a watch for new services, and
	// their endpoints
	go util.Forever(s.WatchForChanges, 1*time.Second)

	for {
		services, endpoints, err = s.GetServices()
		if err != nil {
			glog.Errorf("ConfigSourceEtcd: Failed to get services: %v", err)
		} else {
			if len(services) > 0 {
				serviceUpdate := ServiceUpdate{Op: SET, Services: services}
				s.serviceChannel <- serviceUpdate
			}
			if len(endpoints) > 0 {
				endpointsUpdate := EndpointsUpdate{Op: SET, Endpoints: endpoints}
				s.endpointsChannel <- endpointsUpdate
			}
		}
		time.Sleep(30 * time.Second)
	}
}

// GetServices finds the list of services and their endpoints from etcd.
// This operation is akin to a set a known good at regular intervals.
func (s ConfigSourceEtcd) GetServices() ([]api.Service, []api.Endpoints, error) {
	response, err := s.client.Get(registryRoot+"/specs", true, false)
	if err != nil {
		glog.V(1).Infof("Failed to get the key %s: %v", registryRoot, err)
		if tools.IsEtcdNotFound(err) {
			return []api.Service{}, []api.Endpoints{}, err
		}
	}
	if response.Node.Dir == true {
		retServices := make([]api.Service, len(response.Node.Nodes))
		retEndpoints := make([]api.Endpoints, len(response.Node.Nodes))
		// Ok, so we have directories, this list should be the list
		// of services. Find the local port to listen on and remote endpoints
		// and create a Service entry for it.
		for i, node := range response.Node.Nodes {
			var svc api.Service
			err = api.DecodeInto([]byte(node.Value), &svc)
			if err != nil {
				glog.Errorf("Failed to load Service: %s (%#v)", node.Value, err)
				continue
			}
			retServices[i] = svc
			endpoints, err := s.GetEndpoints(svc.ID)
			if err != nil {
				if tools.IsEtcdNotFound(err) {
					glog.V(1).Infof("Unable to get endpoints for %s : %v", svc.ID, err)
				}
				glog.Errorf("Couldn't get endpoints for %s : %v skipping", svc.ID, err)
				endpoints = api.Endpoints{}
			} else {
				glog.Infof("Got service: %s on localport %d mapping to: %s", svc.ID, svc.Port, endpoints)
			}
			retEndpoints[i] = endpoints
		}
		return retServices, retEndpoints, err
	}
	return nil, nil, fmt.Errorf("did not get the root of the registry %s", registryRoot)
}

// GetEndpoints finds the list of endpoints of the service from etcd.
func (s ConfigSourceEtcd) GetEndpoints(service string) (api.Endpoints, error) {
	key := fmt.Sprintf(registryRoot + "/endpoints/" + service)
	response, err := s.client.Get(key, true, false)
	if err != nil {
		glog.Errorf("Failed to get the key: %s %v", key, err)
		return api.Endpoints{}, err
	}
	// Parse all the endpoint specifications in this value.
	var e api.Endpoints
	err = api.DecodeInto([]byte(response.Node.Value), &e)
	return e, err
}

// etcdResponseToService takes an etcd response and pulls it apart to find service.
func etcdResponseToService(response *etcd.Response) (*api.Service, error) {
	if response.Node == nil {
		return nil, fmt.Errorf("invalid response from etcd: %#v", response)
	}
	var svc api.Service
	err := api.DecodeInto([]byte(response.Node.Value), &svc)
	if err != nil {
		return nil, err
	}
	return &svc, err
}

func (s ConfigSourceEtcd) WatchForChanges() {
	glog.Info("Setting up a watch for new services")
	watchChannel := make(chan *etcd.Response)
	go s.client.Watch("/registry/services/", 0, true, watchChannel, nil)
	for {
		watchResponse, ok := <-watchChannel
		if !ok {
			break
		}
		s.ProcessChange(watchResponse)
	}
}

func (s ConfigSourceEtcd) ProcessChange(response *etcd.Response) {
	glog.Infof("Processing a change in service configuration... %s", *response)

	// If it's a new service being added (signified by a localport being added)
	// then process it as such
	if strings.Contains(response.Node.Key, "/endpoints/") {
		s.ProcessEndpointResponse(response)
	} else if response.Action == "set" {
		service, err := etcdResponseToService(response)
		if err != nil {
			glog.Errorf("Failed to parse %s Port: %s", response, err)
			return
		}

		glog.Infof("New service added/updated: %#v", service)
		serviceUpdate := ServiceUpdate{Op: ADD, Services: []api.Service{*service}}
		s.serviceChannel <- serviceUpdate
		return
	}
	if response.Action == "delete" {
		parts := strings.Split(response.Node.Key[1:], "/")
		if len(parts) == 4 {
			glog.Infof("Deleting service: %s", parts[3])
			serviceUpdate := ServiceUpdate{Op: REMOVE, Services: []api.Service{{JSONBase: api.JSONBase{ID: parts[3]}}}}
			s.serviceChannel <- serviceUpdate
			return
		}
		glog.Infof("Unknown service delete: %#v", parts)
	}
}

func (s ConfigSourceEtcd) ProcessEndpointResponse(response *etcd.Response) {
	glog.Infof("Processing a change in endpoint configuration... %s", *response)
	var endpoints api.Endpoints
	err := api.DecodeInto([]byte(response.Node.Value), &endpoints)
	if err != nil {
		glog.Errorf("Failed to parse service out of etcd key: %v : %+v", response.Node.Value, err)
		return
	}
	endpointsUpdate := EndpointsUpdate{Op: ADD, Endpoints: []api.Endpoints{endpoints}}
	s.endpointsChannel <- endpointsUpdate
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

// RoundRobin Loadbalancer

package proxy

import (
	"errors"
	"net"
	"reflect"
	"strconv"
	"strings"
	"sync"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/golang/glog"
)

type LoadBalancerRR struct {
	lock         sync.RWMutex
	endpointsMap map[string][]string
	rrIndex      map[string]int
}

func NewLoadBalancerRR() *LoadBalancerRR {
	return &LoadBalancerRR{endpointsMap: make(map[string][]string), rrIndex: make(map[string]int)}
}

func (impl LoadBalancerRR) LoadBalance(service string, srcAddr net.Addr) (string, error) {
	impl.lock.RLock()
	endpoints, exists := impl.endpointsMap[service]
	index := impl.rrIndex[service]
	impl.lock.RUnlock()
	if exists == false {
		return "", errors.New("no service entry for:" + service)
	}
	if len(endpoints) == 0 {
		return "", errors.New("no endpoints for: " + service)
	}
	endpoint := endpoints[index]
	impl.rrIndex[service] = (index + 1) % len(endpoints)
	return endpoint, nil
}

func (impl LoadBalancerRR) IsValid(spec string) bool {
	index := strings.Index(spec, ":")
	if index == -1 {
		return false
	}
	value, err := strconv.Atoi(spec[index+1:])
	if err != nil {
		return false
	}
	return value > 0
}

func (impl LoadBalancerRR) FilterValidEndpoints(endpoints []string) []string {
	var result []string
	for _, spec := range endpoints {
		if impl.IsValid(spec) {
			result = append(result, spec)
		}
	}
	return result
}

func (impl LoadBalancerRR) OnUpdate(endpoints []api.Endpoints) {
	tmp := make(map[string]bool)
	impl.lock.Lock()
	defer impl.lock.Unlock()
	// First update / add all new endpoints for services.
	for _, value := range endpoints {
		existingEndpoints, exists := impl.endpointsMap[value.Name]
		if !exists || !reflect.DeepEqual(value.Endpoints, existingEndpoints) {
			glog.Infof("LoadBalancerRR: Setting endpoints for %s to %+v", value.Name, value.Endpoints)
			impl.endpointsMap[value.Name] = impl.FilterValidEndpoints(value.Endpoints)
			// Start RR from the beginning if added or updated.
			impl.rrIndex[value.Name] = 0
		}
		tmp[value.Name] = true
	}
	// Then remove any endpoints no longer relevant
	for key, value := range impl.endpointsMap {
		_, exists := tmp[key]
		if !exists {
			glog.Infof("LoadBalancerRR: Removing endpoints for %s -> %+v", key, value)
			delete(impl.endpointsMap, key)
		}
	}
}
<commit_msg>pkg/proxy: filtering before comparing<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

// RoundRobin Loadbalancer

package proxy

import (
	"errors"
	"net"
	"reflect"
	"strconv"
	"strings"
	"sync"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/golang/glog"
)

type LoadBalancerRR struct {
	lock         sync.RWMutex
	endpointsMap map[string][]string
	rrIndex      map[string]int
}

func NewLoadBalancerRR() *LoadBalancerRR {
	return &LoadBalancerRR{endpointsMap: make(map[string][]string), rrIndex: make(map[string]int)}
}

func (impl LoadBalancerRR) LoadBalance(service string, srcAddr net.Addr) (string, error) {
	impl.lock.RLock()
	endpoints, exists := impl.endpointsMap[service]
	index := impl.rrIndex[service]
	impl.lock.RUnlock()
	if exists == false {
		return "", errors.New("no service entry for:" + service)
	}
	if len(endpoints) == 0 {
		return "", errors.New("no endpoints for: " + service)
	}
	endpoint := endpoints[index]
	impl.rrIndex[service] = (index + 1) % len(endpoints)
	return endpoint, nil
}

func (impl LoadBalancerRR) IsValid(spec string) bool {
	index := strings.Index(spec, ":")
	if index == -1 {
		return false
	}
	value, err := strconv.Atoi(spec[index+1:])
	if err != nil {
		return false
	}
	return value > 0
}

func (impl LoadBalancerRR) FilterValidEndpoints(endpoints []string) []string {
	var result []string
	for _, spec := range endpoints {
		if impl.IsValid(spec) {
			result = append(result, spec)
		}
	}
	return result
}

func (impl LoadBalancerRR) OnUpdate(endpoints []api.Endpoints) {
	tmp := make(map[string]bool)
	impl.lock.Lock()
	defer impl.lock.Unlock()
	// First update / add all new endpoints for services.
	for _, value := range endpoints {
		existingEndpoints, exists := impl.endpointsMap[value.Name]
		validEndpoints := impl.FilterValidEndpoints(value.Endpoints)
		if !exists || !reflect.DeepEqual(existingEndpoints, validEndpoints) {
			glog.Infof("LoadBalancerRR: Setting endpoints for %s to %+v", value.Name, value.Endpoints)
			impl.endpointsMap[value.Name] = validEndpoints
			// Start RR from the beginning if added or updated.
			impl.rrIndex[value.Name] = 0
		}
		tmp[value.Name] = true
	}
	// Then remove any endpoints no longer relevant
	for key, value := range impl.endpointsMap {
		_, exists := tmp[key]
		if !exists {
			glog.Infof("LoadBalancerRR: Removing endpoints for %s -> %+v", key, value)
			delete(impl.endpointsMap, key)
		}
	}
}
<|endoftext|>


<commit_before>package sharings

import (
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/utils"
	"github.com/cozy/cozy-stack/web/jsonapi"
)

// Sharing contains all the information about a sharing
type Sharing struct {
	SID         string `json:"_id,omitempty"`
	SRev        string `json:"_rev,omitempty"`
	Type        string `json:"type,omitempty"`
	Owner       bool   `json:"owner"`
	Desc        string `json:"desc,omitempty"`
	SharingID   string `json:"sharing_id,omitempty"`
	SharingType string `json:"sharing_type"`

	Permissions *permissions.Set    `json:"permissions,omitempty"`
	SRecipients []*SharingRecipient `json:"recipients,omitempty"`
}

// SharingRecipient contains the information about a recipient for a sharing
type SharingRecipient struct {
	Status       string `json:"status,omitempty"`
	AccessToken  string `json:"status,omitempty"`
	RefreshToken string `json:"status,omitempty"`

	RefRecipient jsonapi.ResourceIdentifier `json:"recipient,omitempty"`

	recipient *Recipient
}

// ID returns the sharing qualified identifier
func (s *Sharing) ID() string { return s.SID }

// Rev returns the sharing revision
func (s *Sharing) Rev() string { return s.SRev }

// DocType returns the sharing document type
func (s *Sharing) DocType() string { return consts.Sharings }

// SetID changes the sharing qualified identifier
func (s *Sharing) SetID(id string) { s.SID = id }

// SetRev changes the sharing revision
func (s *Sharing) SetRev(rev string) { s.SRev = rev }

// Links implements jsonapi.Doc
func (s *Sharing) Links() *jsonapi.LinksList {
	return &jsonapi.LinksList{Self: "/sharing/" + s.SID}
}

// Recipients returns the sharing recipients
func (s *Sharing) Recipients(db couchdb.Database) ([]*SharingRecipient, error) {
	var sRecipients []*SharingRecipient

	for _, sRec := range s.SRecipients {
		recipient, err := GetRecipient(db, sRec.RefRecipient.ID)
		if err != nil {
			return nil, err
		}
		sRec.recipient = recipient
		sRecipients = append(sRecipients, sRec)
	}

	s.SRecipients = sRecipients
	return sRecipients, nil
}

// Relationships is part of the jsonapi.Object interface
// It is used to generate the recipients relationships
func (s *Sharing) Relationships() jsonapi.RelationshipMap {
	l := len(s.SRecipients)
	i := 0

	data := make([]jsonapi.ResourceIdentifier, l)
	for _, rec := range s.SRecipients {
		r := rec.recipient
		data[i] = jsonapi.ResourceIdentifier{ID: r.ID(), Type: r.DocType()}
		i++
	}
	contents := jsonapi.Relationship{Data: data}
	return jsonapi.RelationshipMap{"recipients": contents}
}

// Included is part of the jsonapi.Object interface
func (s *Sharing) Included() []jsonapi.Object {
	var included []jsonapi.Object
	for _, rec := range s.SRecipients {
		r := rec.recipient
		included = append(included, r)
	}
	return included
}

// GetRecipient returns the Recipient stored in database from a given ID
func GetRecipient(db couchdb.Database, recID string) (*Recipient, error) {
	doc := &Recipient{}
	err := couchdb.GetDoc(db, consts.Recipients, recID, doc)
	if couchdb.IsNotFoundError(err) {
		err = ErrRecipientDoesNotExist
	}
	return doc, err
}

// CheckSharingCreation initializes and check some sharing fields at creation
func CheckSharingCreation(db couchdb.Database, sharing *Sharing) error {

	sharingType := sharing.SharingType
	if sharingType != consts.OneShotSharing &&
		sharingType != consts.MasterSlaveSharing &&
		sharingType != consts.MasterMasterSharing {
		return ErrBadSharingType
	}

	sRecipients, err := sharing.Recipients(db)
	if err != nil {
		return err
	}
	for _, sRec := range sRecipients {
		sRec.Status = consts.PendingSharingStatus
	}

	sharing.Owner = true
	sharing.SharingID = utils.RandomString(32)

	return nil
}

// Create inserts a Sharing document in database
func Create(db couchdb.Database, doc *Sharing) error {
	err := couchdb.CreateDoc(db, doc)
	if err != nil {
		return err
	}
	return nil
}

var (
	_ couchdb.Doc    = &Sharing{}
	_ jsonapi.Object = &Sharing{}
)
<commit_msg>Fix JSON and lint<commit_after>package sharings

import (
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/utils"
	"github.com/cozy/cozy-stack/web/jsonapi"
)

// Sharing contains all the information about a sharing
type Sharing struct {
	SID         string `json:"_id,omitempty"`
	SRev        string `json:"_rev,omitempty"`
	Type        string `json:"type,omitempty"`
	Owner       bool   `json:"owner"`
	Desc        string `json:"desc,omitempty"`
	SharingID   string `json:"sharing_id,omitempty"`
	SharingType string `json:"sharing_type"`

	Permissions *permissions.Set    `json:"permissions,omitempty"`
	SRecipients []*SharingRecipient `json:"recipients,omitempty"`
}

// SharingRecipient contains the information about a recipient for a sharing
type SharingRecipient struct {
	Status       string `json:"status,omitempty"`
	AccessToken  string `json:"access_token,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`

	RefRecipient jsonapi.ResourceIdentifier `json:"recipient,omitempty"`

	recipient *Recipient
}

// ID returns the sharing qualified identifier
func (s *Sharing) ID() string { return s.SID }

// Rev returns the sharing revision
func (s *Sharing) Rev() string { return s.SRev }

// DocType returns the sharing document type
func (s *Sharing) DocType() string { return consts.Sharings }

// SetID changes the sharing qualified identifier
func (s *Sharing) SetID(id string) { s.SID = id }

// SetRev changes the sharing revision
func (s *Sharing) SetRev(rev string) { s.SRev = rev }

// Links implements jsonapi.Doc
func (s *Sharing) Links() *jsonapi.LinksList {
	return &jsonapi.LinksList{Self: "/sharing/" + s.SID}
}

// Recipients returns the sharing recipients
func (s *Sharing) Recipients(db couchdb.Database) ([]*SharingRecipient, error) {
	var sRecipients []*SharingRecipient

	for _, sRec := range s.SRecipients {
		recipient, err := GetRecipient(db, sRec.RefRecipient.ID)
		if err != nil {
			return nil, err
		}
		sRec.recipient = recipient
		sRecipients = append(sRecipients, sRec)
	}

	s.SRecipients = sRecipients
	return sRecipients, nil
}

// Relationships is part of the jsonapi.Object interface
// It is used to generate the recipients relationships
func (s *Sharing) Relationships() jsonapi.RelationshipMap {
	l := len(s.SRecipients)
	i := 0

	data := make([]jsonapi.ResourceIdentifier, l)
	for _, rec := range s.SRecipients {
		r := rec.recipient
		data[i] = jsonapi.ResourceIdentifier{ID: r.ID(), Type: r.DocType()}
		i++
	}
	contents := jsonapi.Relationship{Data: data}
	return jsonapi.RelationshipMap{"recipients": contents}
}

// Included is part of the jsonapi.Object interface
func (s *Sharing) Included() []jsonapi.Object {
	var included []jsonapi.Object
	for _, rec := range s.SRecipients {
		r := rec.recipient
		included = append(included, r)
	}
	return included
}

// GetRecipient returns the Recipient stored in database from a given ID
func GetRecipient(db couchdb.Database, recID string) (*Recipient, error) {
	doc := &Recipient{}
	err := couchdb.GetDoc(db, consts.Recipients, recID, doc)
	if couchdb.IsNotFoundError(err) {
		err = ErrRecipientDoesNotExist
	}
	return doc, err
}

// CheckSharingCreation initializes and check some sharing fields at creation
func CheckSharingCreation(db couchdb.Database, sharing *Sharing) error {

	sharingType := sharing.SharingType
	if sharingType != consts.OneShotSharing &&
		sharingType != consts.MasterSlaveSharing &&
		sharingType != consts.MasterMasterSharing {
		return ErrBadSharingType
	}

	sRecipients, err := sharing.Recipients(db)
	if err != nil {
		return err
	}
	for _, sRec := range sRecipients {
		sRec.Status = consts.PendingSharingStatus
	}

	sharing.Owner = true
	sharing.SharingID = utils.RandomString(32)

	return nil
}

// Create inserts a Sharing document in database
func Create(db couchdb.Database, doc *Sharing) error {
	err := couchdb.CreateDoc(db, doc)
	return err
}

var (
	_ couchdb.Doc    = &Sharing{}
	_ jsonapi.Object = &Sharing{}
)
<|endoftext|>


<commit_before>/*
Debootstrap Action

Construct the target rootfs with debootstrap tool.

Yaml syntax:
 - action: debootstrap
   mirror: URL
   suite: "name"
   components: <list of components>
   variant: "name"
   keyring-package:
   keyring-file:

Mandatory properties:

- suite -- release code name or symbolic name (e.g. "stable")

Optional properties:

- check-gpg -- verify GPG signatures on Release files, true by default

- mirror -- URL with Debian-compatible repository

- variant -- name of the bootstrap script variant to use

- components -- list of components to use for packages selection.
Example:
 components: [ main, contrib ]

- keyring-package -- keyring for package validation.

- keyring-file -- keyring file for repository validation.

- merged-usr -- use merged '/usr' filesystem, true by default.
*/
package actions

import (
	"fmt"
	"io"
	"os"
	"path"
	"strings"

	"github.com/go-debos/debos"
)

type DebootstrapAction struct {
	debos.BaseAction `yaml:",inline"`
	Suite            string
	Mirror           string
	Variant          string
	KeyringPackage   string `yaml:"keyring-package"`
	KeyringFile      string `yaml:"keyring-file"`
	Components       []string
	MergedUsr        bool `yaml:"merged-usr"`
	CheckGpg         bool `yaml:"check-gpg"`
}

func NewDebootstrapAction() *DebootstrapAction {
	d := DebootstrapAction{}
	// Use filesystem with merged '/usr' by default
	d.MergedUsr = true
	// Be secure by default
	d.CheckGpg = true
	return &d

}

func (d *DebootstrapAction) RunSecondStage(context debos.DebosContext) error {
	cmdline := []string{
		"/debootstrap/debootstrap",
		"--no-check-gpg",
		"--second-stage"}

	if d.Components != nil {
		s := strings.Join(d.Components, ",")
		cmdline = append(cmdline, fmt.Sprintf("--components=%s", s))
	}

	c := debos.NewChrootCommandForContext(context)
	// Can't use nspawn for debootstrap as it wants to create device nodes
	c.ChrootMethod = debos.CHROOT_METHOD_CHROOT

	return c.Run("Debootstrap (stage 2)", cmdline...)
}

func (d *DebootstrapAction) Run(context *debos.DebosContext) error {
	d.LogStart()
	cmdline := []string{"debootstrap"}

	if d.MergedUsr {
		cmdline = append(cmdline, "--merged-usr")
	}

	if !d.CheckGpg {
		cmdline = append(cmdline, fmt.Sprintf("--no-check-gpg"))
	} else if d.KeyringFile != "" {
		path := debos.CleanPathAt(d.KeyringFile, context.RecipeDir)
		cmdline = append(cmdline, fmt.Sprintf("--keyring=%s", path))
	}

	if d.KeyringPackage != "" {
		cmdline = append(cmdline, fmt.Sprintf("--include=%s", d.KeyringPackage))
	}

	if d.Components != nil {
		s := strings.Join(d.Components, ",")
		cmdline = append(cmdline, fmt.Sprintf("--components=%s", s))
	}

	/* FIXME drop the hardcoded amd64 assumption" */
	foreign := context.Architecture != "amd64"

	if foreign {
		cmdline = append(cmdline, "--foreign")
		cmdline = append(cmdline, fmt.Sprintf("--arch=%s", context.Architecture))

	}

	if d.Variant != "" {
		cmdline = append(cmdline, fmt.Sprintf("--variant=%s", d.Variant))
	}

	cmdline = append(cmdline, d.Suite)
	cmdline = append(cmdline, context.Rootdir)
	cmdline = append(cmdline, d.Mirror)
	cmdline = append(cmdline, "/usr/share/debootstrap/scripts/unstable")

	err := debos.Command{}.Run("Debootstrap", cmdline...)

	if err != nil {
		return err
	}

	if foreign {
		err = d.RunSecondStage(*context)
		if err != nil {
			return err
		}
	}

	/* HACK */
	srclist, err := os.OpenFile(path.Join(context.Rootdir, "etc/apt/sources.list"),
		os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		return err
	}
	_, err = io.WriteString(srclist, fmt.Sprintf("deb %s %s %s\n",
		d.Mirror,
		d.Suite,
		strings.Join(d.Components, " ")))
	if err != nil {
		return err
	}
	srclist.Close()

	c := debos.NewChrootCommandForContext(*context)

	return c.Run("apt clean", "/usr/bin/apt-get", "clean")
}
<commit_msg>debootstrap: Output the debootstrap log if it fails<commit_after>/*
Debootstrap Action

Construct the target rootfs with debootstrap tool.

Yaml syntax:
 - action: debootstrap
   mirror: URL
   suite: "name"
   components: <list of components>
   variant: "name"
   keyring-package:
   keyring-file:

Mandatory properties:

- suite -- release code name or symbolic name (e.g. "stable")

Optional properties:

- check-gpg -- verify GPG signatures on Release files, true by default

- mirror -- URL with Debian-compatible repository

- variant -- name of the bootstrap script variant to use

- components -- list of components to use for packages selection.
Example:
 components: [ main, contrib ]

- keyring-package -- keyring for package validation.

- keyring-file -- keyring file for repository validation.

- merged-usr -- use merged '/usr' filesystem, true by default.
*/
package actions

import (
	"fmt"
	"io"
	"os"
	"path"
	"strings"

	"github.com/go-debos/debos"
)

type DebootstrapAction struct {
	debos.BaseAction `yaml:",inline"`
	Suite            string
	Mirror           string
	Variant          string
	KeyringPackage   string `yaml:"keyring-package"`
	KeyringFile      string `yaml:"keyring-file"`
	Components       []string
	MergedUsr        bool `yaml:"merged-usr"`
	CheckGpg         bool `yaml:"check-gpg"`
}

func NewDebootstrapAction() *DebootstrapAction {
	d := DebootstrapAction{}
	// Use filesystem with merged '/usr' by default
	d.MergedUsr = true
	// Be secure by default
	d.CheckGpg = true
	return &d

}

func (d *DebootstrapAction) RunSecondStage(context debos.DebosContext) error {
	cmdline := []string{
		"/debootstrap/debootstrap",
		"--no-check-gpg",
		"--second-stage"}

	if d.Components != nil {
		s := strings.Join(d.Components, ",")
		cmdline = append(cmdline, fmt.Sprintf("--components=%s", s))
	}

	c := debos.NewChrootCommandForContext(context)
	// Can't use nspawn for debootstrap as it wants to create device nodes
	c.ChrootMethod = debos.CHROOT_METHOD_CHROOT

	err := c.Run("Debootstrap (stage 2)", cmdline...)

	if (err != nil) {
		log := path.Join(context.Rootdir, "debootstrap/debootstrap.log")
		_ = debos.Command{}.Run("debootstrap.log", "cat", log)
	}

	return err
}

func (d *DebootstrapAction) Run(context *debos.DebosContext) error {
	d.LogStart()
	cmdline := []string{"debootstrap"}

	if d.MergedUsr {
		cmdline = append(cmdline, "--merged-usr")
	}

	if !d.CheckGpg {
		cmdline = append(cmdline, fmt.Sprintf("--no-check-gpg"))
	} else if d.KeyringFile != "" {
		path := debos.CleanPathAt(d.KeyringFile, context.RecipeDir)
		cmdline = append(cmdline, fmt.Sprintf("--keyring=%s", path))
	}

	if d.KeyringPackage != "" {
		cmdline = append(cmdline, fmt.Sprintf("--include=%s", d.KeyringPackage))
	}

	if d.Components != nil {
		s := strings.Join(d.Components, ",")
		cmdline = append(cmdline, fmt.Sprintf("--components=%s", s))
	}

	/* FIXME drop the hardcoded amd64 assumption" */
	foreign := context.Architecture != "amd64"

	if foreign {
		cmdline = append(cmdline, "--foreign")
		cmdline = append(cmdline, fmt.Sprintf("--arch=%s", context.Architecture))

	}

	if d.Variant != "" {
		cmdline = append(cmdline, fmt.Sprintf("--variant=%s", d.Variant))
	}

	cmdline = append(cmdline, d.Suite)
	cmdline = append(cmdline, context.Rootdir)
	cmdline = append(cmdline, d.Mirror)
	cmdline = append(cmdline, "/usr/share/debootstrap/scripts/unstable")

	err := debos.Command{}.Run("Debootstrap", cmdline...)

	if err != nil {
		log := path.Join(context.Rootdir, "debootstrap/debootstrap.log")
		_ = debos.Command{}.Run("debootstrap.log", "cat", log)
		return err
	}

	if foreign {
		err = d.RunSecondStage(*context)
		if err != nil {
			return err
		}
	}

	/* HACK */
	srclist, err := os.OpenFile(path.Join(context.Rootdir, "etc/apt/sources.list"),
		os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		return err
	}
	_, err = io.WriteString(srclist, fmt.Sprintf("deb %s %s %s\n",
		d.Mirror,
		d.Suite,
		strings.Join(d.Components, " ")))
	if err != nil {
		return err
	}
	srclist.Close()

	c := debos.NewChrootCommandForContext(*context)

	return c.Run("apt clean", "/usr/bin/apt-get", "clean")
}
<|endoftext|>


<commit_before>package engo

import (
	"fmt"
	"log"
	"sync"

	"engo.io/ecs"
)

// BackEnd represents the back end used for the window management / GL Surface
type BackEnd uint

const (
	// BackEndGLFW uses glfw
	BackEndGLFW BackEnd = iota
	// BackEndWeb uses gopherjs
	BackEndWeb
	// BackEndMobile uses gomobile
	BackEndMobile
)

var (
	// Time is the active FPS counter
	Time *Clock

	// Input handles all input: mouse, keyboard and touch
	Input *InputManager

	// Mailbox is used by all Systems to communicate
	Mailbox *MessageManager

	currentUpdater            Updater
	currentScene              Scene
	sceneMutex                *sync.RWMutex
	opts                      RunOptions
	resetLoopTicker           = make(chan bool, 1)
	closeGame                 bool
	closerMutex               *sync.RWMutex
	gameWidth, gameHeight     float32
	windowWidth, windowHeight float32
	canvasWidth, canvasHeight float32
	headlessWidth             = 800
	headlessHeight            = 800

	// CurrentBackEnd is the current back end used for window management
	CurrentBackEnd BackEnd
	// ResizeXOffset is how far the screen moves from (0,0) being the top-left corner
	// when the window is resized
	ResizeXOffset = float32(0)
	// ResizeYOffset is how far the screen moves from (0,0) being the top-left corner
	// when the window is resized
	ResizeYOffset = float32(0)
)

const (
	// DefaultVerticalAxis is the name of the default vertical axis, as used internally in `engo` when `StandardInputs`
	// is defined.
	DefaultVerticalAxis = "vertical"

	// DefaultHorizontalAxis is the name of the default horizontal axis, as used internally in `engo` when `StandardInputs`
	// is defined.
	DefaultHorizontalAxis = "horizontal"
	// DefaultMouseXAxis is the name of the default horizontal mouse axis
	DefaultMouseXAxis = "mouse x"
	// DefaultMouseYAxis is the name of the default vertical mouse axis
	DefaultMouseYAxis = "mouse y"
)

// RunOptions are the options used to Run engo
type RunOptions struct {
	// NoRun indicates the Open function should return immediately, without looping
	NoRun bool

	// Title is the Window title
	Title string

	// HeadlessMode indicates whether or not OpenGL calls should be made
	HeadlessMode bool

	// Fullscreen indicates the game should run in fullscreen mode if run on a desktop
	Fullscreen bool

	Width, Height int

	// GlobalScale scales all size/render components by the scale factor
	// Any point passed less than or equal to zero will result in the scale being set to
	// engo.Point{1, 1}.
	// All the systems in common should scale themselves accordingly (collision, camera, render, etc)
	// However, custom systems should be aware of this if this is set.
	GlobalScale Point

	// VSync indicates whether or not OpenGL should wait for the monitor to swp the buffers
	VSync bool

	// Resizable indicates whether or not the Window should be resizable.  Defaults to `true`.
	NotResizable bool

	// ScaleOnResize indicates whether or not engo should make things larger/smaller whenever the screen resizes
	ScaleOnResize bool

	// FPSLimit indicates the maximum number of frames per second
	FPSLimit int

	// OverrideCloseAction indicates that (when true) engo will never close whenever the gamer wants to close the
	// game - that will be your responsibility
	OverrideCloseAction bool

	// StandardInputs is an easy way to map common inputs to actions, such as "jump" being <SPACE>, and "action" being
	// <ENTER>.
	StandardInputs bool

	// MSAA indicates the amount of samples that should be taken. Leaving it blank will default to 1, and you may
	// use any positive value you wish. It may be possible that the operating system / environment doesn't support
	// the requested amount. In that case, GLFW will (hopefully) pick the highest supported sampling count. The higher
	// the value, the bigger the performance cost.
	//
	// Our `RenderSystem` automatically calls `gl.Enable(gl.MULTISAMPLE)` (which is required to make use of it), but
	// if you're going to use your own rendering `System` instead, you will have to call it yourself.
	//
	// Also note that this value is entirely ignored in WebGL - most browsers enable it by default when available, and
	// none of them (at time of writing) allow you to tune it.
	//
	// More info at https://www.opengl.org/wiki/Multisampling
	// "With multisampling, each pixel at the edge of a polygon is sampled multiple times."
	MSAA int

	// AssetsRoot is the path where all resources (images, audio files, fonts, etc.) can be found. Leaving this at
	// empty-string, will default this to `assets`.
	//
	// Whenever using any value that does not start with the directory `assets`, you will not be able to support
	// mobile (Android/iOS), because they **require** all assets to be within the `assets` directory. You may however
	// use any subfolder-structure within that `assets` directory.
	AssetsRoot string

	// MobileWidth and MobileHeight are the width and height given from the Android/iOS OpenGL Surface used for Gomobile bind
	MobileWidth, MobileHeight int

	// Update is the function called each frame during the runLoop to update all of the
	// systems. If left blank, it defaults to &ecs.World{}. Use this if you plan on utilizing
	// engo's window / GL management but don't want to use the ECS paradigm.
	Update Updater
}

// Run is called to create a window, initialize everything, and start the main loop. Once this function returns,
// the game window has been closed already. You can supply a lot of options within `RunOptions`, and your starting
// `Scene` should be defined in `defaultScene`.
func Run(o RunOptions, defaultScene Scene) {
	closerMutex, sceneMutex = &sync.RWMutex{}, &sync.RWMutex{}

	// Setting defaults
	if o.FPSLimit == 0 {
		o.FPSLimit = 60
	}

	if o.MSAA < 0 {
		panic("MSAA has to be greater or equal to 0")
	}

	if o.MSAA == 0 {
		o.MSAA = 1
	}

	if len(o.AssetsRoot) == 0 {
		o.AssetsRoot = "assets"
	}

	if o.Update == nil {
		o.Update = &ecs.World{}
	}

	if o.GlobalScale.X <= 0 || o.GlobalScale.Y <= 0 {
		o.GlobalScale = Point{X: 1, Y: 1}
	}

	opts = o

	// Create input
	Input = NewInputManager()
	if opts.StandardInputs {
		log.Println("Using standard inputs")

		Input.RegisterButton("jump", KeySpace)
		Input.RegisterButton("action", KeyEnter)

		Input.RegisterAxis(DefaultHorizontalAxis, AxisKeyPair{KeyA, KeyD}, AxisKeyPair{KeyArrowLeft, KeyArrowRight})
		Input.RegisterAxis(DefaultVerticalAxis, AxisKeyPair{KeyW, KeyS}, AxisKeyPair{KeyArrowUp, KeyArrowDown})

		Input.RegisterAxis(DefaultMouseXAxis, NewAxisMouse(AxisMouseHori))
		Input.RegisterAxis(DefaultMouseYAxis, NewAxisMouse(AxisMouseVert))
	}

	Files.SetRoot(opts.AssetsRoot)
	currentUpdater = opts.Update

	// And run the game
	if opts.HeadlessMode {
		if opts.Width == 0 {
			opts.Width = headlessWidth
		}
		if opts.Height == 0 {
			opts.Height = headlessHeight
		}
		windowWidth = float32(opts.Width)
		windowHeight = float32(opts.Height)
		gameWidth = float32(opts.Width)
		gameHeight = float32(opts.Height)
		canvasWidth = float32(opts.Width)
		canvasHeight = float32(opts.Height)

		if !opts.NoRun {
			runHeadless(defaultScene)
		} else {
			SetScene(defaultScene, true)
		}
	} else {
		CreateWindow(opts.Title, opts.Width, opts.Height, opts.Fullscreen, opts.MSAA)
		defer DestroyWindow()

		if !opts.NoRun {
			runLoop(defaultScene, false)
		}
	}
}

// SetScaleOnResize can be used to change the value in the given `RunOpts` after already having called `engo.Run`.
func SetScaleOnResize(b bool) {
	opts.ScaleOnResize = b
}

// SetOverrideCloseAction can be used to change the value in the given `RunOpts` after already having called `engo.Run`.
func SetOverrideCloseAction(value bool) {
	opts.OverrideCloseAction = value
}

// SetFPSLimit can be used to change the value in the given `RunOpts` after already having called `engo.Run`.
func SetFPSLimit(limit int) error {
	if limit <= 0 {
		return fmt.Errorf("FPS Limit out of bounds. Requires > 0")
	}
	opts.FPSLimit = limit
	resetLoopTicker <- true
	return nil
}

// Headless indicates whether or not OpenGL-calls should be made
func Headless() bool {
	return opts.HeadlessMode
}

// ScaleOnResize indicates whether or not the screen should resize (i.e. make things look smaller/bigger) whenever
// the window resized. If `false`, then the size of the screen does not affect the size of the things drawn - it just
// makes less/more objects visible
func ScaleOnResize() bool {
	return opts.ScaleOnResize
}

// Exit is the safest way to close your game, as `engo` will correctly attempt to close all windows, handlers and contexts
func Exit() {
	closerMutex.Lock()
	closeGame = true
	closerMutex.Unlock()
}

// GameWidth returns the current game width
func GameWidth() float32 {
	return gameWidth
}

// GameHeight returns the current game height
func GameHeight() float32 {
	return gameHeight
}

func closeEvent() {
	sceneMutex.RLock()
	for _, scenes := range scenes {
		if exiter, ok := scenes.scene.(Exiter); ok {
			exiter.Exit()
		}
	}
	sceneMutex.RUnlock()

	if !opts.OverrideCloseAction {
		Exit()
	} else {
		log.Println("[WARNING] default close action set to false, please make sure you manually handle this")
	}
}

func runHeadless(defaultScene Scene) {
	runLoop(defaultScene, true)
}

// GetGlobalScale returns the GlobalScale factor set in the RunOptions or via
// SetGlobalScale()
func GetGlobalScale() Point {
	return opts.GlobalScale
}

// SetGlobalScale sets the GlobalScale to the given dimensions. If either dimension is
// less than or equal to zero, GlobalScale is set to (1, 1).
func SetGlobalScale(p Point) {
	if p.X <= 0 || p.Y <= 0 {
		opts.GlobalScale = Point{X: 1, Y: 1}
		return
	}
	opts.GlobalScale = p
}
<commit_msg>Hotfix - Restored the import path engo.io<commit_after>package engo // import "engo.io/engo"

import (
	"fmt"
	"log"
	"sync"

	"engo.io/ecs"
)

// BackEnd represents the back end used for the window management / GL Surface
type BackEnd uint

const (
	// BackEndGLFW uses glfw
	BackEndGLFW BackEnd = iota
	// BackEndWeb uses gopherjs
	BackEndWeb
	// BackEndMobile uses gomobile
	BackEndMobile
)

var (
	// Time is the active FPS counter
	Time *Clock

	// Input handles all input: mouse, keyboard and touch
	Input *InputManager

	// Mailbox is used by all Systems to communicate
	Mailbox *MessageManager

	currentUpdater            Updater
	currentScene              Scene
	sceneMutex                *sync.RWMutex
	opts                      RunOptions
	resetLoopTicker           = make(chan bool, 1)
	closeGame                 bool
	closerMutex               *sync.RWMutex
	gameWidth, gameHeight     float32
	windowWidth, windowHeight float32
	canvasWidth, canvasHeight float32
	headlessWidth             = 800
	headlessHeight            = 800

	// CurrentBackEnd is the current back end used for window management
	CurrentBackEnd BackEnd
	// ResizeXOffset is how far the screen moves from (0,0) being the top-left corner
	// when the window is resized
	ResizeXOffset = float32(0)
	// ResizeYOffset is how far the screen moves from (0,0) being the top-left corner
	// when the window is resized
	ResizeYOffset = float32(0)
)

const (
	// DefaultVerticalAxis is the name of the default vertical axis, as used internally in `engo` when `StandardInputs`
	// is defined.
	DefaultVerticalAxis = "vertical"

	// DefaultHorizontalAxis is the name of the default horizontal axis, as used internally in `engo` when `StandardInputs`
	// is defined.
	DefaultHorizontalAxis = "horizontal"
	// DefaultMouseXAxis is the name of the default horizontal mouse axis
	DefaultMouseXAxis = "mouse x"
	// DefaultMouseYAxis is the name of the default vertical mouse axis
	DefaultMouseYAxis = "mouse y"
)

// RunOptions are the options used to Run engo
type RunOptions struct {
	// NoRun indicates the Open function should return immediately, without looping
	NoRun bool

	// Title is the Window title
	Title string

	// HeadlessMode indicates whether or not OpenGL calls should be made
	HeadlessMode bool

	// Fullscreen indicates the game should run in fullscreen mode if run on a desktop
	Fullscreen bool

	Width, Height int

	// GlobalScale scales all size/render components by the scale factor
	// Any point passed less than or equal to zero will result in the scale being set to
	// engo.Point{1, 1}.
	// All the systems in common should scale themselves accordingly (collision, camera, render, etc)
	// However, custom systems should be aware of this if this is set.
	GlobalScale Point

	// VSync indicates whether or not OpenGL should wait for the monitor to swp the buffers
	VSync bool

	// Resizable indicates whether or not the Window should be resizable.  Defaults to `true`.
	NotResizable bool

	// ScaleOnResize indicates whether or not engo should make things larger/smaller whenever the screen resizes
	ScaleOnResize bool

	// FPSLimit indicates the maximum number of frames per second
	FPSLimit int

	// OverrideCloseAction indicates that (when true) engo will never close whenever the gamer wants to close the
	// game - that will be your responsibility
	OverrideCloseAction bool

	// StandardInputs is an easy way to map common inputs to actions, such as "jump" being <SPACE>, and "action" being
	// <ENTER>.
	StandardInputs bool

	// MSAA indicates the amount of samples that should be taken. Leaving it blank will default to 1, and you may
	// use any positive value you wish. It may be possible that the operating system / environment doesn't support
	// the requested amount. In that case, GLFW will (hopefully) pick the highest supported sampling count. The higher
	// the value, the bigger the performance cost.
	//
	// Our `RenderSystem` automatically calls `gl.Enable(gl.MULTISAMPLE)` (which is required to make use of it), but
	// if you're going to use your own rendering `System` instead, you will have to call it yourself.
	//
	// Also note that this value is entirely ignored in WebGL - most browsers enable it by default when available, and
	// none of them (at time of writing) allow you to tune it.
	//
	// More info at https://www.opengl.org/wiki/Multisampling
	// "With multisampling, each pixel at the edge of a polygon is sampled multiple times."
	MSAA int

	// AssetsRoot is the path where all resources (images, audio files, fonts, etc.) can be found. Leaving this at
	// empty-string, will default this to `assets`.
	//
	// Whenever using any value that does not start with the directory `assets`, you will not be able to support
	// mobile (Android/iOS), because they **require** all assets to be within the `assets` directory. You may however
	// use any subfolder-structure within that `assets` directory.
	AssetsRoot string

	// MobileWidth and MobileHeight are the width and height given from the Android/iOS OpenGL Surface used for Gomobile bind
	MobileWidth, MobileHeight int

	// Update is the function called each frame during the runLoop to update all of the
	// systems. If left blank, it defaults to &ecs.World{}. Use this if you plan on utilizing
	// engo's window / GL management but don't want to use the ECS paradigm.
	Update Updater
}

// Run is called to create a window, initialize everything, and start the main loop. Once this function returns,
// the game window has been closed already. You can supply a lot of options within `RunOptions`, and your starting
// `Scene` should be defined in `defaultScene`.
func Run(o RunOptions, defaultScene Scene) {
	closerMutex, sceneMutex = &sync.RWMutex{}, &sync.RWMutex{}

	// Setting defaults
	if o.FPSLimit == 0 {
		o.FPSLimit = 60
	}

	if o.MSAA < 0 {
		panic("MSAA has to be greater or equal to 0")
	}

	if o.MSAA == 0 {
		o.MSAA = 1
	}

	if len(o.AssetsRoot) == 0 {
		o.AssetsRoot = "assets"
	}

	if o.Update == nil {
		o.Update = &ecs.World{}
	}

	if o.GlobalScale.X <= 0 || o.GlobalScale.Y <= 0 {
		o.GlobalScale = Point{X: 1, Y: 1}
	}

	opts = o

	// Create input
	Input = NewInputManager()
	if opts.StandardInputs {
		log.Println("Using standard inputs")

		Input.RegisterButton("jump", KeySpace)
		Input.RegisterButton("action", KeyEnter)

		Input.RegisterAxis(DefaultHorizontalAxis, AxisKeyPair{KeyA, KeyD}, AxisKeyPair{KeyArrowLeft, KeyArrowRight})
		Input.RegisterAxis(DefaultVerticalAxis, AxisKeyPair{KeyW, KeyS}, AxisKeyPair{KeyArrowUp, KeyArrowDown})

		Input.RegisterAxis(DefaultMouseXAxis, NewAxisMouse(AxisMouseHori))
		Input.RegisterAxis(DefaultMouseYAxis, NewAxisMouse(AxisMouseVert))
	}

	Files.SetRoot(opts.AssetsRoot)
	currentUpdater = opts.Update

	// And run the game
	if opts.HeadlessMode {
		if opts.Width == 0 {
			opts.Width = headlessWidth
		}
		if opts.Height == 0 {
			opts.Height = headlessHeight
		}
		windowWidth = float32(opts.Width)
		windowHeight = float32(opts.Height)
		gameWidth = float32(opts.Width)
		gameHeight = float32(opts.Height)
		canvasWidth = float32(opts.Width)
		canvasHeight = float32(opts.Height)

		if !opts.NoRun {
			runHeadless(defaultScene)
		} else {
			SetScene(defaultScene, true)
		}
	} else {
		CreateWindow(opts.Title, opts.Width, opts.Height, opts.Fullscreen, opts.MSAA)
		defer DestroyWindow()

		if !opts.NoRun {
			runLoop(defaultScene, false)
		}
	}
}

// SetScaleOnResize can be used to change the value in the given `RunOpts` after already having called `engo.Run`.
func SetScaleOnResize(b bool) {
	opts.ScaleOnResize = b
}

// SetOverrideCloseAction can be used to change the value in the given `RunOpts` after already having called `engo.Run`.
func SetOverrideCloseAction(value bool) {
	opts.OverrideCloseAction = value
}

// SetFPSLimit can be used to change the value in the given `RunOpts` after already having called `engo.Run`.
func SetFPSLimit(limit int) error {
	if limit <= 0 {
		return fmt.Errorf("FPS Limit out of bounds. Requires > 0")
	}
	opts.FPSLimit = limit
	resetLoopTicker <- true
	return nil
}

// Headless indicates whether or not OpenGL-calls should be made
func Headless() bool {
	return opts.HeadlessMode
}

// ScaleOnResize indicates whether or not the screen should resize (i.e. make things look smaller/bigger) whenever
// the window resized. If `false`, then the size of the screen does not affect the size of the things drawn - it just
// makes less/more objects visible
func ScaleOnResize() bool {
	return opts.ScaleOnResize
}

// Exit is the safest way to close your game, as `engo` will correctly attempt to close all windows, handlers and contexts
func Exit() {
	closerMutex.Lock()
	closeGame = true
	closerMutex.Unlock()
}

// GameWidth returns the current game width
func GameWidth() float32 {
	return gameWidth
}

// GameHeight returns the current game height
func GameHeight() float32 {
	return gameHeight
}

func closeEvent() {
	sceneMutex.RLock()
	for _, scenes := range scenes {
		if exiter, ok := scenes.scene.(Exiter); ok {
			exiter.Exit()
		}
	}
	sceneMutex.RUnlock()

	if !opts.OverrideCloseAction {
		Exit()
	} else {
		log.Println("[WARNING] default close action set to false, please make sure you manually handle this")
	}
}

func runHeadless(defaultScene Scene) {
	runLoop(defaultScene, true)
}

// GetGlobalScale returns the GlobalScale factor set in the RunOptions or via
// SetGlobalScale()
func GetGlobalScale() Point {
	return opts.GlobalScale
}

// SetGlobalScale sets the GlobalScale to the given dimensions. If either dimension is
// less than or equal to zero, GlobalScale is set to (1, 1).
func SetGlobalScale(p Point) {
	if p.X <= 0 || p.Y <= 0 {
		opts.GlobalScale = Point{X: 1, Y: 1}
		return
	}
	opts.GlobalScale = p
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"flag"
	"fmt"
	"github.com/xiangli-cmu/go-raft"
	"github.com/xiangli-cmu/raft-etcd/store"
	"github.com/xiangli-cmu/raft-etcd/web"
	//"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	//"strconv"
	"strings"
	"time"
)

//------------------------------------------------------------------------------
//
// Initialization
//
//------------------------------------------------------------------------------

var verbose bool

var cluster string

var address string
var clientPort int
var serverPort int
var webPort int

var serverCertFile string
var serverKeyFile string
var serverCAFile string

var clientCertFile string
var clientKeyFile string
var clientCAFile string

var dirPath string

var ignore bool

var maxSize int

func init() {
	flag.BoolVar(&verbose, "v", false, "verbose logging")

	flag.StringVar(&cluster, "C", "", "the ip address and port of a existing cluster")

	flag.StringVar(&address, "a", "", "the ip address of the local machine")
	flag.IntVar(&clientPort, "c", 4001, "the port to communicate with clients")
	flag.IntVar(&serverPort, "s", 7001, "the port to communicate with servers")
	flag.IntVar(&webPort, "w", -1, "the port of web interface")

	flag.StringVar(&serverCAFile, "serverCAFile", "", "the path of the CAFile")
	flag.StringVar(&serverCertFile, "serverCert", "", "the cert file of the server")
	flag.StringVar(&serverKeyFile, "serverKey", "", "the key file of the server")

	flag.StringVar(&clientCAFile, "clientCAFile", "", "the path of the client CAFile")
	flag.StringVar(&clientCertFile, "clientCert", "", "the cert file of the client")
	flag.StringVar(&clientKeyFile, "clientKey", "", "the key file of the client")

	flag.StringVar(&dirPath, "d", "./", "the directory to store log and snapshot")

	flag.BoolVar(&ignore, "i", false, "ignore the old configuration, create a new node")

	flag.IntVar(&maxSize, "m", 1024, "the max size of result buffer")
}

// CONSTANTS
const (
	HTTP = iota
	HTTPS
	HTTPSANDVERIFY
)

const (
	SERVER = iota
	CLIENT
)

const (
	ELECTIONTIMTOUT  = 200 * time.Millisecond
	HEARTBEATTIMEOUT = 50 * time.Millisecond
)

//------------------------------------------------------------------------------
//
// Typedefs
//
//------------------------------------------------------------------------------

type Info struct {
	Address    string `json:"address"`
	ServerPort int    `json:"serverPort"`
	ClientPort int    `json:"clientPort"`
	WebPort    int    `json:"webPort"`
}

//------------------------------------------------------------------------------
//
// Variables
//
//------------------------------------------------------------------------------

var server *raft.Server
var serverTransHandler transHandler
var logger *log.Logger

var storeMsg chan string

//------------------------------------------------------------------------------
//
// Functions
//
//------------------------------------------------------------------------------

//--------------------------------------
// Main
//--------------------------------------

func main() {
	var err error
	logger = log.New(os.Stdout, "", log.LstdFlags)
	flag.Parse()

	// Setup commands.
	raft.RegisterCommand(&JoinCommand{})
	raft.RegisterCommand(&SetCommand{})
	raft.RegisterCommand(&GetCommand{})
	raft.RegisterCommand(&DeleteCommand{})

	if err := os.MkdirAll(dirPath, 0744); err != nil {
		fatal("Unable to create path: %v", err)
	}

	// Read server info from file or grab it from user.
	var info *Info = getInfo(dirPath)

	name := fmt.Sprintf("%s:%d", info.Address, info.ServerPort)

	fmt.Printf("ServerName: %s\n\n", name)

	// secrity type
	st := securityType(SERVER)

	if st == -1 {
		panic("ERROR type")
	}

	serverTransHandler = createTranHandler(st)

	// Setup new raft server.
	s := store.CreateStore(maxSize)

	// create raft server
	server, err = raft.NewServer(name, dirPath, serverTransHandler, s, nil)

	if err != nil {
		fatal("%v", err)
	}

	err = server.LoadSnapshot()

	if err == nil {
		debug("%s finished load snapshot", server.Name())
	} else {
		fmt.Println(err)
		debug("%s bad snapshot", server.Name())
	}
	server.Initialize()
	debug("%s finished init", server.Name())
	server.SetElectionTimeout(ELECTIONTIMTOUT)
	server.SetHeartbeatTimeout(HEARTBEATTIMEOUT)
	debug("%s finished set timeout", server.Name())

	if server.IsLogEmpty() {

		// start as a leader in a new cluster
		if cluster == "" {
			server.StartLeader()

			// join self as a peer
			command := &JoinCommand{}
			command.Name = server.Name()
			server.Do(command)
			debug("%s start as a leader", server.Name())

			// start as a fellower in a existing cluster
		} else {
			server.StartFollower()

			err := Join(server, cluster)
			if err != nil {
				panic(err)
			}
			fmt.Println("success join")
		}

		// rejoin the previous cluster
	} else {
		server.StartFollower()
		debug("%s start as a follower", server.Name())
	}

	// open the snapshot
	//go server.Snapshot()

	if webPort != -1 {
		// start web
		s.SetMessager(&storeMsg)
		go webHelper()
		go web.Start(server, webPort)
	}

	go startServTransport(info.ServerPort, st)
	startClientTransport(info.ClientPort, securityType(CLIENT))

}

func usage() {
	fatal("usage: raftd [PATH]")
}

func createTranHandler(st int) transHandler {
	t := transHandler{}

	switch st {
	case HTTP:
		t := transHandler{}
		t.client = nil
		return t

	case HTTPS:
		fallthrough
	case HTTPSANDVERIFY:
		tlsCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)

		if err != nil {
			panic(err)
		}

		tr := &http.Transport{
			TLSClientConfig: &tls.Config{
				Certificates:       []tls.Certificate{tlsCert},
				InsecureSkipVerify: true,
			},
			DisableCompression: true,
		}

		t.client = &http.Client{Transport: tr}
		return t
	}

	// for complier
	return transHandler{}
}

func startServTransport(port int, st int) {

	// internal commands
	http.HandleFunc("/join", JoinHttpHandler)
	http.HandleFunc("/vote", VoteHttpHandler)
	http.HandleFunc("/log", GetLogHttpHandler)
	http.HandleFunc("/log/append", AppendEntriesHttpHandler)
	http.HandleFunc("/snapshot", SnapshotHttpHandler)
	http.HandleFunc("/client", clientHttpHandler)

	switch st {

	case HTTP:
		debug("raft server [%s] listen on http", server.Name())
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))

	case HTTPS:
		http.ListenAndServeTLS(fmt.Sprintf(":%d", port), serverCertFile, serverKeyFile, nil)

	case HTTPSANDVERIFY:
		pemByte, _ := ioutil.ReadFile(serverCAFile)

		block, pemByte := pem.Decode(pemByte)

		cert, err := x509.ParseCertificate(block.Bytes)

		if err != nil {
			fmt.Println(err)
		}

		certPool := x509.NewCertPool()

		certPool.AddCert(cert)

		server := &http.Server{
			TLSConfig: &tls.Config{
				ClientAuth: tls.RequireAndVerifyClientCert,
				ClientCAs:  certPool,
			},
			Addr: fmt.Sprintf(":%d", port),
		}
		err = server.ListenAndServeTLS(serverCertFile, serverKeyFile)

		if err != nil {
			log.Fatal(err)
		}
	}

}

func startClientTransport(port int, st int) {
	// external commands
	http.HandleFunc("/v1/keys/", Multiplexer)
	http.HandleFunc("/v1/watch/", WatchHttpHandler)
	http.HandleFunc("/v1/list/", ListHttpHandler)
	http.HandleFunc("/master", MasterHttpHandler)

	switch st {

	case HTTP:
		debug("etcd [%s] listen on http", server.Name())
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))

	case HTTPS:
		http.ListenAndServeTLS(fmt.Sprintf(":%d", port), clientCertFile, clientKeyFile, nil)

	case HTTPSANDVERIFY:
		pemByte, _ := ioutil.ReadFile(clientCAFile)

		block, pemByte := pem.Decode(pemByte)

		cert, err := x509.ParseCertificate(block.Bytes)

		if err != nil {
			fmt.Println(err)
		}

		certPool := x509.NewCertPool()

		certPool.AddCert(cert)

		server := &http.Server{
			TLSConfig: &tls.Config{
				ClientAuth: tls.RequireAndVerifyClientCert,
				ClientCAs:  certPool,
			},
			Addr: fmt.Sprintf(":%d", port),
		}
		err = server.ListenAndServeTLS(clientCertFile, clientKeyFile)

		if err != nil {
			log.Fatal(err)
		}
	}
}

//--------------------------------------
// Config
//--------------------------------------

func securityType(source int) int {

	var keyFile, certFile, CAFile string

	switch source {
	case SERVER:
		keyFile = serverKeyFile
		certFile = serverCertFile
		CAFile = serverCAFile

	case CLIENT:
		keyFile = clientKeyFile
		certFile = clientCertFile
		CAFile = clientCAFile
	}

	if keyFile == "" && certFile == "" && CAFile == "" {

		return HTTP

	}

	if keyFile != "" && certFile != "" {

		if CAFile != "" {
			return HTTPSANDVERIFY
		}

		return HTTPS
	}

	return -1
}

func getInfo(path string) *Info {
	info := &Info{}

	// Read in the server info if available.
	infoPath := fmt.Sprintf("%s/info", path)

	// delete the old configuration if exist
	if ignore {
		logPath := fmt.Sprintf("%s/log", path)
		snapshotPath := fmt.Sprintf("%s/snapshotPath", path)
		os.Remove(infoPath)
		os.Remove(logPath)
		os.RemoveAll(snapshotPath)

	}

	if file, err := os.Open(infoPath); err == nil {
		if content, err := ioutil.ReadAll(file); err != nil {
			fatal("Unable to read info: %v", err)
		} else {
			if err = json.Unmarshal(content, &info); err != nil {
				fatal("Unable to parse info: %v", err)
			}
		}
		file.Close()

		// Otherwise ask user for info and write it to file.
	} else {

		if address == "" {
			fatal("Please give the address of the local machine")
		}

		info.Address = address
		info.Address = strings.TrimSpace(info.Address)
		fmt.Println("address ", info.Address)

		info.ServerPort = serverPort
		info.ClientPort = clientPort
		info.WebPort = webPort

		// Write to file.
		content, _ := json.Marshal(info)
		content = []byte(string(content) + "\n")
		if err := ioutil.WriteFile(infoPath, content, 0644); err != nil {
			fatal("Unable to write info to file: %v", err)
		}
	}

	return info
}

//--------------------------------------
// Handlers
//--------------------------------------

// Send join requests to the leader.
func Join(s *raft.Server, serverName string) error {
	var b bytes.Buffer

	command := &JoinCommand{}
	command.Name = s.Name()

	json.NewEncoder(&b).Encode(command)

	// t must be ok
	t, _ := server.Transporter().(transHandler)
	debug("Send Join Request to %s", serverName)
	resp, err := Post(&t, fmt.Sprintf("%s/join", serverName), &b)

	for {
		if resp != nil {
			defer resp.Body.Close()
			if resp.StatusCode == http.StatusOK {
				return nil
			}
			if resp.StatusCode == http.StatusServiceUnavailable {
				address, err := ioutil.ReadAll(resp.Body)
				if err != nil {
					warn("Cannot Read Leader info: %v", err)
				}
				debug("Leader is %s", address)
				debug("Send Join Request to %s", address)
				json.NewEncoder(&b).Encode(command)
				resp, err = Post(&t, fmt.Sprintf("%s/join", address), &b)
			}
		}
	}
	return fmt.Errorf("Unable to join: %v", err)
}
<commit_msg>make sure leader join self as a peer<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"flag"
	"fmt"
	"github.com/xiangli-cmu/go-raft"
	"github.com/xiangli-cmu/raft-etcd/store"
	"github.com/xiangli-cmu/raft-etcd/web"
	//"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	//"strconv"
	"strings"
	"time"
)

//------------------------------------------------------------------------------
//
// Initialization
//
//------------------------------------------------------------------------------

var verbose bool

var cluster string

var address string
var clientPort int
var serverPort int
var webPort int

var serverCertFile string
var serverKeyFile string
var serverCAFile string

var clientCertFile string
var clientKeyFile string
var clientCAFile string

var dirPath string

var ignore bool

var maxSize int

func init() {
	flag.BoolVar(&verbose, "v", false, "verbose logging")

	flag.StringVar(&cluster, "C", "", "the ip address and port of a existing cluster")

	flag.StringVar(&address, "a", "", "the ip address of the local machine")
	flag.IntVar(&clientPort, "c", 4001, "the port to communicate with clients")
	flag.IntVar(&serverPort, "s", 7001, "the port to communicate with servers")
	flag.IntVar(&webPort, "w", -1, "the port of web interface")

	flag.StringVar(&serverCAFile, "serverCAFile", "", "the path of the CAFile")
	flag.StringVar(&serverCertFile, "serverCert", "", "the cert file of the server")
	flag.StringVar(&serverKeyFile, "serverKey", "", "the key file of the server")

	flag.StringVar(&clientCAFile, "clientCAFile", "", "the path of the client CAFile")
	flag.StringVar(&clientCertFile, "clientCert", "", "the cert file of the client")
	flag.StringVar(&clientKeyFile, "clientKey", "", "the key file of the client")

	flag.StringVar(&dirPath, "d", "./", "the directory to store log and snapshot")

	flag.BoolVar(&ignore, "i", false, "ignore the old configuration, create a new node")

	flag.IntVar(&maxSize, "m", 1024, "the max size of result buffer")
}

// CONSTANTS
const (
	HTTP = iota
	HTTPS
	HTTPSANDVERIFY
)

const (
	SERVER = iota
	CLIENT
)

const (
	ELECTIONTIMTOUT  = 200 * time.Millisecond
	HEARTBEATTIMEOUT = 50 * time.Millisecond
)

//------------------------------------------------------------------------------
//
// Typedefs
//
//------------------------------------------------------------------------------

type Info struct {
	Address    string `json:"address"`
	ServerPort int    `json:"serverPort"`
	ClientPort int    `json:"clientPort"`
	WebPort    int    `json:"webPort"`
}

//------------------------------------------------------------------------------
//
// Variables
//
//------------------------------------------------------------------------------

var server *raft.Server
var serverTransHandler transHandler
var logger *log.Logger

var storeMsg chan string

//------------------------------------------------------------------------------
//
// Functions
//
//------------------------------------------------------------------------------

//--------------------------------------
// Main
//--------------------------------------

func main() {
	var err error
	logger = log.New(os.Stdout, "", log.LstdFlags)
	flag.Parse()

	// Setup commands.
	raft.RegisterCommand(&JoinCommand{})
	raft.RegisterCommand(&SetCommand{})
	raft.RegisterCommand(&GetCommand{})
	raft.RegisterCommand(&DeleteCommand{})

	if err := os.MkdirAll(dirPath, 0744); err != nil {
		fatal("Unable to create path: %v", err)
	}

	// Read server info from file or grab it from user.
	var info *Info = getInfo(dirPath)

	name := fmt.Sprintf("%s:%d", info.Address, info.ServerPort)

	fmt.Printf("ServerName: %s\n\n", name)

	// secrity type
	st := securityType(SERVER)

	if st == -1 {
		panic("ERROR type")
	}

	serverTransHandler = createTranHandler(st)

	// Setup new raft server.
	s := store.CreateStore(maxSize)

	// create raft server
	server, err = raft.NewServer(name, dirPath, serverTransHandler, s, nil)

	if err != nil {
		fatal("%v", err)
	}

	err = server.LoadSnapshot()

	if err == nil {
		debug("%s finished load snapshot", server.Name())
	} else {
		fmt.Println(err)
		debug("%s bad snapshot", server.Name())
	}
	server.Initialize()
	debug("%s finished init", server.Name())
	server.SetElectionTimeout(ELECTIONTIMTOUT)
	server.SetHeartbeatTimeout(HEARTBEATTIMEOUT)
	debug("%s finished set timeout", server.Name())

	if server.IsLogEmpty() {

		// start as a leader in a new cluster
		if cluster == "" {
			server.StartLeader()

			time.Sleep(time.Millisecond * 20)

			// join self as a peer
			for {
				command := &JoinCommand{}
				command.Name = server.Name()
				_, err := server.Do(command)
				if err == nil {
					break
				}
			}
			debug("%s start as a leader", server.Name())

			// start as a fellower in a existing cluster
		} else {
			server.StartFollower()

			err := Join(server, cluster)
			if err != nil {
				panic(err)
			}
			fmt.Println("success join")
		}

		// rejoin the previous cluster
	} else {
		server.StartFollower()
		debug("%s start as a follower", server.Name())
	}

	// open the snapshot
	//go server.Snapshot()

	if webPort != -1 {
		// start web
		s.SetMessager(&storeMsg)
		go webHelper()
		go web.Start(server, webPort)
	}

	go startServTransport(info.ServerPort, st)
	startClientTransport(info.ClientPort, securityType(CLIENT))

}

func usage() {
	fatal("usage: raftd [PATH]")
}

func createTranHandler(st int) transHandler {
	t := transHandler{}

	switch st {
	case HTTP:
		t := transHandler{}
		t.client = nil
		return t

	case HTTPS:
		fallthrough
	case HTTPSANDVERIFY:
		tlsCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)

		if err != nil {
			panic(err)
		}

		tr := &http.Transport{
			TLSClientConfig: &tls.Config{
				Certificates:       []tls.Certificate{tlsCert},
				InsecureSkipVerify: true,
			},
			DisableCompression: true,
		}

		t.client = &http.Client{Transport: tr}
		return t
	}

	// for complier
	return transHandler{}
}

func startServTransport(port int, st int) {

	// internal commands
	http.HandleFunc("/join", JoinHttpHandler)
	http.HandleFunc("/vote", VoteHttpHandler)
	http.HandleFunc("/log", GetLogHttpHandler)
	http.HandleFunc("/log/append", AppendEntriesHttpHandler)
	http.HandleFunc("/snapshot", SnapshotHttpHandler)
	http.HandleFunc("/client", clientHttpHandler)

	switch st {

	case HTTP:
		debug("raft server [%s] listen on http", server.Name())
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))

	case HTTPS:
		http.ListenAndServeTLS(fmt.Sprintf(":%d", port), serverCertFile, serverKeyFile, nil)

	case HTTPSANDVERIFY:
		pemByte, _ := ioutil.ReadFile(serverCAFile)

		block, pemByte := pem.Decode(pemByte)

		cert, err := x509.ParseCertificate(block.Bytes)

		if err != nil {
			fmt.Println(err)
		}

		certPool := x509.NewCertPool()

		certPool.AddCert(cert)

		server := &http.Server{
			TLSConfig: &tls.Config{
				ClientAuth: tls.RequireAndVerifyClientCert,
				ClientCAs:  certPool,
			},
			Addr: fmt.Sprintf(":%d", port),
		}
		err = server.ListenAndServeTLS(serverCertFile, serverKeyFile)

		if err != nil {
			log.Fatal(err)
		}
	}

}

func startClientTransport(port int, st int) {
	// external commands
	http.HandleFunc("/v1/keys/", Multiplexer)
	http.HandleFunc("/v1/watch/", WatchHttpHandler)
	http.HandleFunc("/v1/list/", ListHttpHandler)
	http.HandleFunc("/master", MasterHttpHandler)

	switch st {

	case HTTP:
		debug("etcd [%s] listen on http", server.Name())
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))

	case HTTPS:
		http.ListenAndServeTLS(fmt.Sprintf(":%d", port), clientCertFile, clientKeyFile, nil)

	case HTTPSANDVERIFY:
		pemByte, _ := ioutil.ReadFile(clientCAFile)

		block, pemByte := pem.Decode(pemByte)

		cert, err := x509.ParseCertificate(block.Bytes)

		if err != nil {
			fmt.Println(err)
		}

		certPool := x509.NewCertPool()

		certPool.AddCert(cert)

		server := &http.Server{
			TLSConfig: &tls.Config{
				ClientAuth: tls.RequireAndVerifyClientCert,
				ClientCAs:  certPool,
			},
			Addr: fmt.Sprintf(":%d", port),
		}
		err = server.ListenAndServeTLS(clientCertFile, clientKeyFile)

		if err != nil {
			log.Fatal(err)
		}
	}
}

//--------------------------------------
// Config
//--------------------------------------

func securityType(source int) int {

	var keyFile, certFile, CAFile string

	switch source {
	case SERVER:
		keyFile = serverKeyFile
		certFile = serverCertFile
		CAFile = serverCAFile

	case CLIENT:
		keyFile = clientKeyFile
		certFile = clientCertFile
		CAFile = clientCAFile
	}

	if keyFile == "" && certFile == "" && CAFile == "" {

		return HTTP

	}

	if keyFile != "" && certFile != "" {

		if CAFile != "" {
			return HTTPSANDVERIFY
		}

		return HTTPS
	}

	return -1
}

func getInfo(path string) *Info {
	info := &Info{}

	// Read in the server info if available.
	infoPath := fmt.Sprintf("%s/info", path)

	// delete the old configuration if exist
	if ignore {
		logPath := fmt.Sprintf("%s/log", path)
		snapshotPath := fmt.Sprintf("%s/snapshotPath", path)
		os.Remove(infoPath)
		os.Remove(logPath)
		os.RemoveAll(snapshotPath)

	}

	if file, err := os.Open(infoPath); err == nil {
		if content, err := ioutil.ReadAll(file); err != nil {
			fatal("Unable to read info: %v", err)
		} else {
			if err = json.Unmarshal(content, &info); err != nil {
				fatal("Unable to parse info: %v", err)
			}
		}
		file.Close()

		// Otherwise ask user for info and write it to file.
	} else {

		if address == "" {
			fatal("Please give the address of the local machine")
		}

		info.Address = address
		info.Address = strings.TrimSpace(info.Address)
		fmt.Println("address ", info.Address)

		info.ServerPort = serverPort
		info.ClientPort = clientPort
		info.WebPort = webPort

		// Write to file.
		content, _ := json.Marshal(info)
		content = []byte(string(content) + "\n")
		if err := ioutil.WriteFile(infoPath, content, 0644); err != nil {
			fatal("Unable to write info to file: %v", err)
		}
	}

	return info
}

//--------------------------------------
// Handlers
//--------------------------------------

// Send join requests to the leader.
func Join(s *raft.Server, serverName string) error {
	var b bytes.Buffer

	command := &JoinCommand{}
	command.Name = s.Name()

	json.NewEncoder(&b).Encode(command)

	// t must be ok
	t, _ := server.Transporter().(transHandler)
	debug("Send Join Request to %s", serverName)
	resp, err := Post(&t, fmt.Sprintf("%s/join", serverName), &b)

	for {
		if resp != nil {
			defer resp.Body.Close()
			if resp.StatusCode == http.StatusOK {
				return nil
			}
			if resp.StatusCode == http.StatusServiceUnavailable {
				address, err := ioutil.ReadAll(resp.Body)
				if err != nil {
					warn("Cannot Read Leader info: %v", err)
				}
				debug("Leader is %s", address)
				debug("Send Join Request to %s", address)
				json.NewEncoder(&b).Encode(command)
				resp, err = Post(&t, fmt.Sprintf("%s/join", address), &b)
			}
		}
	}
	return fmt.Errorf("Unable to join: %v", err)
}
<|endoftext|>