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

package backends

import (
	"fmt"
	"net/http"

	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
	"google.golang.org/api/compute/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/ingress-gce/pkg/backends/features"
	"k8s.io/ingress-gce/pkg/composite"
	"k8s.io/ingress-gce/pkg/utils"
	"k8s.io/ingress-gce/pkg/utils/namer"
	"k8s.io/klog"
	"k8s.io/legacy-cloud-providers/gce"
)

const (
	DefaultConnectionDrainingTimeoutSeconds = 30
)

// Backends handles CRUD operations for backends.
type Backends struct {
	cloud *gce.Cloud
	namer namer.BackendNamer
}

// Backends is a Pool.
var _ Pool = (*Backends)(nil)

// NewPool returns a new backend pool.
// - cloud: implements BackendServices
// - namer: produces names for backends.
func NewPool(cloud *gce.Cloud, namer namer.BackendNamer) *Backends {
	return &Backends{
		cloud: cloud,
		namer: namer,
	}
}

// ensureDescription updates the BackendService Description with the expected value
func ensureDescription(be *composite.BackendService, sp *utils.ServicePort) (needsUpdate bool) {
	desc := sp.GetDescription()
	features.SetDescription(&desc, sp)
	descString := desc.String()
	if be.Description == descString {
		return false
	}
	be.Description = descString
	return true
}

// Create implements Pool.
func (b *Backends) Create(sp utils.ServicePort, hcLink string) (*composite.BackendService, error) {
	name := sp.BackendName()
	namedPort := &compute.NamedPort{
		Name: b.namer.NamedPort(sp.NodePort),
		Port: sp.NodePort,
	}

	version := features.VersionFromServicePort(&sp)
	be := &composite.BackendService{
		Version:      version,
		Name:         name,
		Protocol:     string(sp.Protocol),
		Port:         namedPort.Port,
		PortName:     namedPort.Name,
		HealthChecks: []string{hcLink},
	}

	if sp.L7ILBEnabled {
		be.LoadBalancingScheme = "INTERNAL"
	}

	ensureDescription(be, &sp)
	scope := features.ScopeFromServicePort(&sp)
	key, err := composite.CreateKey(b.cloud, name, scope)
	if err != nil {
		return nil, err
	}

	if err := composite.CreateBackendService(b.cloud, key, be); err != nil {
		return nil, err
	}
	// Note: We need to perform a GCE call to re-fetch the object we just created
	// so that the "Fingerprint" field is filled in. This is needed to update the
	// object without error.
	return b.Get(name, version, scope)
}

// Update implements Pool.
func (b *Backends) Update(be *composite.BackendService) error {
	// Ensure the backend service has the proper version before updating.
	be.Version = features.VersionFromDescription(be.Description)
	scope, err := composite.ScopeFromSelfLink(be.SelfLink)
	if err != nil {
		return err
	}

	key, err := composite.CreateKey(b.cloud, be.Name, scope)
	if err != nil {
		return err
	}
	if err := composite.UpdateBackendService(b.cloud, key, be); err != nil {
		return err
	}
	return nil
}

// Get implements Pool.
func (b *Backends) Get(name string, version meta.Version, scope meta.KeyType) (*composite.BackendService, error) {
	key, err := composite.CreateKey(b.cloud, name, scope)
	if err != nil {
		return nil, err
	}
	be, err := composite.GetBackendService(b.cloud, key, version)
	if err != nil {
		return nil, err
	}
	// Evaluate the existing features from description to see if a lower
	// API version is required so that we don't lose information from
	// the existing backend service.
	versionRequired := features.VersionFromDescription(be.Description)

	if features.IsLowerVersion(versionRequired, version) {
		be, err = composite.GetBackendService(b.cloud, key, versionRequired)
		if err != nil {
			return nil, err
		}
	}
	return be, nil
}

// Delete implements Pool.
func (b *Backends) Delete(name string, version meta.Version, scope meta.KeyType) error {
	klog.V(2).Infof("Deleting backend service %v", name)

	key, err := composite.CreateKey(b.cloud, name, scope)
	if err != nil {
		return err
	}
	err = composite.DeleteBackendService(b.cloud, key, version)
	if err != nil {
		if utils.IsHTTPErrorCode(err, http.StatusNotFound) || utils.IsInUsedByError(err) {
			klog.Infof("DeleteBackendService(_, %v, %v) = %v; ignorable error", key, version, err)
			return nil
		}
		klog.Errorf("DeleteBackendService(_, %v, %v) = %v", key, version, err)
		return err
	}
	klog.V(2).Infof("DeleteBackendService(_, %v, %v) ok", key, version)
	return nil
}

// Health implements Pool.
func (b *Backends) Health(name string, version meta.Version, scope meta.KeyType) (string, error) {
	be, err := b.Get(name, version, scope)
	if err != nil {
		return "Unknown", fmt.Errorf("error getting backend service %s: %v", name, err)
	}
	if len(be.Backends) == 0 {
		return "Unknown", fmt.Errorf("no backends found for backend service %q", name)
	}

	// TODO: Include port, ip in the status, since it's in the health info.
	// TODO (shance) convert to composite types
	for _, backend := range be.Backends {
		var hs *compute.BackendServiceGroupHealth
		switch scope {
		case meta.Global:
			hs, err = b.cloud.GetGlobalBackendServiceHealth(name, backend.Group)
		case meta.Regional:
			hs, err = b.cloud.GetRegionalBackendServiceHealth(name, b.cloud.Region(), backend.Group)
		default:
			return "Unknown", fmt.Errorf("invalid scope for Health(): %s", scope)
		}

		if err != nil {
			return "Unknown", fmt.Errorf("error getting health for backend %q: %v", name, err)
		}
		if len(hs.HealthStatus) == 0 || hs.HealthStatus[0] == nil {
			klog.V(3).Infof("backend service %q does not have health status: %v", name, hs.HealthStatus)
			continue
		}

		// TODO: State transition are important, not just the latest.
		return hs.HealthStatus[0].HealthState, nil
	}
	return "Unknown", nil
}

// List lists all backends managed by this controller.
func (b *Backends) List(key *meta.Key, version meta.Version) ([]*composite.BackendService, error) {
	// TODO: for consistency with the rest of this sub-package this method
	// should return a list of backend ports.
	var backends []*composite.BackendService
	var err error

	backends, err = composite.ListBackendServices(b.cloud, key, version)
	if err != nil {
		return nil, err
	}

	var clusterBackends []*composite.BackendService

	for _, bs := range backends {
		if b.namer.NameBelongsToCluster(bs.Name) {
			scope, err := composite.ScopeFromSelfLink(bs.SelfLink)
			if err != nil {
				return nil, err
			}
			bs.Scope = scope

			clusterBackends = append(clusterBackends, bs)
		}
	}
	return clusterBackends, nil
}

// EnsureL4BackendService creates or updates the backend service with the given name.
func (b *Backends) EnsureL4BackendService(name, hcLink, protocol, sessionAffinity, scheme string, nm types.NamespacedName, version meta.Version) (*composite.BackendService, error) {
	klog.V(2).Infof("EnsureL4BackendService(%v, %v, %v): checking existing backend service", name, scheme, protocol)
	key, err := composite.CreateKey(b.cloud, name, meta.Regional)
	if err != nil {
		return nil, err
	}
	bs, err := composite.GetBackendService(b.cloud, key, meta.VersionGA)
	if err != nil && !utils.IsNotFoundError(err) {
		return nil, err
	}
	desc, err := utils.MakeL4ILBServiceDescription(nm.String(), "", meta.VersionGA)
	if err != nil {
		klog.Warningf("EnsureL4BackendService: Failed to generate description for BackendService %s, err %v",
			name, err)
	}
	expectedBS := &composite.BackendService{
		Name:                name,
		Protocol:            string(protocol),
		Description:         desc,
		HealthChecks:        []string{hcLink},
		SessionAffinity:     utils.TranslateAffinityType(sessionAffinity),
		LoadBalancingScheme: string(scheme),
		ConnectionDraining:  &composite.ConnectionDraining{DrainingTimeoutSec: DefaultConnectionDrainingTimeoutSeconds},
	}

	// Create backend service if none was found
	if bs == nil {
		klog.V(2).Infof("EnsureL4BackendService: creating backend service %v", name)
		err := composite.CreateBackendService(b.cloud, key, expectedBS)
		if err != nil {
			return nil, err
		}
		klog.V(2).Infof("EnsureL4BackendService: created backend service %v successfully", name)
		// We need to perform a GCE call to re-fetch the object we just created
		// so that the "Fingerprint" field is filled in. This is needed to update the
		// object without error. The lookup is also needed to populate the selfLink.
		return composite.GetBackendService(b.cloud, key, meta.VersionGA)
	}

	if backendSvcEqual(expectedBS, bs) {
		return bs, nil
	}
	if bs.ConnectionDraining != nil && bs.ConnectionDraining.DrainingTimeoutSec > 0 {
		// if user overrides this value, continue using that.
		expectedBS.ConnectionDraining.DrainingTimeoutSec = bs.ConnectionDraining.DrainingTimeoutSec
	}
	klog.V(2).Infof("EnsureL4BackendService: updating backend service %v", name)
	// Set fingerprint for optimistic locking
	expectedBS.Fingerprint = bs.Fingerprint
	if err := composite.UpdateBackendService(b.cloud, key, expectedBS); err != nil {
		return nil, err
	}
	klog.V(2).Infof("EnsureL4BackendService: updated backend service %v successfully", name)
	return composite.GetBackendService(b.cloud, key, meta.VersionGA)
}

// backendsListEqual asserts that backend lists are equal by group link only
func backendsListEqual(a, b []*composite.Backend) bool {
	if len(a) != len(b) {
		return false
	}
	if len(a) == 0 {
		return true
	}

	aSet := sets.NewString()
	for _, v := range a {
		aSet.Insert(v.Group)
	}
	bSet := sets.NewString()
	for _, v := range b {
		bSet.Insert(v.Group)
	}

	return aSet.Equal(bSet)
}

// backendSvcEqual returns true if the 2 BackendService objects are equal.
// ConnectionDraining timeout is not checked for equality, if user changes
// this timeout and no other backendService parameters change, the backend
// service will not be updated.
func backendSvcEqual(a, b *composite.BackendService) bool {
	return a.Protocol == b.Protocol &&
		a.Description == b.Description &&
		a.SessionAffinity == b.SessionAffinity &&
		a.LoadBalancingScheme == b.LoadBalancingScheme &&
		utils.EqualStringSets(a.HealthChecks, b.HealthChecks) &&
		backendsListEqual(a.Backends, b.Backends)
}
<commit_msg>Enable access logs by default<commit_after>/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package backends

import (
	"fmt"
	"net/http"

	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
	"google.golang.org/api/compute/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/ingress-gce/pkg/backends/features"
	"k8s.io/ingress-gce/pkg/composite"
	"k8s.io/ingress-gce/pkg/utils"
	"k8s.io/ingress-gce/pkg/utils/namer"
	"k8s.io/klog"
	"k8s.io/legacy-cloud-providers/gce"
)

const (
	DefaultConnectionDrainingTimeoutSeconds = 30
)

// Backends handles CRUD operations for backends.
type Backends struct {
	cloud *gce.Cloud
	namer namer.BackendNamer
}

// Backends is a Pool.
var _ Pool = (*Backends)(nil)

// NewPool returns a new backend pool.
// - cloud: implements BackendServices
// - namer: produces names for backends.
func NewPool(cloud *gce.Cloud, namer namer.BackendNamer) *Backends {
	return &Backends{
		cloud: cloud,
		namer: namer,
	}
}

// ensureDescription updates the BackendService Description with the expected value
func ensureDescription(be *composite.BackendService, sp *utils.ServicePort) (needsUpdate bool) {
	desc := sp.GetDescription()
	features.SetDescription(&desc, sp)
	descString := desc.String()
	if be.Description == descString {
		return false
	}
	be.Description = descString
	return true
}

// Create implements Pool.
func (b *Backends) Create(sp utils.ServicePort, hcLink string) (*composite.BackendService, error) {
	name := sp.BackendName()
	namedPort := &compute.NamedPort{
		Name: b.namer.NamedPort(sp.NodePort),
		Port: sp.NodePort,
	}

	version := features.VersionFromServicePort(&sp)
	be := &composite.BackendService{
		Version:      version,
		Name:         name,
		Protocol:     string(sp.Protocol),
		Port:         namedPort.Port,
		PortName:     namedPort.Name,
		HealthChecks: []string{hcLink},
		// LogConfig is using GA API so this is not considered for computing API version.
		LogConfig: &composite.BackendServiceLogConfig{
			Enable: true,
		},
	}

	if sp.L7ILBEnabled {
		be.LoadBalancingScheme = "INTERNAL"
	}

	ensureDescription(be, &sp)
	scope := features.ScopeFromServicePort(&sp)
	key, err := composite.CreateKey(b.cloud, name, scope)
	if err != nil {
		return nil, err
	}

	if err := composite.CreateBackendService(b.cloud, key, be); err != nil {
		return nil, err
	}
	// Note: We need to perform a GCE call to re-fetch the object we just created
	// so that the "Fingerprint" field is filled in. This is needed to update the
	// object without error.
	return b.Get(name, version, scope)
}

// Update implements Pool.
func (b *Backends) Update(be *composite.BackendService) error {
	// Ensure the backend service has the proper version before updating.
	be.Version = features.VersionFromDescription(be.Description)
	scope, err := composite.ScopeFromSelfLink(be.SelfLink)
	if err != nil {
		return err
	}

	key, err := composite.CreateKey(b.cloud, be.Name, scope)
	if err != nil {
		return err
	}
	if err := composite.UpdateBackendService(b.cloud, key, be); err != nil {
		return err
	}
	return nil
}

// Get implements Pool.
func (b *Backends) Get(name string, version meta.Version, scope meta.KeyType) (*composite.BackendService, error) {
	key, err := composite.CreateKey(b.cloud, name, scope)
	if err != nil {
		return nil, err
	}
	be, err := composite.GetBackendService(b.cloud, key, version)
	if err != nil {
		return nil, err
	}
	// Evaluate the existing features from description to see if a lower
	// API version is required so that we don't lose information from
	// the existing backend service.
	versionRequired := features.VersionFromDescription(be.Description)

	if features.IsLowerVersion(versionRequired, version) {
		be, err = composite.GetBackendService(b.cloud, key, versionRequired)
		if err != nil {
			return nil, err
		}
	}
	return be, nil
}

// Delete implements Pool.
func (b *Backends) Delete(name string, version meta.Version, scope meta.KeyType) error {
	klog.V(2).Infof("Deleting backend service %v", name)

	key, err := composite.CreateKey(b.cloud, name, scope)
	if err != nil {
		return err
	}
	err = composite.DeleteBackendService(b.cloud, key, version)
	if err != nil {
		if utils.IsHTTPErrorCode(err, http.StatusNotFound) || utils.IsInUsedByError(err) {
			klog.Infof("DeleteBackendService(_, %v, %v) = %v; ignorable error", key, version, err)
			return nil
		}
		klog.Errorf("DeleteBackendService(_, %v, %v) = %v", key, version, err)
		return err
	}
	klog.V(2).Infof("DeleteBackendService(_, %v, %v) ok", key, version)
	return nil
}

// Health implements Pool.
func (b *Backends) Health(name string, version meta.Version, scope meta.KeyType) (string, error) {
	be, err := b.Get(name, version, scope)
	if err != nil {
		return "Unknown", fmt.Errorf("error getting backend service %s: %v", name, err)
	}
	if len(be.Backends) == 0 {
		return "Unknown", fmt.Errorf("no backends found for backend service %q", name)
	}

	// TODO: Include port, ip in the status, since it's in the health info.
	// TODO (shance) convert to composite types
	for _, backend := range be.Backends {
		var hs *compute.BackendServiceGroupHealth
		switch scope {
		case meta.Global:
			hs, err = b.cloud.GetGlobalBackendServiceHealth(name, backend.Group)
		case meta.Regional:
			hs, err = b.cloud.GetRegionalBackendServiceHealth(name, b.cloud.Region(), backend.Group)
		default:
			return "Unknown", fmt.Errorf("invalid scope for Health(): %s", scope)
		}

		if err != nil {
			return "Unknown", fmt.Errorf("error getting health for backend %q: %v", name, err)
		}
		if len(hs.HealthStatus) == 0 || hs.HealthStatus[0] == nil {
			klog.V(3).Infof("backend service %q does not have health status: %v", name, hs.HealthStatus)
			continue
		}

		// TODO: State transition are important, not just the latest.
		return hs.HealthStatus[0].HealthState, nil
	}
	return "Unknown", nil
}

// List lists all backends managed by this controller.
func (b *Backends) List(key *meta.Key, version meta.Version) ([]*composite.BackendService, error) {
	// TODO: for consistency with the rest of this sub-package this method
	// should return a list of backend ports.
	var backends []*composite.BackendService
	var err error

	backends, err = composite.ListBackendServices(b.cloud, key, version)
	if err != nil {
		return nil, err
	}

	var clusterBackends []*composite.BackendService

	for _, bs := range backends {
		if b.namer.NameBelongsToCluster(bs.Name) {
			scope, err := composite.ScopeFromSelfLink(bs.SelfLink)
			if err != nil {
				return nil, err
			}
			bs.Scope = scope

			clusterBackends = append(clusterBackends, bs)
		}
	}
	return clusterBackends, nil
}

// EnsureL4BackendService creates or updates the backend service with the given name.
func (b *Backends) EnsureL4BackendService(name, hcLink, protocol, sessionAffinity, scheme string, nm types.NamespacedName, version meta.Version) (*composite.BackendService, error) {
	klog.V(2).Infof("EnsureL4BackendService(%v, %v, %v): checking existing backend service", name, scheme, protocol)
	key, err := composite.CreateKey(b.cloud, name, meta.Regional)
	if err != nil {
		return nil, err
	}
	bs, err := composite.GetBackendService(b.cloud, key, meta.VersionGA)
	if err != nil && !utils.IsNotFoundError(err) {
		return nil, err
	}
	desc, err := utils.MakeL4ILBServiceDescription(nm.String(), "", meta.VersionGA)
	if err != nil {
		klog.Warningf("EnsureL4BackendService: Failed to generate description for BackendService %s, err %v",
			name, err)
	}
	expectedBS := &composite.BackendService{
		Name:                name,
		Protocol:            string(protocol),
		Description:         desc,
		HealthChecks:        []string{hcLink},
		SessionAffinity:     utils.TranslateAffinityType(sessionAffinity),
		LoadBalancingScheme: string(scheme),
		ConnectionDraining:  &composite.ConnectionDraining{DrainingTimeoutSec: DefaultConnectionDrainingTimeoutSeconds},
	}

	// Create backend service if none was found
	if bs == nil {
		klog.V(2).Infof("EnsureL4BackendService: creating backend service %v", name)
		err := composite.CreateBackendService(b.cloud, key, expectedBS)
		if err != nil {
			return nil, err
		}
		klog.V(2).Infof("EnsureL4BackendService: created backend service %v successfully", name)
		// We need to perform a GCE call to re-fetch the object we just created
		// so that the "Fingerprint" field is filled in. This is needed to update the
		// object without error. The lookup is also needed to populate the selfLink.
		return composite.GetBackendService(b.cloud, key, meta.VersionGA)
	}

	if backendSvcEqual(expectedBS, bs) {
		return bs, nil
	}
	if bs.ConnectionDraining != nil && bs.ConnectionDraining.DrainingTimeoutSec > 0 {
		// if user overrides this value, continue using that.
		expectedBS.ConnectionDraining.DrainingTimeoutSec = bs.ConnectionDraining.DrainingTimeoutSec
	}
	klog.V(2).Infof("EnsureL4BackendService: updating backend service %v", name)
	// Set fingerprint for optimistic locking
	expectedBS.Fingerprint = bs.Fingerprint
	if err := composite.UpdateBackendService(b.cloud, key, expectedBS); err != nil {
		return nil, err
	}
	klog.V(2).Infof("EnsureL4BackendService: updated backend service %v successfully", name)
	return composite.GetBackendService(b.cloud, key, meta.VersionGA)
}

// backendsListEqual asserts that backend lists are equal by group link only
func backendsListEqual(a, b []*composite.Backend) bool {
	if len(a) != len(b) {
		return false
	}
	if len(a) == 0 {
		return true
	}

	aSet := sets.NewString()
	for _, v := range a {
		aSet.Insert(v.Group)
	}
	bSet := sets.NewString()
	for _, v := range b {
		bSet.Insert(v.Group)
	}

	return aSet.Equal(bSet)
}

// backendSvcEqual returns true if the 2 BackendService objects are equal.
// ConnectionDraining timeout is not checked for equality, if user changes
// this timeout and no other backendService parameters change, the backend
// service will not be updated.
func backendSvcEqual(a, b *composite.BackendService) bool {
	return a.Protocol == b.Protocol &&
		a.Description == b.Description &&
		a.SessionAffinity == b.SessionAffinity &&
		a.LoadBalancingScheme == b.LoadBalancingScheme &&
		utils.EqualStringSets(a.HealthChecks, b.HealthChecks) &&
		backendsListEqual(a.Backends, b.Backends)
}
<|endoftext|>


<commit_before>package master

import (
	"net/http"
	"os"
	"path"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
	kconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/proxy"
	pconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/proxy/config"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler"
	"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory"
	etcdconfig "github.com/coreos/etcd/config"
	"github.com/coreos/etcd/etcd"
	etcdclient "github.com/coreos/go-etcd/etcd"
	"github.com/fsouza/go-dockerclient"
	"github.com/golang/glog"
	"github.com/google/cadvisor/client"
	"github.com/spf13/cobra"
)

func NewCommandStartAllInOne(name string) *cobra.Command {
	return &cobra.Command{
		Use:   name,
		Short: "Launch in all-in-one mode",
		Run: func(c *cobra.Command, args []string) {
			startAllInOne()
		},
	}
}

func startAllInOne() {
	minionHost := "127.0.0.1"
	minionPort := 10250
	rootDirectory := path.Clean("/var/lib/openshift")
	osAddr := "127.0.0.1:8080"
	osPrefix := "/osapi/v1beta1"
	kubePrefix := "/api/v1beta1"
	kubeClient, err := client.New("http://"+osAddr, nil)
	if err != nil {
		glog.Fatalf("Unable to configure client - bad URL: %v", err)
	}

	etcdAddr := "127.0.0.1:4001"
	etcdServers := []string{} // default
	etcdConfig := etcdconfig.New()
	etcdConfig.BindAddr = etcdAddr
	etcdConfig.DataDir = "openshift.local.etcd"
	etcdConfig.NameFromHostname()

	// check docker connection
	dockerAddr := getDockerEndpoint("")
	dockerClient, err := docker.NewClient(dockerAddr)
	if err != nil {
		glog.Fatal("Couldn't connect to docker.")
	}
	if err := dockerClient.Ping(); err != nil {
		glog.Errorf("WARNING: Docker could not be reached at %s.  Docker must be installed and running to start containers.\n%v", dockerAddr, err)
	}

	cadvisorClient, err := cadvisor.NewClient("http://127.0.0.1:4194")
	if err != nil {
		glog.Errorf("Error on creating cadvisor client: %v", err)
	}

	// initialize etcd
	etcdServer := etcd.New(etcdConfig)
	go util.Forever(func() {
		glog.Infof("Started etcd at http://%s", etcdAddr)
		etcdServer.Run()
	}, 0)

	etcdClient := etcdclient.NewClient(etcdServers)
	for i := 0; ; i += 1 {
		_, err := etcdClient.Get("/", false, false)
		if err == nil || tools.IsEtcdNotFound(err) {
			break
		}
		if i > 100 {
			glog.Fatal("Could not reach etcd: %v", err)
		}
		time.Sleep(50 * time.Millisecond)
	}

	// initialize Kubelet
	os.MkdirAll(rootDirectory, 0750)
	cfg := kconfig.NewPodConfig(kconfig.PodConfigNotificationSnapshotAndUpdates)
	kconfig.NewSourceEtcd(kconfig.EtcdKeyForHost(minionHost), etcdClient, cfg.Channel("etcd"))
	k := kubelet.NewMainKubelet(
		minionHost,
		dockerClient,
		cadvisorClient,
		etcdClient,
		rootDirectory,
		30*time.Second)
	go util.Forever(func() { k.Run(cfg.Updates()) }, 0)
	go util.Forever(func() {
		kubelet.ListenAndServeKubeletServer(k, cfg.Channel("http"), minionHost, uint(minionPort))
	}, 0)

	// initialize OpenShift API
	storage := map[string]apiserver.RESTStorage{}

	osMux := http.NewServeMux()

	// initialize Kubernetes API
	podInfoGetter := &client.HTTPPodInfoGetter{
		Client: http.DefaultClient,
		Port:   uint(minionPort),
	}
	masterConfig := &master.Config{
		Client:             kubeClient,
		EtcdServers:        etcdServers,
		HealthCheckMinions: true,
		Minions:            []string{minionHost},
		PodInfoGetter:      podInfoGetter,
	}
	m := master.New(masterConfig)

	apiserver.NewAPIGroup(m.API_v1beta1()).InstallREST(osMux, kubePrefix)
	apiserver.NewAPIGroup(storage, runtime.Codec).InstallREST(osMux, osPrefix)
	apiserver.InstallSupport(osMux)

	osApi := &http.Server{
		Addr:           osAddr,
		Handler:        apiserver.RecoverPanics(osMux),
		ReadTimeout:    5 * time.Minute,
		WriteTimeout:   5 * time.Minute,
		MaxHeaderBytes: 1 << 20,
	}

	go util.Forever(func() {
		glog.Infof("Started Kubernetes API at http://%s%s", osAddr, kubePrefix)
		glog.Infof("Started OpenShift API at http://%s%s", osAddr, osPrefix)
		glog.Fatal(osApi.ListenAndServe())
	}, 0)

	// initialize kube proxy
	serviceConfig := pconfig.NewServiceConfig()
	endpointsConfig := pconfig.NewEndpointsConfig()
	pconfig.NewConfigSourceEtcd(etcdClient,
		serviceConfig.Channel("etcd"),
		endpointsConfig.Channel("etcd"))
	loadBalancer := proxy.NewLoadBalancerRR()
	proxier := proxy.NewProxier(loadBalancer)
	serviceConfig.RegisterHandler(proxier)
	endpointsConfig.RegisterHandler(loadBalancer)
	glog.Infof("Started Kubernetes Proxy")

	// initialize replication manager
	controllerManager := controller.NewReplicationManager(kubeClient)
	controllerManager.Run(10 * time.Second)
	glog.Infof("Started Kubernetes Replication Manager")

	// initialize scheduler
	configFactory := &factory.ConfigFactory{Client: kubeClient}
	config := configFactory.Create()
	s := scheduler.New(config)
	s.Run()
	glog.Infof("Started Kubernetes Scheduler")

	select {}
}

func env(key string, defaultValue string) string {
	val := os.Getenv(key)
	if len(val) == 0 {
		return defaultValue
	} else {
		return val
	}
}

func getDockerEndpoint(dockerEndpoint string) string {
	var endpoint string
	if len(dockerEndpoint) > 0 {
		endpoint = dockerEndpoint
	} else if len(os.Getenv("DOCKER_HOST")) > 0 {
		endpoint = os.Getenv("DOCKER_HOST")
	} else {
		endpoint = "unix:///var/run/docker.sock"
	}
	glog.Infof("Connecting to docker on %s", endpoint)

	return endpoint
}
<commit_msg>Use consistent etcd name<commit_after>package master

import (
	"net/http"
	"os"
	"path"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
	kconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/proxy"
	pconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/proxy/config"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler"
	"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory"
	etcdconfig "github.com/coreos/etcd/config"
	"github.com/coreos/etcd/etcd"
	etcdclient "github.com/coreos/go-etcd/etcd"
	"github.com/fsouza/go-dockerclient"
	"github.com/golang/glog"
	"github.com/google/cadvisor/client"
	"github.com/spf13/cobra"
)

func NewCommandStartAllInOne(name string) *cobra.Command {
	return &cobra.Command{
		Use:   name,
		Short: "Launch in all-in-one mode",
		Run: func(c *cobra.Command, args []string) {
			startAllInOne()
		},
	}
}

func startAllInOne() {
	minionHost := "127.0.0.1"
	minionPort := 10250
	rootDirectory := path.Clean("/var/lib/openshift")
	osAddr := "127.0.0.1:8080"
	osPrefix := "/osapi/v1beta1"
	kubePrefix := "/api/v1beta1"
	kubeClient, err := client.New("http://"+osAddr, nil)
	if err != nil {
		glog.Fatalf("Unable to configure client - bad URL: %v", err)
	}

	etcdAddr := "127.0.0.1:4001"
	etcdServers := []string{} // default
	etcdConfig := etcdconfig.New()
	etcdConfig.BindAddr = etcdAddr
	etcdConfig.DataDir = "openshift.local.etcd"
	etcdConfig.Name = "openshift.local"

	// check docker connection
	dockerAddr := getDockerEndpoint("")
	dockerClient, err := docker.NewClient(dockerAddr)
	if err != nil {
		glog.Fatal("Couldn't connect to docker.")
	}
	if err := dockerClient.Ping(); err != nil {
		glog.Errorf("WARNING: Docker could not be reached at %s.  Docker must be installed and running to start containers.\n%v", dockerAddr, err)
	}

	cadvisorClient, err := cadvisor.NewClient("http://127.0.0.1:4194")
	if err != nil {
		glog.Errorf("Error on creating cadvisor client: %v", err)
	}

	// initialize etcd
	etcdServer := etcd.New(etcdConfig)
	go util.Forever(func() {
		glog.Infof("Started etcd at http://%s", etcdAddr)
		etcdServer.Run()
	}, 0)

	etcdClient := etcdclient.NewClient(etcdServers)
	for i := 0; ; i += 1 {
		_, err := etcdClient.Get("/", false, false)
		if err == nil || tools.IsEtcdNotFound(err) {
			break
		}
		if i > 100 {
			glog.Fatal("Could not reach etcd: %v", err)
		}
		time.Sleep(50 * time.Millisecond)
	}

	// initialize Kubelet
	os.MkdirAll(rootDirectory, 0750)
	cfg := kconfig.NewPodConfig(kconfig.PodConfigNotificationSnapshotAndUpdates)
	kconfig.NewSourceEtcd(kconfig.EtcdKeyForHost(minionHost), etcdClient, cfg.Channel("etcd"))
	k := kubelet.NewMainKubelet(
		minionHost,
		dockerClient,
		cadvisorClient,
		etcdClient,
		rootDirectory,
		30*time.Second)
	go util.Forever(func() { k.Run(cfg.Updates()) }, 0)
	go util.Forever(func() {
		kubelet.ListenAndServeKubeletServer(k, cfg.Channel("http"), minionHost, uint(minionPort))
	}, 0)

	// initialize OpenShift API
	storage := map[string]apiserver.RESTStorage{}

	osMux := http.NewServeMux()

	// initialize Kubernetes API
	podInfoGetter := &client.HTTPPodInfoGetter{
		Client: http.DefaultClient,
		Port:   uint(minionPort),
	}
	masterConfig := &master.Config{
		Client:             kubeClient,
		EtcdServers:        etcdServers,
		HealthCheckMinions: true,
		Minions:            []string{minionHost},
		PodInfoGetter:      podInfoGetter,
	}
	m := master.New(masterConfig)

	apiserver.NewAPIGroup(m.API_v1beta1()).InstallREST(osMux, kubePrefix)
	apiserver.NewAPIGroup(storage, runtime.Codec).InstallREST(osMux, osPrefix)
	apiserver.InstallSupport(osMux)

	osApi := &http.Server{
		Addr:           osAddr,
		Handler:        apiserver.RecoverPanics(osMux),
		ReadTimeout:    5 * time.Minute,
		WriteTimeout:   5 * time.Minute,
		MaxHeaderBytes: 1 << 20,
	}

	go util.Forever(func() {
		glog.Infof("Started Kubernetes API at http://%s%s", osAddr, kubePrefix)
		glog.Infof("Started OpenShift API at http://%s%s", osAddr, osPrefix)
		glog.Fatal(osApi.ListenAndServe())
	}, 0)

	// initialize kube proxy
	serviceConfig := pconfig.NewServiceConfig()
	endpointsConfig := pconfig.NewEndpointsConfig()
	pconfig.NewConfigSourceEtcd(etcdClient,
		serviceConfig.Channel("etcd"),
		endpointsConfig.Channel("etcd"))
	loadBalancer := proxy.NewLoadBalancerRR()
	proxier := proxy.NewProxier(loadBalancer)
	serviceConfig.RegisterHandler(proxier)
	endpointsConfig.RegisterHandler(loadBalancer)
	glog.Infof("Started Kubernetes Proxy")

	// initialize replication manager
	controllerManager := controller.NewReplicationManager(kubeClient)
	controllerManager.Run(10 * time.Second)
	glog.Infof("Started Kubernetes Replication Manager")

	// initialize scheduler
	configFactory := &factory.ConfigFactory{Client: kubeClient}
	config := configFactory.Create()
	s := scheduler.New(config)
	s.Run()
	glog.Infof("Started Kubernetes Scheduler")

	select {}
}

func env(key string, defaultValue string) string {
	val := os.Getenv(key)
	if len(val) == 0 {
		return defaultValue
	} else {
		return val
	}
}

func getDockerEndpoint(dockerEndpoint string) string {
	var endpoint string
	if len(dockerEndpoint) > 0 {
		endpoint = dockerEndpoint
	} else if len(os.Getenv("DOCKER_HOST")) > 0 {
		endpoint = os.Getenv("DOCKER_HOST")
	} else {
		endpoint = "unix:///var/run/docker.sock"
	}
	glog.Infof("Connecting to docker on %s", endpoint)

	return endpoint
}
<|endoftext|>


<commit_before>// Copyright 2016 Ross Light
//
// 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 kdbcrypt encrypts and decrypts data using the KeePass1 encryption scheme.
package kdbcrypt // import "zombiezen.com/go/sandpass/pkg/kdbcrypt"

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"io"
	"io/ioutil"
	"sync"

	"golang.org/x/crypto/twofish"
	"zombiezen.com/go/sandpass/pkg/cipherio"
	"zombiezen.com/go/sandpass/pkg/padding"
)

// Errors
var (
	ErrUnknownCipher = errors.New("keepass: unknown cipher")
	ErrSize          = errors.New("keepass: data size not a multiple of 16")
)

// Block size in bytes.
const BlockSize = 16

// A Key is the set of parameters used to build the cipher key.
type Key struct {
	Password        []byte // optional
	KeyFileHash     []byte // must be nil or length 16
	MasterSeed      [16]byte
	TransformSeed   [32]byte
	TransformRounds uint32
}

// Params specifies the encryption/decryption values.
type Params struct {
	Key    Key
	Cipher Cipher
	IV     [16]byte
}

// Cipher is a cipher algorithm.
type Cipher int

// Available ciphers
const (
	RijndaelCipher Cipher = iota
	TwofishCipher
)

// NewEncrypter creates a new writer that encrypts to w.  Closing the
// new writer writes the final, padded block but does not close w.
func NewEncrypter(w io.Writer, params *Params) (io.WriteCloser, error) {
	ciph, err := params.Cipher.cipher(params.Key.build())
	if err != nil {
		return nil, err
	}
	e := cipher.NewCBCEncrypter(ciph, params.IV[:])
	return cipherio.NewWriter(w, e, padding.PKCS7), nil
}

// NewDecrypter creates a new reader that decrypts and strips padding from r.
func NewDecrypter(r io.Reader, params *Params) (io.Reader, error) {
	ciph, err := params.Cipher.cipher(params.Key.build())
	if err != nil {
		return nil, err
	}
	d := cipher.NewCBCDecrypter(ciph, params.IV[:])
	return cipherio.NewReader(r, d, padding.PKCS7), nil
}

func (k *Key) build() []byte {
	sum := sha256.New()

	sum.Write(k.MasterSeed[:])

	base := k.baseHash()
	var wg sync.WaitGroup
	wg.Add(2)
	var tk [sha256.Size]byte
	go transformKeyBlock(&wg, tk[:aes.BlockSize], base[:aes.BlockSize], k.TransformSeed[:], k.TransformRounds)
	go transformKeyBlock(&wg, tk[aes.BlockSize:], base[aes.BlockSize:], k.TransformSeed[:], k.TransformRounds)
	wg.Wait()
	tk = sha256.Sum256(tk[:])
	sum.Write(tk[:])

	return sum.Sum(nil)
}

func (c Cipher) cipher(key []byte) (cipher.Block, error) {
	switch c {
	case RijndaelCipher:
		return aes.NewCipher(key)
	case TwofishCipher:
		return twofish.NewCipher(key)
	default:
		return nil, ErrUnknownCipher
	}
}

// transformKeyBlock applies rounds of AES encryption using key seed to src and stores the result in dst.
func transformKeyBlock(wg *sync.WaitGroup, dst, src, seed []byte, rounds uint32) {
	dst = dst[:aes.BlockSize]
	copy(dst, src)
	c, err := aes.NewCipher(seed)
	if err != nil {
		panic(err)
	}
	for i := uint32(0); i < rounds; i++ {
		c.Encrypt(dst, dst)
	}
	wg.Done()
}

// baseHash returns the key's hash prior to encryption rounds.
func (k *Key) baseHash() [sha256.Size]byte {
	if len(k.KeyFileHash) == 0 {
		return sha256.Sum256(k.Password)
	}
	if len(k.Password) == 0 {
		var a [sha256.Size]byte
		copy(a[:], k.KeyFileHash)
		return a
	}
	h := sha256.New()
	p := sha256.Sum256(k.Password)
	h.Write(p[:])
	h.Write(k.KeyFileHash)
	var a [sha256.Size]byte
	h.Sum(a[:0])
	return a
}

// ReadKeyFile reads a key file and returns its hash for use in a Key.
func ReadKeyFile(r io.Reader) ([]byte, error) {
	const maxSize = 64
	data, err := ioutil.ReadAll(&io.LimitedReader{R: r, N: maxSize + 1})
	if err != nil {
		return data, err
	}
	switch len(data) {
	case 32:
		return data, nil
	case 64:
		h := make([]byte, hex.DecodedLen(len(data)))
		if _, err := hex.Decode(h, data); err == nil {
			return h, nil
		}
	}
	s := sha256.New()
	s.Write(data[:])
	if _, err := io.Copy(s, r); err != nil {
		return nil, err
	}
	return s.Sum(nil), nil
}
<commit_msg>pkg/kdbcrypt: move around code into logical order<commit_after>// Copyright 2016 Ross Light
//
// 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 kdbcrypt encrypts and decrypts data using the KeePass1 encryption scheme.
package kdbcrypt // import "zombiezen.com/go/sandpass/pkg/kdbcrypt"

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"io"
	"io/ioutil"
	"sync"

	"golang.org/x/crypto/twofish"
	"zombiezen.com/go/sandpass/pkg/cipherio"
	"zombiezen.com/go/sandpass/pkg/padding"
)

// Errors
var (
	ErrUnknownCipher = errors.New("keepass: unknown cipher")
	ErrSize          = errors.New("keepass: data size not a multiple of 16")
)

// Block size in bytes.
const BlockSize = 16

// Params specifies the encryption/decryption values.
type Params struct {
	Key    Key
	Cipher Cipher
	IV     [16]byte
}

// A Key is the set of parameters used to build the cipher key.
type Key struct {
	Password        []byte // optional
	KeyFileHash     []byte // must be nil or length 16
	MasterSeed      [16]byte
	TransformSeed   [32]byte
	TransformRounds uint32
}

func (k *Key) build() []byte {
	sum := sha256.New()

	sum.Write(k.MasterSeed[:])

	base := k.baseHash()
	var wg sync.WaitGroup
	wg.Add(2)
	var tk [sha256.Size]byte
	go transformKeyBlock(&wg, tk[:aes.BlockSize], base[:aes.BlockSize], k.TransformSeed[:], k.TransformRounds)
	go transformKeyBlock(&wg, tk[aes.BlockSize:], base[aes.BlockSize:], k.TransformSeed[:], k.TransformRounds)
	wg.Wait()
	tk = sha256.Sum256(tk[:])
	sum.Write(tk[:])

	return sum.Sum(nil)
}

// baseHash returns the key's hash prior to encryption rounds.
func (k *Key) baseHash() [sha256.Size]byte {
	if len(k.KeyFileHash) == 0 {
		return sha256.Sum256(k.Password)
	}
	if len(k.Password) == 0 {
		var a [sha256.Size]byte
		copy(a[:], k.KeyFileHash)
		return a
	}
	h := sha256.New()
	p := sha256.Sum256(k.Password)
	h.Write(p[:])
	h.Write(k.KeyFileHash)
	var a [sha256.Size]byte
	h.Sum(a[:0])
	return a
}

// transformKeyBlock applies rounds of AES encryption using key seed to src and stores the result in dst.
func transformKeyBlock(wg *sync.WaitGroup, dst, src, seed []byte, rounds uint32) {
	dst = dst[:aes.BlockSize]
	copy(dst, src)
	c, err := aes.NewCipher(seed)
	if err != nil {
		panic(err)
	}
	for i := uint32(0); i < rounds; i++ {
		c.Encrypt(dst, dst)
	}
	wg.Done()
}

// Cipher is a cipher algorithm.
type Cipher int

// Available ciphers
const (
	RijndaelCipher Cipher = iota
	TwofishCipher
)

func (c Cipher) cipher(key []byte) (cipher.Block, error) {
	switch c {
	case RijndaelCipher:
		return aes.NewCipher(key)
	case TwofishCipher:
		return twofish.NewCipher(key)
	default:
		return nil, ErrUnknownCipher
	}
}

// NewEncrypter creates a new writer that encrypts to w.  Closing the
// new writer writes the final, padded block but does not close w.
func NewEncrypter(w io.Writer, params *Params) (io.WriteCloser, error) {
	ciph, err := params.Cipher.cipher(params.Key.build())
	if err != nil {
		return nil, err
	}
	e := cipher.NewCBCEncrypter(ciph, params.IV[:])
	return cipherio.NewWriter(w, e, padding.PKCS7), nil
}

// NewDecrypter creates a new reader that decrypts and strips padding from r.
func NewDecrypter(r io.Reader, params *Params) (io.Reader, error) {
	ciph, err := params.Cipher.cipher(params.Key.build())
	if err != nil {
		return nil, err
	}
	d := cipher.NewCBCDecrypter(ciph, params.IV[:])
	return cipherio.NewReader(r, d, padding.PKCS7), nil
}

// ReadKeyFile reads a key file and returns its hash for use in a Key.
func ReadKeyFile(r io.Reader) ([]byte, error) {
	const maxSize = 64
	data, err := ioutil.ReadAll(&io.LimitedReader{R: r, N: maxSize + 1})
	if err != nil {
		return data, err
	}
	switch len(data) {
	case 32:
		return data, nil
	case 64:
		h := make([]byte, hex.DecodedLen(len(data)))
		if _, err := hex.Decode(h, data); err == nil {
			return h, nil
		}
	}
	s := sha256.New()
	s.Write(data[:])
	if _, err := io.Copy(s, r); err != nil {
		return nil, err
	}
	return s.Sum(nil), nil
}
<|endoftext|>


<commit_before>package slinga

import (
	"github.com/mattn/go-zglob"
	"sort"
)

/*
	This file declares all the necessary structures for Dependencies (User "wants" Service)
*/

// Dependency in a form <UserID> requested <Service> (and provided additional <Labels>)
type Dependency struct {
	ID      string
	UserID  string
	Service string
	Labels  map[string]string
	Trace   bool

	// This field is populated when dependency gets resolved
	ResolvesTo string
}

// GlobalDependencies represents the list of global dependencies (see the definition above)
type GlobalDependencies struct {
	// dependencies <service> -> list of dependencies
	DependenciesByService map[string][]*Dependency

	// dependencies <id> -> dependency
	DependenciesByID map[string]*Dependency
}

func (src *GlobalDependencies) count() int {
	return countElements(src.DependenciesByID)
}

// NewGlobalDependencies creates and initializes a new empty list of global dependencies
func NewGlobalDependencies() GlobalDependencies {
	return GlobalDependencies{
		DependenciesByService: make(map[string][]*Dependency),
		DependenciesByID:      make(map[string]*Dependency),
	}
}

// Apply set of transformations to labels
func (dependency *Dependency) getLabelSet() LabelSet {
	return LabelSet{Labels: dependency.Labels}
}

// SetTrace enable tracing (detailed engine output) for all dependencies
func (src *GlobalDependencies) SetTrace(trace bool) {
	if trace {
		for _, d := range src.DependenciesByID {
			d.Trace = true
		}
	}
}

// Append a single dependency to an existing object
func (src GlobalDependencies) appendDependency(dependency *Dependency) {
	src.DependenciesByService[dependency.Service] = append(src.DependenciesByService[dependency.Service], dependency)
	src.DependenciesByID[dependency.ID] = dependency
}

// Copy the whole structure with dependencies
func (src GlobalDependencies) makeCopy() GlobalDependencies {
	result := NewGlobalDependencies()
	for _, v := range src.DependenciesByID {
		result.appendDependency(v)
	}
	return result
}

// LoadDependenciesFromDir loads all dependencies from a given directory
func LoadDependenciesFromDir(baseDir string) GlobalDependencies {
	// read all services
	files, _ := zglob.Glob(GetAptomiObjectFilePatternYaml(baseDir, TypeDependencies))
	sort.Strings(files)
	result := NewGlobalDependencies()
	for _, fileName := range files {
		t := loadDependenciesFromFile(fileName)
		for _, d := range t {
			result.appendDependency(d)
		}
	}
	return result
}
<commit_msg>check for empty dependency IDs<commit_after>package slinga

import (
	"github.com/mattn/go-zglob"
	"sort"
	log "github.com/Sirupsen/logrus"
)

/*
	This file declares all the necessary structures for Dependencies (User "wants" Service)
*/

// Dependency in a form <UserID> requested <Service> (and provided additional <Labels>)
type Dependency struct {
	ID      string
	UserID  string
	Service string
	Labels  map[string]string
	Trace   bool

	// This field is populated when dependency gets resolved
	ResolvesTo string
}

// GlobalDependencies represents the list of global dependencies (see the definition above)
type GlobalDependencies struct {
	// dependencies <service> -> list of dependencies
	DependenciesByService map[string][]*Dependency

	// dependencies <id> -> dependency
	DependenciesByID map[string]*Dependency
}

func (src *GlobalDependencies) count() int {
	return countElements(src.DependenciesByID)
}

// NewGlobalDependencies creates and initializes a new empty list of global dependencies
func NewGlobalDependencies() GlobalDependencies {
	return GlobalDependencies{
		DependenciesByService: make(map[string][]*Dependency),
		DependenciesByID:      make(map[string]*Dependency),
	}
}

// Apply set of transformations to labels
func (dependency *Dependency) getLabelSet() LabelSet {
	return LabelSet{Labels: dependency.Labels}
}

// SetTrace enable tracing (detailed engine output) for all dependencies
func (src *GlobalDependencies) SetTrace(trace bool) {
	if trace {
		for _, d := range src.DependenciesByID {
			d.Trace = true
		}
	}
}

// Append a single dependency to an existing object
func (src GlobalDependencies) appendDependency(dependency *Dependency) {
	if len(dependency.ID) <= 0 {
		debug.WithFields(log.Fields{
			"dependency": dependency,
		}).Fatal("Empty dependency ID")
	}
	src.DependenciesByService[dependency.Service] = append(src.DependenciesByService[dependency.Service], dependency)
	src.DependenciesByID[dependency.ID] = dependency
}

// Copy the whole structure with dependencies
func (src GlobalDependencies) makeCopy() GlobalDependencies {
	result := NewGlobalDependencies()
	for _, v := range src.DependenciesByID {
		result.appendDependency(v)
	}
	return result
}

// LoadDependenciesFromDir loads all dependencies from a given directory
func LoadDependenciesFromDir(baseDir string) GlobalDependencies {
	// read all services
	files, _ := zglob.Glob(GetAptomiObjectFilePatternYaml(baseDir, TypeDependencies))
	sort.Strings(files)
	result := NewGlobalDependencies()
	for _, fileName := range files {
		t := loadDependenciesFromFile(fileName)
		for _, d := range t {
			result.appendDependency(d)
		}
	}
	return result
}
<|endoftext|>


<commit_before>package flow

import (
	"log"
	"reflect"

	"github.com/chrislusf/glow/util"
)

// hash data or by data key, return a new dataset
// This is devided into 2 steps:
// 1. Each record is sharded to a local shard
// 2. The destination shard will collect its child shards and merge into one
func (d *Dataset) Partition(shard int) *Dataset {
	if shard == 1 {
		return d
	}
	if d.IsKeyPartitioned && shard == len(d.Shards) {
		return d
	}
	ret := d.partition_scatter(shard).partition_collect(shard)
	ret.IsKeyPartitioned = true
	return ret
}

func HashByKey(input reflect.Value, shard int) int {
	v := guessKey(input)

	dt := v.Type()
	if dt.Kind() == reflect.Interface {
		dt = reflect.TypeOf(v.Interface())
		v = reflect.ValueOf(v.Interface())
	}

	var x int
	switch dt.Kind() {
	case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Uint8:
		x = int(v.Int()) % shard
	case reflect.String:
		x = int(util.Hash([]byte(v.String()))) % shard
	case reflect.Slice:
		x = int(util.Hash(v.Bytes())) % shard
	default:
		log.Fatalf("unexpected key to hash %s: %v", v.Kind().String(), v)
	}
	return x
}

func (d *Dataset) partition_scatter(shard int) (ret *Dataset) {
	ret = d.context.newNextDataset(len(d.Shards)*shard, d.Type)
	step := d.context.AddOneToEveryNStep(d, shard, ret)
	step.Name = "Partition_scatter"
	step.Function = func(task *Task) {
		for input := range task.InputChan() {
			x := HashByKey(input, shard)
			task.Outputs[x].WriteChan.Send(input)
		}
	}
	return
}

func (d *Dataset) partition_collect(shard int) (ret *Dataset) {
	ret = d.context.newNextDataset(shard, d.Type)
	step := d.context.AddLinkedNToOneStep(d, len(d.Shards)/shard, ret)
	step.Name = "Partition_collect"
	step.Function = func(task *Task) {
		for input := range task.MergedInputChan() {
			task.Outputs[0].WriteChan.Send(input)
		}
	}
	return
}
<commit_msg>rename to hashByKey()<commit_after>package flow

import (
	"log"
	"reflect"

	"github.com/chrislusf/glow/util"
)

// hash data or by data key, return a new dataset
// This is devided into 2 steps:
// 1. Each record is sharded to a local shard
// 2. The destination shard will collect its child shards and merge into one
func (d *Dataset) Partition(shard int) *Dataset {
	if shard == 1 {
		return d
	}
	if d.IsKeyPartitioned && shard == len(d.Shards) {
		return d
	}
	ret := d.partition_scatter(shard).partition_collect(shard)
	ret.IsKeyPartitioned = true
	return ret
}

func hashByKey(input reflect.Value, shard int) int {
	v := guessKey(input)

	dt := v.Type()
	if dt.Kind() == reflect.Interface {
		dt = reflect.TypeOf(v.Interface())
		v = reflect.ValueOf(v.Interface())
	}

	var x int
	switch dt.Kind() {
	case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Uint8:
		x = int(v.Int()) % shard
	case reflect.String:
		x = int(util.Hash([]byte(v.String()))) % shard
	case reflect.Slice:
		x = int(util.Hash(v.Bytes())) % shard
	default:
		log.Fatalf("unexpected key to hash %s: %v", v.Kind().String(), v)
	}
	return x
}

func (d *Dataset) partition_scatter(shard int) (ret *Dataset) {
	ret = d.context.newNextDataset(len(d.Shards)*shard, d.Type)
	step := d.context.AddOneToEveryNStep(d, shard, ret)
	step.Name = "Partition_scatter"
	step.Function = func(task *Task) {
		for input := range task.InputChan() {
			x := hashByKey(input, shard)
			task.Outputs[x].WriteChan.Send(input)
		}
	}
	return
}

func (d *Dataset) partition_collect(shard int) (ret *Dataset) {
	ret = d.context.newNextDataset(shard, d.Type)
	step := d.context.AddLinkedNToOneStep(d, len(d.Shards)/shard, ret)
	step.Name = "Partition_collect"
	step.Function = func(task *Task) {
		for input := range task.MergedInputChan() {
			task.Outputs[0].WriteChan.Send(input)
		}
	}
	return
}
<|endoftext|>


<commit_before>package immortal

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"syscall"
	"time"
)

// Process interface
type Process interface {
	Kill() error
	Pid() int
	Signal(sig syscall.Signal) error
	Start() (*process, error)
}

type process struct {
	*Config
	Logger
	LoggerStderr Logger
	cmd          *exec.Cmd
	errch        chan error
	quit         chan struct{}
	sTime, eTime time.Time
}

// Start runs the command
func (p *process) Start() (*process, error) {
	// command obtained from Config parent
	p.cmd = exec.Command(p.command[0], p.command[1:]...)

	// change working directory
	if p.Cwd != "" {
		p.cmd.Dir = p.Cwd
	}

	// set environment vars
	if p.Env != nil {
		env := os.Environ()
		for k, v := range p.Env {
			env = append(env, fmt.Sprintf("%s=%s", k, v))
		}
		p.cmd.Env = env
	}

	sysProcAttr := new(syscall.SysProcAttr)

	// set owner
	if p.user != nil {
		uid, err := strconv.Atoi(p.user.Uid)
		if err != nil {
			return nil, err
		}
		gid, err := strconv.Atoi(p.user.Gid)
		if err != nil {
			return nil, err
		}
		sysProcAttr.Credential = &syscall.Credential{
			Uid: uint32(uid),
			Gid: uint32(gid),
		}
	}

	// Set Process group ID to Pgid, or, if Pgid == 0, to new pid
	sysProcAttr.Setpgid = true
	sysProcAttr.Pgid = 0

	// set the attributes
	p.cmd.SysProcAttr = sysProcAttr

	var (
		prStdout, prStderr, pwStdout, pwStderr *os.File
		e                                      error
	)
	// log only if are available loggers
	if p.Logger.IsLogging() && p.LoggerStderr.IsLogging() {
		// create the pipes for Stdout
		prStdout, pwStdout, e = os.Pipe()
		if e == nil {
			p.cmd.Stdout = pwStdout
			go p.Logger.Log(prStdout)
		}
		prStderr, pwStderr, e = os.Pipe()
		if e == nil {
			p.cmd.Stderr = pwStderr
			go p.LoggerStderr.Log(prStderr)
		}
	} else if p.Logger.IsLogging() {
		// create the pipes for Stdout
		prStdout, pwStdout, e = os.Pipe()
		if e == nil {
			p.cmd.Stdout = pwStdout
			p.cmd.Stderr = pwStdout
			go p.Logger.Log(prStdout)
		}
	} else if p.LoggerStderr.IsLogging() {
		// create the pipes for Stdout
		prStderr, pwStderr, e = os.Pipe()
		if e == nil {
			p.cmd.Stderr = pwStderr
			go p.LoggerStderr.Log(prStderr)
		}
	}

	// Start the process
	if err := p.cmd.Start(); err != nil {
		return nil, err
	}

	// set start time
	p.sTime = time.Now()

	// create error channel
	p.errch = make(chan error, 1)

	// wait process to finish
	go func(wStdout, wStderr *os.File) {
		err := p.cmd.Wait()
		if wStdout != nil {
			wStdout.Close()
			close(p.quit)
		}
		if wStderr != nil {
			wStderr.Close()
		}
		p.errch <- err
	}(pwStdout, pwStderr)

	return p, nil
}

// Kill the entire Process group.
func (p *process) Kill() error {
	processGroup := 0 - p.cmd.Process.Pid
	return syscall.Kill(processGroup, syscall.SIGKILL)
}

// Pid return Process PID
func (p *process) Pid() int {
	if p.cmd == nil || p.cmd.Process == nil {
		return 0
	}
	return p.cmd.Process.Pid
}

// Signal sends a signal to the Process
func (p *process) Signal(sig syscall.Signal) error {
	return syscall.Kill(p.cmd.Process.Pid, sig)
}

// NewProcess return process instance
func NewProcess(cfg *Config) *process {
	qch := make(chan struct{})
	return &process{
		Config: cfg,
		Logger: &LogWriter{
			logger: NewLogger(cfg, qch),
		},
		LoggerStderr: &LogWriter{
			logger: NewStderrLogger(cfg),
		},
		quit: qch,
	}
}
<commit_msg>update<commit_after>package immortal

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"syscall"
	"time"
)

// Process interface
type Process interface {
	Kill() error
	Pid() int
	Signal(sig syscall.Signal) error
	Start() (*process, error)
}

type process struct {
	*Config
	Logger
	LoggerStderr Logger
	cmd          *exec.Cmd
	errch        chan error
	quit         chan struct{}
	sTime, eTime time.Time
}

// SetEnv set environment variables - If the Cmd.Env contains duplicate
// environment keys, only the last value in the slice for each duplicate
// key is used.
func (p *process) SetEnv(env []string) {
	if p.Env != nil {
		for k, v := range p.Env {
			env = append(env, fmt.Sprintf("%s=%s", k, v))
		}
		p.cmd.Env = env
	}
}

// SetsysProcAttr - set process owner (run on behalf)
func (p *process) SetsysProcAttr() error {
	sysProcAttr := &syscall.SysProcAttr{
		Setpgid: true, // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
		Pgid:    0,    // Child's process group ID if Setpgid.
	}

	// set owner
	if p.user != nil {
		uid, err := strconv.Atoi(p.user.Uid)
		if err != nil {
			return err
		}
		gid, err := strconv.Atoi(p.user.Gid)
		if err != nil {
			return err
		}
		sysProcAttr.Credential = &syscall.Credential{
			Uid: uint32(uid),
			Gid: uint32(gid),
		}
	}

	// set the attributes
	p.cmd.SysProcAttr = sysProcAttr
}

// Start runs the command
func (p *process) Start() (*process, error) {
	// command obtained from Config parent
	p.cmd = exec.Command(p.command[0], p.command[1:]...)

	// change working directory
	if p.Cwd != "" {
		p.cmd.Dir = p.Cwd
	}

	// set environment variables
	p.SetEnv(os.Environ())

	// set sysProcAttr
	if err := p.SetsysProcAttr(); err != nil {
		return nil, err
	}

	var (
		prStdout, prStderr, pwStdout, pwStderr *os.File
		e                                      error
	)
	// log only if are available loggers
	if p.Logger.IsLogging() && p.LoggerStderr.IsLogging() {
		// create the pipes for Stdout
		prStdout, pwStdout, e = os.Pipe()
		if e == nil {
			p.cmd.Stdout = pwStdout
			go p.Logger.Log(prStdout)
		}
		prStderr, pwStderr, e = os.Pipe()
		if e == nil {
			p.cmd.Stderr = pwStderr
			go p.LoggerStderr.Log(prStderr)
		}
	} else if p.Logger.IsLogging() {
		// create the pipes for Stdout
		prStdout, pwStdout, e = os.Pipe()
		if e == nil {
			p.cmd.Stdout = pwStdout
			p.cmd.Stderr = pwStdout
			go p.Logger.Log(prStdout)
		}
	} else if p.LoggerStderr.IsLogging() {
		// create the pipes for Stdout
		prStderr, pwStderr, e = os.Pipe()
		if e == nil {
			p.cmd.Stderr = pwStderr
			go p.LoggerStderr.Log(prStderr)
		}
	}

	// Start the process
	if err := p.cmd.Start(); err != nil {
		return nil, err
	}

	// set start time
	p.sTime = time.Now()

	// create error channel
	p.errch = make(chan error, 1)

	// wait process to finish
	go func(wStdout, wStderr *os.File) {
		err := p.cmd.Wait()
		if wStdout != nil {
			wStdout.Close()
			close(p.quit)
		}
		if wStderr != nil {
			wStderr.Close()
		}
		p.errch <- err
	}(pwStdout, pwStderr)

	return p, nil
}

// Kill the entire Process group.
func (p *process) Kill() error {
	processGroup := 0 - p.cmd.Process.Pid
	return syscall.Kill(processGroup, syscall.SIGKILL)
}

// Pid return Process PID
func (p *process) Pid() int {
	if p.cmd == nil || p.cmd.Process == nil {
		return 0
	}
	return p.cmd.Process.Pid
}

// Signal sends a signal to the Process
func (p *process) Signal(sig syscall.Signal) error {
	return syscall.Kill(p.cmd.Process.Pid, sig)
}

// NewProcess return process instance
func NewProcess(cfg *Config) *process {
	qch := make(chan struct{})
	return &process{
		Config: cfg,
		Logger: &LogWriter{
			logger: NewLogger(cfg, qch),
		},
		LoggerStderr: &LogWriter{
			logger: NewStderrLogger(cfg),
		},
		quit: qch,
	}
}
<|endoftext|>


<commit_before>package ostential
import (
	"os"
	"log"
	"fmt"
	"time"
	"flag"
	"sync"
	"strings"
	"net/url"
	"net/http"
	"encoding/json"
	gorillawebsocket "github.com/gorilla/websocket"
)

type Duration time.Duration

func(d Duration) String() string {
	s := time.Duration(d).String()
	if strings.HasSuffix(s, "m0s") {
		s = strings.TrimSuffix(s, "0s")
	}
	if strings.HasSuffix(s, "h0m") {
		s = strings.TrimSuffix(s, "0m")
	}
	return s
}

func(d Duration) MarshalJSON() ([]byte, error) {
	return json.Marshal(d.String())
}

type periodValue struct {
	Duration
	above *Duration // optional
}

func(pv *periodValue) Set(input string) error {
	v, err := time.ParseDuration(input)
	if err != nil {
		return err
	}
	if v < time.Second { // hard coded
		return fmt.Errorf("Less than a second: %s", v)
	}
	if v % time.Second != 0 {
		return fmt.Errorf("Not a multiple of a second: %s", v)
	}
	if pv.above != nil && v < time.Duration(*pv.above) {
		return fmt.Errorf("Should be above %s: %s", *pv.above, v)
	}
	pv.Duration = Duration(v)
	return nil
}

var periodFlag = periodValue{Duration: Duration(time.Second)} // default
func init() {
	flag.Var(&periodFlag, "u",      "Collection (update) interval")
	flag.Var(&periodFlag, "update", "Collection (update) interval")
}

var _ = os.Stdout
// var wslog = log.New(os.Stdout, "", log.LstdFlags|log.Lmicroseconds)

func Loop() {
	// flags must be parsed by now, `periodFlag' is used here
	go func() {
		for {
			now := time.Now()
			nextsecond := now.Truncate(time.Second).Add(time.Second).Sub(now)
			<-time.After(nextsecond)

			if connections.expires() {
				// wslog.Printf("Have expires, COLLECT\n")
				lastInfo.collect()
			} else {
				// wslog.Printf("NO REFRESH\n")
			}
			connections.tack()
		}
	}()

	for {
		select {
		case conn := <-register:
			connections.reg(conn)

		case conn := <-unregister:
			if connections.unreg(conn) == 0 { // if no connections left
				lastInfo.reset_prev()
			}
		}
	}
}

type conn struct {
	Conn *gorillawebsocket.Conn
	requestOrigin *http.Request
	receive chan *received
	full client
	mutex sync.Mutex
}

func(c *conn) expires() bool {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	refreshes := map[string]*refresh{
		// a map by _string_ is for debug only; otherwise that would be a []*refresh
		"expires MEM": c.full.RefreshMEM,
		"expires IF":  c.full.RefreshIF,
		"expires CPU": c.full.RefreshCPU,
		"expires DF":  c.full.RefreshDF,
		"expires PS":  c.full.RefreshPS,
		"expires VG":  c.full.RefreshVG,
	}

	expires := false
	for lprefix, refresh := range refreshes {
		if !refresh.expires() {
			continue
		}
		return true
		log.Println(lprefix, refresh)
		expires = false
	}
	return expires
}

type connmap map[*conn]struct{}
type conns struct {
	connmap
	mutex sync.Mutex
}

func (cs *conns) tack() {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()

	for c := range cs.connmap {
		c.receive <- nil
	}
}

func (cs *conns) reg(c *conn) {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()

	cs.connmap[c] = struct{}{}
}

func (c *conn) close_chan() {
	c.mutex.Lock()
	defer func() {
		defer c.mutex.Unlock()
		if e := recover(); e != nil {
			if err, ok := e.(error); ok {
				fmt.Printf("CLOSE? PANIC %s\n", err.Error())
			} else {
				fmt.Printf("CLOSE? PANIC (UNDESCRIPT) %+v\n", e)
			}
			panic(e)
		}
	}()
	close(c.receive)
}

func (cs *conns) unreg(c *conn) int {
	c.close_chan()

	cs.mutex.Lock()
	defer cs.mutex.Unlock()

	delete(cs.connmap, c)
	return len(cs.connmap)
}

func (cs *conns) expires() bool {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()

	for c := range cs.connmap {
		if c.expires() {
			return true
		}
	}
	return false
}

var (
	connections = conns{connmap: map[*conn]struct{}{}}
	unregister = make(chan *conn)
	  register = make(chan *conn)
)

type recvClient struct {
	commonClient
	MorePsignal *bool
	RefreshSignalMEM *string
	RefreshSignalIF  *string
	RefreshSignalCPU *string
	RefreshSignalDF  *string
	RefreshSignalPS  *string
	RefreshSignalVG  *string
}

func (rs *recvClient) mergeMorePsignal(cs *client) {
	if rs.MorePsignal == nil {
		return
	}
	if *rs.MorePsignal {
		if cs.psLimit < 65536 {
			cs.psLimit *= 2
		}
	} else if cs.psLimit >= 2 {
		cs.psLimit /= 2
	}
	rs.MorePsignal = nil
}

func (rs *recvClient) mergeRefreshSignal(ppinput *string, prefresh *refresh, sendr **refresh, senderr **bool) error {
	if ppinput == nil {
		return nil
	}
	pv := periodValue{above: &periodFlag.Duration}
	if err := pv.Set(*ppinput); err != nil {
		*senderr = newtrue()
		return err
	}
	*senderr = newfalse()
	*sendr = new(refresh)
	(**sendr).Duration = pv.Duration
	prefresh.Duration = pv.Duration
	prefresh.tick = 0
	return nil
}

func (rs *recvClient) MergeClient(cs *client, send *sendClient) error {
	rs.mergeMorePsignal(cs)
	if err := rs.mergeRefreshSignal(rs.RefreshSignalMEM, cs.RefreshMEM, &send.RefreshMEM, &send.RefreshErrorMEM); err != nil { return err }
	if err := rs.mergeRefreshSignal(rs.RefreshSignalIF,  cs.RefreshIF,  &send.RefreshIF,  &send.RefreshErrorIF);  err != nil { return err }
	if err := rs.mergeRefreshSignal(rs.RefreshSignalCPU, cs.RefreshCPU, &send.RefreshCPU, &send.RefreshErrorCPU); err != nil { return err }
	if err := rs.mergeRefreshSignal(rs.RefreshSignalDF,  cs.RefreshDF,  &send.RefreshDF,  &send.RefreshErrorDF);  err != nil { return err }
	if err := rs.mergeRefreshSignal(rs.RefreshSignalPS,  cs.RefreshPS,  &send.RefreshPS,  &send.RefreshErrorPS);  err != nil { return err }
	if err := rs.mergeRefreshSignal(rs.RefreshSignalVG,  cs.RefreshVG,  &send.RefreshVG,  &send.RefreshErrorVG);  err != nil { return err }
	return nil
}

type received struct {
	Search *string
	Client *recvClient
}

type served struct {
	conn *conn // passing conn into received.ServeHTTP
	received *received
}

func(c *conn) writeError(err error) bool {
	type errorJSON struct {
		Error string
	}
	if c.Conn.WriteJSON(errorJSON{err.Error()}) != nil {
		return false
	}
	return true
}

func(c *conn) waitfor_messages() { // read from the conn
	defer c.Conn.Close()
	for {
		rd := new(received)
		if err := c.Conn.ReadJSON(&rd); err != nil {
			break
		}
		c.receive <- rd
	}
}

func(c *conn) waitfor_updates() { // write to the conn
	defer func() {
		unregister <- c
		c.Conn.Close()
	}()
	for {
		select {
		case rd, ok := <- c.receive:
			if !ok {
				break
			}
			if next := c.tack(rd); next != nil {
				if *next {
					continue
				} else {
					break
				}
			}
		}
	}
}

func(c *conn) tack(rd *received) *bool {
	c.mutex.Lock()
	defer func() {
		c.mutex.Unlock()
		if e := recover(); e != nil {
			stack := ""
			/*
            // The stack to be fmt.Printf-d. Not sure if I should
			sbuf := make([]byte, 4096)
			size := runtime.Stack(sbuf, false)
			stack = string(sbuf[:size])
			*/

			if err, ok := e.(error); ok {
				c.writeError(err) // an alert for the client

				fmt.Printf("PANIC %s\n%s\n", err.Error(), stack)
			} else {
				fmt.Printf("PANIC (UNDESCRIPT) %+v\n%s\n", e, stack)
			}
			panic(e)
		}
	}()

	var req *http.Request
	if form, err := rd.form(); err != nil {
		// if !c.writeError(err) { return newfalse() } // should I write an error?
		return newtrue() // continue receiving
	} else if form != nil {
		// compile an actual Request
		r := *c.requestOrigin
		r.Form = form
		req = &r // http.Request{Form: form}
	}

	sd := served{conn: c, received: rd}
	serve := sd.ServeHTTP // sd.ServeHTTP survives req being nil
	if req != nil { // the only case when req.Form is not nil
		// a non-nil req is no-go for stdaccess anyway
		serve = stdaccess.Constructor(sd).ServeHTTP
	}

	w := dummyStatus{}
	serve(w, req)

	if w.status == http.StatusBadRequest {
		return newfalse() // write failure, stop receiving
	}
	return nil
}

func (rd *received) form() (url.Values, error) {
	if rd == nil {
		return nil, nil
	}
	if rd.Search == nil {
		return nil, nil
	}
	return url.ParseQuery(strings.TrimPrefix(*rd.Search, "?"))
	// url.ParseQuery should not return a nil url.Values without an error
}

func (sd served) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	stop := func() {
		w.WriteHeader(http.StatusBadRequest) // well, not a bad request but a write failure
	}
	send := sendClient{}
	if sd.received != nil {
		if sd.received.Client != nil {
			err := sd.received.Client.MergeClient(&sd.conn.full, &send)
			if err != nil {
				// if !sd.conn.Conn.writeError(err) { stop(); return }
				send.DebugError = new(string)
				*send.DebugError = err.Error()
			}
			sd.conn.full.Merge(*sd.received.Client, &send)
		}
	}

	updates := getUpdates(r, &sd.conn.full, send, false)

	if sd.conn.Conn.WriteJSON(updates) != nil {
		stop()
	}
	w.WriteHeader(http.StatusSwitchingProtocols) // last change to WriteHeader. 101 is 200
}

type dummyStatus struct { // yet another ResponseWriter
	status int
}

func (w dummyStatus) WriteHeader(s int) {
	w.status = s
	// don't expect any actual WriteHeader. This is dummy after all
}

func (w dummyStatus) Header() http.Header {
	panic("dummyStatus.Header: SHOULD NOT BE USED")
// 	return w.ResponseWriter.Header()
// 	return make(http.Header) // IF TO RETURN ANYTHING, THAT SHOULD BE ONE http.Header PER dummyStatus
}

func (w dummyStatus) Write(b []byte) (int, error) {
	panic("dummyStatus.Write: SHOULD NOT BE USED")
// 	return w.ResponseWriter.Write(b)
	return len(b), nil
}

func slashws(w http.ResponseWriter, req *http.Request) {
	// Upgrader.Upgrade() has Origin check if .CheckOrigin is nil
	upgrader := gorillawebsocket.Upgrader{}
	wsconn, err := upgrader.Upgrade(w, req, nil)
	if err != nil { // Upgrade() does http.Error() to the client
		return
	}

	// req.Method == "GET" asserted by the mux
	req.Form = nil // reset reused later .Form
	c := &conn{
		Conn: wsconn,
		requestOrigin: req,
		receive: make(chan *received, 2),
		full: defaultClient(),
	}
	register <- c
	defer func() {
		unregister <- c
	}()
	go c.waitfor_messages() // read from the client
	   c.waitfor_updates()  // write to the client
}
<commit_msg>pushable (off-the-clock) partial pageUpdate. no use yet<commit_after>package ostential
import (
	"os"
	"log"
	"fmt"
	"time"
	"flag"
	"sync"
	"strings"
	"net/url"
	"net/http"
	"encoding/json"
	gorillawebsocket "github.com/gorilla/websocket"
)

// Duration type derives time.Duration
type Duration time.Duration

// String returns Duration string representation
func(d Duration) String() string {
	s := time.Duration(d).String()
	if strings.HasSuffix(s, "m0s") {
		s = strings.TrimSuffix(s, "0s")
	}
	if strings.HasSuffix(s, "h0m") {
		s = strings.TrimSuffix(s, "0m")
	}
	return s
}

// MarshalJSON is for encoding/json marshaling into Duration string representation
func(d Duration) MarshalJSON() ([]byte, error) {
	return json.Marshal(d.String())
}

type periodValue struct {
	Duration
	above *Duration // optional
}

func(pv *periodValue) Set(input string) error {
	v, err := time.ParseDuration(input)
	if err != nil {
		return err
	}
	if v < time.Second { // hard coded
		return fmt.Errorf("Less than a second: %s", v)
	}
	if v % time.Second != 0 {
		return fmt.Errorf("Not a multiple of a second: %s", v)
	}
	if pv.above != nil && v < time.Duration(*pv.above) {
		return fmt.Errorf("Should be above %s: %s", *pv.above, v)
	}
	pv.Duration = Duration(v)
	return nil
}

var periodFlag = periodValue{Duration: Duration(time.Second)} // default
func init() {
	flag.Var(&periodFlag, "u",      "Collection (update) interval")
	flag.Var(&periodFlag, "update", "Collection (update) interval")
}

var _ = os.Stdout
// var wslog = log.New(os.Stdout, "", log.LstdFlags|log.Lmicroseconds)

// Loop is the ostent background job
func Loop() {
	// flags must be parsed by now, `periodFlag' is used here
	go func() {
		for {
			now := time.Now()
			nextsecond := now.Truncate(time.Second).Add(time.Second).Sub(now)
			<-time.After(nextsecond)

			if connections.expires() {
				// wslog.Printf("Have expires, COLLECT\n")
				lastInfo.collect()
			} else {
				// wslog.Printf("NO REFRESH\n")
			}
			connections.tack()
		}
	}()

	for {
		select {
		case update := <-pUPDATES:
			connections.push(update)

		case conn := <-register:
			connections.reg(conn)

		case conn := <-unregister:
			if connections.unreg(conn) == 0 { // if no connections left
				lastInfo.reset_prev()
			}
		}
	}
}

type conn struct {
	Conn *gorillawebsocket.Conn
	requestOrigin *http.Request
	receive chan *received
	push    chan *pageUpdate
	full client
	mutex sync.Mutex
}

func (c *conn) expires() bool {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	refreshes := map[string]*refresh{
		// a map by _string_ is for debug only; otherwise that would be a []*refresh
		"expires MEM": c.full.RefreshMEM,
		"expires IF":  c.full.RefreshIF,
		"expires CPU": c.full.RefreshCPU,
		"expires DF":  c.full.RefreshDF,
		"expires PS":  c.full.RefreshPS,
		"expires VG":  c.full.RefreshVG,
	}

	expires := false
	for lprefix, refresh := range refreshes {
		if !refresh.expires() {
			continue
		}
		return true
		log.Println(lprefix, refresh)
		expires = false
	}
	return expires
}

type connmap map[*conn]struct{}
type conns struct {
	connmap
	mutex sync.Mutex
}

func (cs *conns) tack() {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()

	for c := range cs.connmap {
		c.receive <- nil
	}
}

func (cs *conns) push(update *pageUpdate) {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()

	for c := range cs.connmap {
		c.push <- update
	}
}

func (cs *conns) reg(c *conn) {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()

	cs.connmap[c] = struct{}{}
}

func (c *conn) closeChans() {
	c.mutex.Lock()
	defer func() {
		defer c.mutex.Unlock()
		if e := recover(); e != nil {
			if err, ok := e.(error); ok {
				fmt.Printf("CLOSE? PANIC %s\n", err.Error())
			} else {
				fmt.Printf("CLOSE? PANIC (UNDESCRIPT) %+v\n", e)
			}
			panic(e)
		}
	}()
	close(c.receive)
	close(c.push)
}

func (cs *conns) unreg(c *conn) int {
	c.closeChans()

	cs.mutex.Lock()
	defer cs.mutex.Unlock()

	delete(cs.connmap, c)
	return len(cs.connmap)
}

func (cs *conns) expires() bool {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()

	for c := range cs.connmap {
		if c.expires() {
			return true
		}
	}
	return false
}

var (
	connections  = conns{connmap: map[*conn]struct{}{}}
	unregister   = make(chan *conn)
	  register   = make(chan *conn)

	// pUPDATES is the channel for off-the-clock pageUpdate[s] to push
	pUPDATES     = make(chan *pageUpdate)
)

type recvClient struct {
	commonClient
	MorePsignal *bool
	RefreshSignalMEM *string
	RefreshSignalIF  *string
	RefreshSignalCPU *string
	RefreshSignalDF  *string
	RefreshSignalPS  *string
	RefreshSignalVG  *string
}

func (rs *recvClient) mergeMorePsignal(cs *client) {
	if rs.MorePsignal == nil {
		return
	}
	if *rs.MorePsignal {
		if cs.psLimit < 65536 {
			cs.psLimit *= 2
		}
	} else if cs.psLimit >= 2 {
		cs.psLimit /= 2
	}
	rs.MorePsignal = nil
}

func (rs *recvClient) mergeRefreshSignal(ppinput *string, prefresh *refresh, sendr **refresh, senderr **bool) error {
	if ppinput == nil {
		return nil
	}
	pv := periodValue{above: &periodFlag.Duration}
	if err := pv.Set(*ppinput); err != nil {
		*senderr = newtrue()
		return err
	}
	*senderr = newfalse()
	*sendr = new(refresh)
	(**sendr).Duration = pv.Duration
	prefresh.Duration = pv.Duration
	prefresh.tick = 0
	return nil
}

func (rs *recvClient) MergeClient(cs *client, send *sendClient) error {
	rs.mergeMorePsignal(cs)
	if err := rs.mergeRefreshSignal(rs.RefreshSignalMEM, cs.RefreshMEM, &send.RefreshMEM, &send.RefreshErrorMEM); err != nil { return err }
	if err := rs.mergeRefreshSignal(rs.RefreshSignalIF,  cs.RefreshIF,  &send.RefreshIF,  &send.RefreshErrorIF);  err != nil { return err }
	if err := rs.mergeRefreshSignal(rs.RefreshSignalCPU, cs.RefreshCPU, &send.RefreshCPU, &send.RefreshErrorCPU); err != nil { return err }
	if err := rs.mergeRefreshSignal(rs.RefreshSignalDF,  cs.RefreshDF,  &send.RefreshDF,  &send.RefreshErrorDF);  err != nil { return err }
	if err := rs.mergeRefreshSignal(rs.RefreshSignalPS,  cs.RefreshPS,  &send.RefreshPS,  &send.RefreshErrorPS);  err != nil { return err }
	if err := rs.mergeRefreshSignal(rs.RefreshSignalVG,  cs.RefreshVG,  &send.RefreshVG,  &send.RefreshErrorVG);  err != nil { return err }
	return nil
}

type received struct {
	Search *string
	Client *recvClient
}

type served struct {
	conn *conn // passing conn into received.ServeHTTP
	received *received
}

func(c *conn) writeError(err error) bool {
	type errorJSON struct {
		Error string
	}
	return c.Conn.WriteJSON(errorJSON{err.Error()}) == nil
}

func(c *conn) receiveLoop() { // read from the conn
	defer c.Conn.Close()
	for {
		rd := new(received)
		if err := c.Conn.ReadJSON(&rd); err != nil {
			break
		}
		c.receive <- rd
	}
}

func(c *conn) updateLoop() { // write to the conn
	defer func() {
		unregister <- c
		c.Conn.Close()
	}()
	for {
		select {
		case rd, ok := <- c.receive:
			if !ok {
				break
			}
			if next := c.process(rd); next != nil {
				if *next {
					continue
				} else {
					break
				}
			}
		case update, ok := <- c.push:
			if !ok {
				break
			}
			if next := c.writeUpdate(*update); !next {
				break
			}
		}
	}
}

func(c *conn) process(rd *received) *bool {
	c.mutex.Lock()
	defer func() {
		c.mutex.Unlock()
		if e := recover(); e != nil {
			stack := ""
			/*
            // The stack to be fmt.Printf-d. Not sure if I should
			sbuf := make([]byte, 4096)
			size := runtime.Stack(sbuf, false)
			stack = string(sbuf[:size])
			*/

			if err, ok := e.(error); ok {
				c.writeError(err) // an alert for the client

				fmt.Printf("PANIC %s\n%s\n", err.Error(), stack)
			} else {
				fmt.Printf("PANIC (UNDESCRIPT) %+v\n%s\n", e, stack)
			}
			panic(e)
		}
	}()

	var req *http.Request
	if form, err := rd.form(); err != nil {
		// if !c.writeError(err) { return newfalse() } // should I write an error?
		return newtrue() // continue receiving
	} else if form != nil {
		// compile an actual Request
		r := *c.requestOrigin
		r.Form = form
		req = &r // http.Request{Form: form}
	}

	sd := served{conn: c, received: rd}
	serve := sd.ServeHTTP // sd.ServeHTTP survives req being nil
	if req != nil { // the only case when req.Form is not nil
		// a non-nil req is no-go for stdaccess anyway
		serve = stdaccess.Constructor(sd).ServeHTTP
	}

	w := dummyStatus{}
	serve(w, req)

	if w.status == http.StatusBadRequest {
		return newfalse() // write failure, stop receiving
	}
	return nil
}

func (rd *received) form() (url.Values, error) {
	if rd == nil || rd.Search == nil {
		return nil, nil
	}
	return url.ParseQuery(strings.TrimPrefix(*rd.Search, "?"))
	// url.ParseQuery should not return a nil url.Values without an error
}

func (sd served) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	stop := func() {
		w.WriteHeader(http.StatusBadRequest) // well, not a bad request but a write failure
	}
	send := sendClient{}
	if sd.received != nil {
		if sd.received.Client != nil {
			err := sd.received.Client.MergeClient(&sd.conn.full, &send)
			if err != nil {
				// if !sd.conn.Conn.writeError(err) { stop(); return }
				send.DebugError = new(string)
				*send.DebugError = err.Error()
			}
			sd.conn.full.Merge(*sd.received.Client, &send)
		}
	}

	update := getUpdates(r, &sd.conn.full, send, false)

	if sd.conn.Conn.WriteJSON(update) != nil {
		stop()
		return
	}
	w.WriteHeader(http.StatusSwitchingProtocols) // last change to WriteHeader. 101 is 200
}

func (c *conn) writeUpdate(update pageUpdate) bool {
	if *c.full.HideVG && update.VagrantMachines != nil {
		// TODO other .Vagrant* fields may not be discarded
		return true
	}
	return c.Conn.WriteJSON(update) == nil
}

type dummyStatus struct { // yet another ResponseWriter
	status int
}

func (w dummyStatus) WriteHeader(s int) {
	w.status = s
	// don't expect any actual WriteHeader. This is dummy after all
}

func (w dummyStatus) Header() http.Header {
	panic("dummyStatus.Header: SHOULD NOT BE USED")
// 	return w.ResponseWriter.Header()
// 	return make(http.Header) // IF TO RETURN ANYTHING, THAT SHOULD BE ONE http.Header PER dummyStatus
}

func (w dummyStatus) Write(b []byte) (int, error) {
	panic("dummyStatus.Write: SHOULD NOT BE USED")
// 	return w.ResponseWriter.Write(b)
	return len(b), nil
}

func slashws(w http.ResponseWriter, req *http.Request) {
	// Upgrader.Upgrade() has Origin check if .CheckOrigin is nil
	upgrader := gorillawebsocket.Upgrader{}
	wsconn, err := upgrader.Upgrade(w, req, nil)
	if err != nil { // Upgrade() does http.Error() to the client
		return
	}

	// req.Method == "GET" asserted by the mux
	req.Form = nil // reset reused later .Form
	c := &conn{
		Conn: wsconn,
		requestOrigin: req,
		receive: make(chan *received,   2),
		push:    make(chan *pageUpdate, 2),
		full: defaultClient(),
	}
	register <- c
	defer func() {
		unregister <- c
	}()
	go c.receiveLoop() // read from the client
	   c.updateLoop()  // write to the client
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"image/color"
	"time"

	"../libs/twodee"
)

const (
	overlayEndFrame = 0
)

type OverlayLayer struct {
	game             *GameLayer
	app              *Application
	events           *twodee.GameEventHandler
	tileRenderer     *twodee.TileRenderer
	bounds           twodee.Rectangle
	text             *twodee.TextRenderer
	regFont          *twodee.FontFace
	tileM            twodee.TileMetadata
	offset           twodee.Point
	cheevosCache     map[int]*twodee.TextCache
	visible          bool
	frame            int
	gameOverObserver int
}

func NewOverlayLayer(app *Application, game *GameLayer) (layer *OverlayLayer, err error) {
	var (
		regFont *twodee.FontFace
		bg      = color.Transparent
		exoFont = "assets/fonts/Exo-SemiBold.ttf"
		tileM   twodee.TileMetadata
	)
	if regFont, err = twodee.NewFontFace(exoFont, 24, regColor, bg); err != nil {
		return
	}
	tileM = twodee.TileMetadata{
		// TODO: Get an overlay png full of cool tiles.
		Path:       "assets/sun.png",
		PxPerUnit:  320,
		TileWidth:  320,
		TileHeight: 320,
		FramesWide: 1,
		FramesHigh: 1,
	}
	layer = &OverlayLayer{
		app:          app,
		game:         game,
		events:       app.GameEventHandler,
		bounds:       app.WinBounds,
		regFont:      regFont,
		offset:       twodee.Pt(80, 500),
		cheevosCache: map[int]*twodee.TextCache{},
		visible:      false,
		tileM:        tileM,
	}
	layer.Reset()
	return
}

func (l *OverlayLayer) OnGameOver(e twodee.GETyper) {
	l.visible = true
}

func (l *OverlayLayer) Delete() {
	l.visible = false
	if l.tileRenderer != nil {
		l.tileRenderer.Delete()
	}
	for _, v := range l.cheevosCache {
		v.Clear()
	}
	l.events.RemoveObserver(GameOver, l.gameOverObserver)
}

func (l *OverlayLayer) Show(frame int) {
	l.visible = true
	l.frame = frame
}

func (l *OverlayLayer) Render() {
	if !l.visible {
		return
	}
	var (
		y       = l.bounds.Max.Y - l.offsetY
		x       = l.offset.X
		texture *twodee.Texture
	)
	l.tileRenderer.Bind()
	l.tileRenderer.Draw(l.frame, 0.5, 0.5, 0, false, false)
	l.tileRenderer.Unbind()

	l.text.Bind()
	for i, item := range l.game.Cheevos.GetSatisfied() {
		if textCache, ok = l.cache[i]; !ok {
			textCache = twodee.NewTextCache(l.regFont)
			l.cache[i] = textCache
		}
		textCache.SetText(item.Label())
		texture = textCache.Texture
		if texture != nil {
			y = y - float32(texture.Height)
			l.text.Draw(texture, x, y)
		}
		// FINISH
	}
	text := fmt.Sprintf("hihi\nhow\nare\nyou")
	l.cheevosText.SetText(text)
	if l.cheevosText.Texture != nil {
		y = maxY - float32(l.cheevosText.Texture.Height)
		l.text.Draw(l.cheevosText.Texture, 5, y)
	}
	l.text.Unbind()
}

func (l *OverlayLayer) HandleEvent(evt twodee.Event) bool {
	if !l.visible {
		return true
	}
	switch event := evt.(type) {
	case *twodee.KeyEvent:
		if event.Type != twodee.Press {
			break
		}
		switch event.Code {
		case twodee.KeyEnter:
			return l.NewGame()
		}
	}
	// Handle all events.
	return false
}

func (l *OverlayLayer) NewGame() bool {
	if err := l.Reset(); err != nil {
		// TODO: Ugh, see if we can reset GameLayer?
		return false
	}
	return false
}

func (l *OverlayLayer) Update(elapsed time.Duration) {
}

func (l *OverlayLayer) Reset() (err error) {
	l.Delete()
	if l.tileRenderer, err = twodee.NewTileRenderer(l.bounds, l.app.WinBounds, l.tileM); err != nil {
		return
	}
	l.gameOverObserver = l.events.AddObserver(GameOver, l.OnGameOver)
	if l.text, err = twodee.NewTextRenderer(l.bounds); err != nil {
		return
	}
	return
}
<commit_msg>Hook up final overlay to cheevos list.<commit_after>package main

import (
	"image/color"
	"time"

	"../libs/twodee"
)

const (
	overlayEndFrame = 0
)

type OverlayLayer struct {
	game             *GameLayer
	app              *Application
	events           *twodee.GameEventHandler
	tileRenderer     *twodee.TileRenderer
	bounds           twodee.Rectangle
	text             *twodee.TextRenderer
	regFont          *twodee.FontFace
	tileM            twodee.TileMetadata
	offset           twodee.Point
	cheevosCache     map[int]*twodee.TextCache
	visible          bool
	frame            int
	gameOverObserver int
}

func NewOverlayLayer(app *Application, game *GameLayer) (layer *OverlayLayer, err error) {
	var (
		regFont *twodee.FontFace
		bg      = color.Transparent
		exoFont = "assets/fonts/Exo-SemiBold.ttf"
		tileM   twodee.TileMetadata
	)
	if regFont, err = twodee.NewFontFace(exoFont, 24, regColor, bg); err != nil {
		return
	}
	tileM = twodee.TileMetadata{
		// TODO: Get an overlay png full of cool tiles.
		Path:       "assets/sun.png",
		PxPerUnit:  320,
		TileWidth:  320,
		TileHeight: 320,
		FramesWide: 1,
		FramesHigh: 1,
	}
	layer = &OverlayLayer{
		app:          app,
		game:         game,
		events:       app.GameEventHandler,
		bounds:       app.WinBounds,
		regFont:      regFont,
		offset:       twodee.Pt(80, 500),
		cheevosCache: map[int]*twodee.TextCache{},
		visible:      false,
		tileM:        tileM,
	}
	layer.Reset()
	return
}

func (l *OverlayLayer) OnGameOver(e twodee.GETyper) {
	l.visible = true
}

func (l *OverlayLayer) Delete() {
	l.visible = false
	if l.tileRenderer != nil {
		l.tileRenderer.Delete()
	}
	for _, v := range l.cheevosCache {
		v.Clear()
	}
	l.events.RemoveObserver(GameOver, l.gameOverObserver)
}

func (l *OverlayLayer) Show(frame int) {
	l.visible = true
	l.frame = frame
}

func (l *OverlayLayer) Render() {
	if !l.visible {
		return
	}
	var (
		y         = l.bounds.Max.Y - l.offset.Y
		x         = l.offset.X
		texture   *twodee.Texture
		textCache *twodee.TextCache
		ok        bool
	)
	l.tileRenderer.Bind()
	l.tileRenderer.Draw(l.frame, 0.5, 0.5, 0, false, false)
	l.tileRenderer.Unbind()

	l.text.Bind()
	for i, item := range l.game.Cheevos.Passed {
		if textCache, ok = l.cheevosCache[i]; !ok {
			textCache = twodee.NewTextCache(l.regFont)
			l.cheevosCache[i] = textCache
		}
		textCache.SetText(item)
		texture = textCache.Texture
		if texture != nil {
			y = y - float32(texture.Height)
			l.text.Draw(texture, x, y)
		}
	}
	l.text.Unbind()
}

func (l *OverlayLayer) HandleEvent(evt twodee.Event) bool {
	if !l.visible {
		return true
	}
	switch event := evt.(type) {
	case *twodee.KeyEvent:
		if event.Type != twodee.Press {
			break
		}
		switch event.Code {
		case twodee.KeyEnter:
			return l.NewGame()
		}
	}
	// Handle all events.
	return false
}

func (l *OverlayLayer) NewGame() bool {
	if err := l.Reset(); err != nil {
		// TODO: Ugh, see if we can reset GameLayer?
		return false
	}
	return false
}

func (l *OverlayLayer) Update(elapsed time.Duration) {
}

func (l *OverlayLayer) Reset() (err error) {
	l.Delete()
	if l.tileRenderer, err = twodee.NewTileRenderer(l.bounds, l.app.WinBounds, l.tileM); err != nil {
		return
	}
	l.gameOverObserver = l.events.AddObserver(GameOver, l.OnGameOver)
	if l.text, err = twodee.NewTextRenderer(l.bounds); err != nil {
		return
	}
	return
}
<|endoftext|>


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

package objabi

import (
	"fmt"
	"os"
	"reflect"
	"strings"

	"internal/goexperiment"
)

// Experiment contains the toolchain experiments enabled for the
// current build.
//
// (This is not necessarily the set of experiments the compiler itself
// was built with.)
var Experiment goexperiment.Flags = parseExperiments()

// FramePointerEnabled enables the use of platform conventions for
// saving frame pointers.
//
// This used to be an experiment, but now it's always enabled on
// platforms that support it.
//
// Note: must agree with runtime.framepointer_enabled.
var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"

func parseExperiments() goexperiment.Flags {
	// Start with the statically enabled set of experiments.
	flags := goexperiment.BaselineFlags

	// Pick up any changes to the baseline configuration from the
	// GOEXPERIMENT environment. This can be set at make.bash time
	// and overridden at build time.
	env := envOr("GOEXPERIMENT", defaultGOEXPERIMENT)

	if env != "" {
		// Create a map of known experiment names.
		names := make(map[string]reflect.Value)
		rv := reflect.ValueOf(&flags).Elem()
		rt := rv.Type()
		for i := 0; i < rt.NumField(); i++ {
			field := rv.Field(i)
			names[strings.ToLower(rt.Field(i).Name)] = field
		}

		// Parse names.
		for _, f := range strings.Split(env, ",") {
			if f == "" {
				continue
			}
			if f == "none" {
				// GOEXPERIMENT=none restores the baseline configuration.
				// (This is useful for overriding make.bash-time settings.)
				flags = goexperiment.BaselineFlags
				continue
			}
			val := true
			if strings.HasPrefix(f, "no") {
				f, val = f[2:], false
			}
			field, ok := names[f]
			if !ok {
				fmt.Printf("unknown experiment %s\n", f)
				os.Exit(2)
			}
			field.SetBool(val)
		}
	}

	// regabi is only supported on amd64.
	if GOARCH != "amd64" {
		flags.Regabi = false
		flags.RegabiWrappers = false
		flags.RegabiG = false
		flags.RegabiReflect = false
		flags.RegabiDefer = false
		flags.RegabiArgs = false
	}
	// Setting regabi sets working sub-experiments.
	if flags.Regabi {
		flags.RegabiWrappers = true
		flags.RegabiG = true
		flags.RegabiReflect = true
		flags.RegabiDefer = true
		// Not ready yet:
		//flags.RegabiArgs = true
	}
	// Check regabi dependencies.
	if flags.RegabiG && !flags.RegabiWrappers {
		panic("GOEXPERIMENT regabig requires regabiwrappers")
	}
	if flags.RegabiArgs && !(flags.RegabiWrappers && flags.RegabiG && flags.RegabiReflect && flags.RegabiDefer) {
		panic("GOEXPERIMENT regabiargs requires regabiwrappers,regabig,regabireflect,regabidefer")
	}
	return flags
}

// expList returns the list of lower-cased experiment names for
// experiments that differ from base. base may be nil to indicate no
// experiments.
func expList(exp, base *goexperiment.Flags) []string {
	var list []string
	rv := reflect.ValueOf(exp).Elem()
	var rBase reflect.Value
	if base != nil {
		rBase = reflect.ValueOf(base).Elem()
	}
	rt := rv.Type()
	for i := 0; i < rt.NumField(); i++ {
		name := strings.ToLower(rt.Field(i).Name)
		val := rv.Field(i).Bool()
		baseVal := false
		if base != nil {
			baseVal = rBase.Field(i).Bool()
		}
		if val != baseVal {
			if val {
				list = append(list, name)
			} else {
				list = append(list, "no"+name)
			}
		}
	}
	return list
}

// GOEXPERIMENT is a comma-separated list of enabled or disabled
// experiments that differ from the baseline experiment configuration.
// GOEXPERIMENT is exactly what a user would set on the command line
// to get the set of enabled experiments.
func GOEXPERIMENT() string {
	return strings.Join(expList(&Experiment, &goexperiment.BaselineFlags), ",")
}

// EnabledExperiments returns a list of enabled experiments, as
// lower-cased experiment names.
func EnabledExperiments() []string {
	return expList(&Experiment, nil)
}
<commit_msg>cmd/internal/objabi: make GOEXPERIMENT=none mean "no experiment flags"<commit_after>// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package objabi

import (
	"fmt"
	"os"
	"reflect"
	"strings"

	"internal/goexperiment"
)

// Experiment contains the toolchain experiments enabled for the
// current build.
//
// (This is not necessarily the set of experiments the compiler itself
// was built with.)
var Experiment goexperiment.Flags = parseExperiments()

// FramePointerEnabled enables the use of platform conventions for
// saving frame pointers.
//
// This used to be an experiment, but now it's always enabled on
// platforms that support it.
//
// Note: must agree with runtime.framepointer_enabled.
var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"

func parseExperiments() goexperiment.Flags {
	// Start with the statically enabled set of experiments.
	flags := goexperiment.BaselineFlags

	// Pick up any changes to the baseline configuration from the
	// GOEXPERIMENT environment. This can be set at make.bash time
	// and overridden at build time.
	env := envOr("GOEXPERIMENT", defaultGOEXPERIMENT)

	if env != "" {
		// Create a map of known experiment names.
		names := make(map[string]reflect.Value)
		rv := reflect.ValueOf(&flags).Elem()
		rt := rv.Type()
		for i := 0; i < rt.NumField(); i++ {
			field := rv.Field(i)
			names[strings.ToLower(rt.Field(i).Name)] = field
		}

		// Parse names.
		for _, f := range strings.Split(env, ",") {
			if f == "" {
				continue
			}
			if f == "none" {
				// GOEXPERIMENT=none disables all experiment flags.
				// This is used by cmd/dist, which doesn't know how
				// to build with any experiment flags.
				flags = goexperiment.Flags{}
				continue
			}
			val := true
			if strings.HasPrefix(f, "no") {
				f, val = f[2:], false
			}
			field, ok := names[f]
			if !ok {
				fmt.Printf("unknown experiment %s\n", f)
				os.Exit(2)
			}
			field.SetBool(val)
		}
	}

	// regabi is only supported on amd64.
	if GOARCH != "amd64" {
		flags.Regabi = false
		flags.RegabiWrappers = false
		flags.RegabiG = false
		flags.RegabiReflect = false
		flags.RegabiDefer = false
		flags.RegabiArgs = false
	}
	// Setting regabi sets working sub-experiments.
	if flags.Regabi {
		flags.RegabiWrappers = true
		flags.RegabiG = true
		flags.RegabiReflect = true
		flags.RegabiDefer = true
		// Not ready yet:
		//flags.RegabiArgs = true
	}
	// Check regabi dependencies.
	if flags.RegabiG && !flags.RegabiWrappers {
		panic("GOEXPERIMENT regabig requires regabiwrappers")
	}
	if flags.RegabiArgs && !(flags.RegabiWrappers && flags.RegabiG && flags.RegabiReflect && flags.RegabiDefer) {
		panic("GOEXPERIMENT regabiargs requires regabiwrappers,regabig,regabireflect,regabidefer")
	}
	return flags
}

// expList returns the list of lower-cased experiment names for
// experiments that differ from base. base may be nil to indicate no
// experiments.
func expList(exp, base *goexperiment.Flags) []string {
	var list []string
	rv := reflect.ValueOf(exp).Elem()
	var rBase reflect.Value
	if base != nil {
		rBase = reflect.ValueOf(base).Elem()
	}
	rt := rv.Type()
	for i := 0; i < rt.NumField(); i++ {
		name := strings.ToLower(rt.Field(i).Name)
		val := rv.Field(i).Bool()
		baseVal := false
		if base != nil {
			baseVal = rBase.Field(i).Bool()
		}
		if val != baseVal {
			if val {
				list = append(list, name)
			} else {
				list = append(list, "no"+name)
			}
		}
	}
	return list
}

// GOEXPERIMENT is a comma-separated list of enabled or disabled
// experiments that differ from the baseline experiment configuration.
// GOEXPERIMENT is exactly what a user would set on the command line
// to get the set of enabled experiments.
func GOEXPERIMENT() string {
	return strings.Join(expList(&Experiment, &goexperiment.BaselineFlags), ",")
}

// EnabledExperiments returns a list of enabled experiments, as
// lower-cased experiment names.
func EnabledExperiments() []string {
	return expList(&Experiment, nil)
}
<|endoftext|>