<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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 framework

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"time"

	habv1beta1 "github.com/habitat-sh/habitat-operator/pkg/apis/habitat/v1beta1"

	appsv1beta1 "k8s.io/api/apps/v1beta1"
	"k8s.io/api/core/v1"
	apiv1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/util/yaml"
)

// CreateHabitat creates a Habitat.
func (f *Framework) CreateHabitat(habitat *habv1beta1.Habitat) error {
	return f.Client.Post().
		Namespace(TestNs).
		Resource(habv1beta1.HabitatResourcePlural).
		Body(habitat).
		Do().
		Error()
}

// WaitForResources waits until numPods are in the "Running" state.
// We wait for pods, because those take the longest to create.
// Waiting for anything else would be already testing.
func (f *Framework) WaitForResources(labelName, habitatName string, numPods int) error {
	return wait.Poll(2*time.Second, 5*time.Minute, func() (bool, error) {
		fs := fields.SelectorFromSet(fields.Set{
			"status.phase": "Running",
		})

		ls := labels.SelectorFromSet(labels.Set{
			labelName: habitatName,
		})

		pods, err := f.KubeClient.CoreV1().Pods(TestNs).List(metav1.ListOptions{FieldSelector: fs.String(), LabelSelector: ls.String()})
		if err != nil {
			return false, err
		}

		if len(pods.Items) != numPods {
			return false, nil
		}

		return true, nil
	})
}

func (f *Framework) WaitForEndpoints(habitatName string) error {
	return wait.Poll(time.Second, time.Minute*5, func() (bool, error) {
		ep, err := f.KubeClient.CoreV1().Endpoints(TestNs).Get(habitatName, metav1.GetOptions{})
		if err != nil {
			return false, err
		}

		if len(ep.Subsets) == 0 {
			return false, nil
		}

		if len(ep.Subsets[0].Addresses) == 0 {
			return false, nil
		}

		return true, nil
	})
}

// DeleteHabitat deletes a Habitat as a user would.
func (f *Framework) DeleteHabitat(habitatName string) error {
	return f.Client.Delete().
		Namespace(TestNs).
		Resource(habv1beta1.HabitatResourcePlural).
		Name(habitatName).
		Do().
		Error()
}

// DeleteService delete a Kubernetes service provided.
func (f *Framework) DeleteService(service string) error {
	return f.KubeClient.CoreV1().Services(TestNs).Delete(service, &metav1.DeleteOptions{})
}

func (f *Framework) createRBAC() error {
	// Create Service account.
	sa, err := convertServiceAccount("resources/operator/service-account.yml")
	if err != nil {
		return err
	}
	_, err = f.KubeClient.CoreV1().ServiceAccounts(TestNs).Create(sa)
	if err != nil {
		return err
	}

	// Create cluster role.
	cr, err := convertClusterRole("resources/operator/cluster-role.yml")
	if err != nil {
		return err
	}
	_, err = f.KubeClient.RbacV1().ClusterRoles().Create(cr)
	if err != nil {
		return err
	}

	// Create cluster role bindings.
	crb, err := convertClusterRoleBinding("resources/operator/cluster-role-binding.yml")
	if err != nil {
		return err
	}
	_, err = f.KubeClient.RbacV1().ClusterRoleBindings().Create(crb)
	if err != nil {
		return err
	}

	return nil
}

// convertServiceAccount takes in a path to the YAML file containing the manifest
// It converts it from that file to the ServiceAccount object.
func convertServiceAccount(pathToYaml string) (*apiv1.ServiceAccount, error) {
	sa := apiv1.ServiceAccount{}

	if err := convertToK8sResource(pathToYaml, &sa); err != nil {
		return nil, err
	}

	return &sa, nil
}

// convertClusterRole takes in a path to the YAML file containing the manifest.
// It converts the file to the ClusterRole object.
func convertClusterRole(pathToYaml string) (*rbacv1.ClusterRole, error) {
	cr := rbacv1.ClusterRole{}

	if err := convertToK8sResource(pathToYaml, &cr); err != nil {
		return nil, err
	}

	return &cr, nil
}

// convertClusterRoleBinding takes in a path to the YAML file containing the manifest.
// It converts the file to the ClusterRoleBinding object.
func convertClusterRoleBinding(pathToYaml string) (*rbacv1.ClusterRoleBinding, error) {
	crb := rbacv1.ClusterRoleBinding{}

	if err := convertToK8sResource(pathToYaml, &crb); err != nil {
		return nil, err
	}

	return &crb, nil
}

// ConvertDeployment takes in a path to the YAML file containing the manifest.
// It converts the file to the Deployment object.
func ConvertDeployment(pathToYaml string) (*appsv1beta1.Deployment, error) {
	d := appsv1beta1.Deployment{}

	if err := convertToK8sResource(pathToYaml, &d); err != nil {
		return nil, err
	}

	return &d, nil
}

// ConvertHabitat takes in a path to the YAML file containing the manifest.
// It converts the file to the Habitat object.
func ConvertHabitat(pathToYaml string) (*habv1beta1.Habitat, error) {
	hab := habv1beta1.Habitat{}

	if err := convertToK8sResource(pathToYaml, &hab); err != nil {
		return nil, err
	}

	return &hab, nil
}

// ConvertService takes in a path to the YAML file containing the manifest.
// It converts the file to the Service object.
func ConvertService(pathToYaml string) (*v1.Service, error) {
	s := v1.Service{}

	if err := convertToK8sResource(pathToYaml, &s); err != nil {
		return nil, err
	}

	return &s, nil
}

// ConvertSecret takes in a path to the YAML file containing the manifest.
// It converts the file to the Secret object.
func ConvertSecret(pathToYaml string) (*v1.Secret, error) {
	s := v1.Secret{}

	if err := convertToK8sResource(pathToYaml, &s); err != nil {
		return nil, err
	}

	return &s, nil
}

func convertToK8sResource(pathToYaml string, into interface{}) error {
	manifest, err := pathToOSFile(pathToYaml)
	if err != nil {
		return err
	}

	if err := yaml.NewYAMLToJSONDecoder(manifest).Decode(into); err != nil {
		return err
	}

	return nil
}

// pathToOSFile takes in a path and converts it to a File.
func pathToOSFile(relativePath string) (*os.File, error) {
	path, err := filepath.Abs(relativePath)
	if err != nil {
		return nil, err
	}

	manifest, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	return manifest, nil
}

// QueryService makes an HTTP GET request to `url` and returns the body.
func QueryService(url string) (string, error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("Habitat Service did not start correctly.")
	}

	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	return string(bodyBytes), nil
}
<commit_msg>e2e/v1beta1: Use HabitatInterface methods<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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 framework

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"time"

	habv1beta1 "github.com/habitat-sh/habitat-operator/pkg/apis/habitat/v1beta1"

	appsv1beta1 "k8s.io/api/apps/v1beta1"
	"k8s.io/api/core/v1"
	apiv1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/util/yaml"
)

// CreateHabitat creates a Habitat.
func (f *Framework) CreateHabitat(habitat *habv1beta1.Habitat) error {
	_, err := f.Client.Create(habitat)

	return err
}

// WaitForResources waits until numPods are in the "Running" state.
// We wait for pods, because those take the longest to create.
// Waiting for anything else would be already testing.
func (f *Framework) WaitForResources(labelName, habitatName string, numPods int) error {
	return wait.Poll(2*time.Second, 5*time.Minute, func() (bool, error) {
		fs := fields.SelectorFromSet(fields.Set{
			"status.phase": "Running",
		})

		ls := labels.SelectorFromSet(labels.Set{
			labelName: habitatName,
		})

		pods, err := f.KubeClient.CoreV1().Pods(TestNs).List(metav1.ListOptions{FieldSelector: fs.String(), LabelSelector: ls.String()})
		if err != nil {
			return false, err
		}

		if len(pods.Items) != numPods {
			return false, nil
		}

		return true, nil
	})
}

func (f *Framework) WaitForEndpoints(habitatName string) error {
	return wait.Poll(time.Second, time.Minute*5, func() (bool, error) {
		ep, err := f.KubeClient.CoreV1().Endpoints(TestNs).Get(habitatName, metav1.GetOptions{})
		if err != nil {
			return false, err
		}

		if len(ep.Subsets) == 0 {
			return false, nil
		}

		if len(ep.Subsets[0].Addresses) == 0 {
			return false, nil
		}

		return true, nil
	})
}

// DeleteHabitat deletes a Habitat as a user would.
func (f *Framework) DeleteHabitat(habitatName string) error {
	return f.Client.Delete(habitatName, nil)
}

// DeleteService delete a Kubernetes service provided.
func (f *Framework) DeleteService(service string) error {
	return f.KubeClient.CoreV1().Services(TestNs).Delete(service, &metav1.DeleteOptions{})
}

func (f *Framework) createRBAC() error {
	// Create Service account.
	sa, err := convertServiceAccount("resources/operator/service-account.yml")
	if err != nil {
		return err
	}
	_, err = f.KubeClient.CoreV1().ServiceAccounts(TestNs).Create(sa)
	if err != nil {
		return err
	}

	// Create cluster role.
	cr, err := convertClusterRole("resources/operator/cluster-role.yml")
	if err != nil {
		return err
	}
	_, err = f.KubeClient.RbacV1().ClusterRoles().Create(cr)
	if err != nil {
		return err
	}

	// Create cluster role bindings.
	crb, err := convertClusterRoleBinding("resources/operator/cluster-role-binding.yml")
	if err != nil {
		return err
	}
	_, err = f.KubeClient.RbacV1().ClusterRoleBindings().Create(crb)
	if err != nil {
		return err
	}

	return nil
}

// convertServiceAccount takes in a path to the YAML file containing the manifest
// It converts it from that file to the ServiceAccount object.
func convertServiceAccount(pathToYaml string) (*apiv1.ServiceAccount, error) {
	sa := apiv1.ServiceAccount{}

	if err := convertToK8sResource(pathToYaml, &sa); err != nil {
		return nil, err
	}

	return &sa, nil
}

// convertClusterRole takes in a path to the YAML file containing the manifest.
// It converts the file to the ClusterRole object.
func convertClusterRole(pathToYaml string) (*rbacv1.ClusterRole, error) {
	cr := rbacv1.ClusterRole{}

	if err := convertToK8sResource(pathToYaml, &cr); err != nil {
		return nil, err
	}

	return &cr, nil
}

// convertClusterRoleBinding takes in a path to the YAML file containing the manifest.
// It converts the file to the ClusterRoleBinding object.
func convertClusterRoleBinding(pathToYaml string) (*rbacv1.ClusterRoleBinding, error) {
	crb := rbacv1.ClusterRoleBinding{}

	if err := convertToK8sResource(pathToYaml, &crb); err != nil {
		return nil, err
	}

	return &crb, nil
}

// ConvertDeployment takes in a path to the YAML file containing the manifest.
// It converts the file to the Deployment object.
func ConvertDeployment(pathToYaml string) (*appsv1beta1.Deployment, error) {
	d := appsv1beta1.Deployment{}

	if err := convertToK8sResource(pathToYaml, &d); err != nil {
		return nil, err
	}

	return &d, nil
}

// ConvertHabitat takes in a path to the YAML file containing the manifest.
// It converts the file to the Habitat object.
func ConvertHabitat(pathToYaml string) (*habv1beta1.Habitat, error) {
	hab := habv1beta1.Habitat{}

	if err := convertToK8sResource(pathToYaml, &hab); err != nil {
		return nil, err
	}

	return &hab, nil
}

// ConvertService takes in a path to the YAML file containing the manifest.
// It converts the file to the Service object.
func ConvertService(pathToYaml string) (*v1.Service, error) {
	s := v1.Service{}

	if err := convertToK8sResource(pathToYaml, &s); err != nil {
		return nil, err
	}

	return &s, nil
}

// ConvertSecret takes in a path to the YAML file containing the manifest.
// It converts the file to the Secret object.
func ConvertSecret(pathToYaml string) (*v1.Secret, error) {
	s := v1.Secret{}

	if err := convertToK8sResource(pathToYaml, &s); err != nil {
		return nil, err
	}

	return &s, nil
}

func convertToK8sResource(pathToYaml string, into interface{}) error {
	manifest, err := pathToOSFile(pathToYaml)
	if err != nil {
		return err
	}

	if err := yaml.NewYAMLToJSONDecoder(manifest).Decode(into); err != nil {
		return err
	}

	return nil
}

// pathToOSFile takes in a path and converts it to a File.
func pathToOSFile(relativePath string) (*os.File, error) {
	path, err := filepath.Abs(relativePath)
	if err != nil {
		return nil, err
	}

	manifest, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	return manifest, nil
}

// QueryService makes an HTTP GET request to `url` and returns the body.
func QueryService(url string) (string, error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("Habitat Service did not start correctly.")
	}

	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	return string(bodyBytes), nil
}
<|endoftext|>


<commit_before>package gherkin

import (
	"bufio"
	"fmt"
	"github.com/cucumber/cucumber-messages-go"
	"io"
	"strings"
)

type Parser interface {
	StopAtFirstError(b bool)
	Parse(s Scanner, m Matcher) (err error)
}

/*
The scanner reads a gherkin doc (typically read from a .feature file) and creates a token for
each line. The tokens are passed to the parser, which outputs an AST (Abstract Syntax Tree).

If the scanner sees a # language header, it will reconfigure itself dynamically to look for
Gherkin keywords for the associated language. The keywords are defined in gherkin-languages.json.
*/
type Scanner interface {
	Scan() (line *Line, atEof bool, err error)
}

type Builder interface {
	Build(*Token) (bool, error)
	StartRule(RuleType) (bool, error)
	EndRule(RuleType) (bool, error)
	Reset()
}

type Token struct {
	Type           TokenType
	Keyword        string
	Text           string
	Items          []*LineSpan
	GherkinDialect string
	Indent         string
	Location       *Location
}

func (t *Token) IsEOF() bool {
	return t.Type == TokenType_EOF
}
func (t *Token) String() string {
	return fmt.Sprintf("%v: %s/%s", t.Type, t.Keyword, t.Text)
}

type LineSpan struct {
	Column int
	Text   string
}

func (l *LineSpan) String() string {
	return fmt.Sprintf("%d:%s", l.Column, l.Text)
}

type parser struct {
	builder          Builder
	stopAtFirstError bool
}

func NewParser(b Builder) Parser {
	return &parser{
		builder: b,
	}
}

func (p *parser) StopAtFirstError(b bool) {
	p.stopAtFirstError = b
}

func NewScanner(r io.Reader) Scanner {
	return &scanner{
		s:    bufio.NewScanner(r),
		line: 0,
	}
}

type scanner struct {
	s    *bufio.Scanner
	line int
}

func (t *scanner) Scan() (line *Line, atEof bool, err error) {
	scanning := t.s.Scan()
	if !scanning {
		err = t.s.Err()
		if err == nil {
			atEof = true
		}
	}
	if err == nil {
		t.line += 1
		str := t.s.Text()
		line = &Line{str, t.line, strings.TrimLeft(str, " \t"), atEof}
	}
	return
}

type Line struct {
	LineText        string
	LineNumber      int
	TrimmedLineText string
	AtEof           bool
}

func (g *Line) Indent() int {
	return len(g.LineText) - len(g.TrimmedLineText)
}

func (g *Line) IsEmpty() bool {
	return len(g.TrimmedLineText) == 0
}

func (g *Line) IsEof() bool {
	return g.AtEof
}

func (g *Line) StartsWith(prefix string) bool {
	return strings.HasPrefix(g.TrimmedLineText, prefix)
}

func ParseGherkinDocument(in io.Reader) (gherkinDocument *messages.GherkinDocument, err error) {
	return ParseGherkinDocumentForLanguage(in, DEFAULT_DIALECT)
}

func ParseGherkinDocumentForLanguage(in io.Reader, language string) (gherkinDocument *messages.GherkinDocument, err error) {

	builder := NewAstBuilder()
	parser := NewParser(builder)
	parser.StopAtFirstError(false)
	matcher := NewLanguageMatcher(GherkinDialectsBuildin(), language)

	scanner := NewScanner(in)

	err = parser.Parse(scanner, matcher)

	return builder.GetGherkinDocument(), err
}
<commit_msg>gherkin/go : fix typo<commit_after>package gherkin

import (
	"bufio"
	"fmt"
	"github.com/cucumber/cucumber-messages-go"
	"io"
	"strings"
)

type Parser interface {
	StopAtFirstError(b bool)
	Parse(s Scanner, m Matcher) (err error)
}

/*
The Scanner reads a gherkin doc (typically read from a .feature file) and creates a token for
each line. The tokens are passed to the parser, which outputs an AST (Abstract Syntax Tree).

If the scanner sees a # language header, it will reconfigure itself dynamically to look for
Gherkin keywords for the associated language. The keywords are defined in gherkin-languages.json.
*/
type Scanner interface {
	Scan() (line *Line, atEof bool, err error)
}

type Builder interface {
	Build(*Token) (bool, error)
	StartRule(RuleType) (bool, error)
	EndRule(RuleType) (bool, error)
	Reset()
}

type Token struct {
	Type           TokenType
	Keyword        string
	Text           string
	Items          []*LineSpan
	GherkinDialect string
	Indent         string
	Location       *Location
}

func (t *Token) IsEOF() bool {
	return t.Type == TokenType_EOF
}
func (t *Token) String() string {
	return fmt.Sprintf("%v: %s/%s", t.Type, t.Keyword, t.Text)
}

type LineSpan struct {
	Column int
	Text   string
}

func (l *LineSpan) String() string {
	return fmt.Sprintf("%d:%s", l.Column, l.Text)
}

type parser struct {
	builder          Builder
	stopAtFirstError bool
}

func NewParser(b Builder) Parser {
	return &parser{
		builder: b,
	}
}

func (p *parser) StopAtFirstError(b bool) {
	p.stopAtFirstError = b
}

func NewScanner(r io.Reader) Scanner {
	return &scanner{
		s:    bufio.NewScanner(r),
		line: 0,
	}
}

type scanner struct {
	s    *bufio.Scanner
	line int
}

func (t *scanner) Scan() (line *Line, atEof bool, err error) {
	scanning := t.s.Scan()
	if !scanning {
		err = t.s.Err()
		if err == nil {
			atEof = true
		}
	}
	if err == nil {
		t.line += 1
		str := t.s.Text()
		line = &Line{str, t.line, strings.TrimLeft(str, " \t"), atEof}
	}
	return
}

type Line struct {
	LineText        string
	LineNumber      int
	TrimmedLineText string
	AtEof           bool
}

func (g *Line) Indent() int {
	return len(g.LineText) - len(g.TrimmedLineText)
}

func (g *Line) IsEmpty() bool {
	return len(g.TrimmedLineText) == 0
}

func (g *Line) IsEof() bool {
	return g.AtEof
}

func (g *Line) StartsWith(prefix string) bool {
	return strings.HasPrefix(g.TrimmedLineText, prefix)
}

func ParseGherkinDocument(in io.Reader) (gherkinDocument *messages.GherkinDocument, err error) {
	return ParseGherkinDocumentForLanguage(in, DEFAULT_DIALECT)
}

func ParseGherkinDocumentForLanguage(in io.Reader, language string) (gherkinDocument *messages.GherkinDocument, err error) {

	builder := NewAstBuilder()
	parser := NewParser(builder)
	parser.StopAtFirstError(false)
	matcher := NewLanguageMatcher(GherkinDialectsBuildin(), language)

	scanner := NewScanner(in)

	err = parser.Parse(scanner, matcher)

	return builder.GetGherkinDocument(), err
}
<|endoftext|>


<commit_before>package tcpchan

import (
  "testing"
  "net"
  "fmt"

  "github.com/filwisher/tcptest"
)

var messages = []string{
  "hello",
  "本語本本語語",
  "\t\n\aHello",
}

func TestStartServer(t *testing.T) {
  buffer := make([]byte, 1024)
  tcptest.NewServer(":8080", func (conn net.Conn) {
    for {
      n, err := conn.Read(buffer)
      fmt.Println("Got ser: ", string(buffer[:n]))
      if err != nil {
        panic("Test server failed")
      }
      conn.Write(buffer[:n])
    }
  })
}

func TestConn(t *testing.T) {

  var c TCPChan
  err := c.Dial(":8080")
  if err != nil {
    t.Errorf("Failed with err %s", err.Error())
  }

  for _, msg := range(messages) {
    c.Out <- []byte(msg)
    rcv := <-c.In

    if msg != string(rcv) {
      t.Errorf("Sent %s, got back %s", msg, string(rcv))
    }
  }
}
<commit_msg>removes unnecessary print statements<commit_after>package tcpchan

import (
  "testing"
  "net"

  "github.com/filwisher/tcptest"
)

var messages = []string{
  "hello",
  "本語本本語語",
  "\t\n\aHello",
}

func TestStartServer(t *testing.T) {
  buffer := make([]byte, 1024)
  tcptest.NewServer(":8080", func (conn net.Conn) {
    for {
      n, err := conn.Read(buffer)
      if err != nil {
        panic("Test server failed")
      }
      conn.Write(buffer[:n])
    }
  })
}

func TestConn(t *testing.T) {

  var c TCPChan
  err := c.Dial(":8080")
  if err != nil {
    t.Errorf("Failed with err %s", err.Error())
  }

  for _, msg := range(messages) {
    c.Out <- []byte(msg)
    rcv := <-c.In

    if msg != string(rcv) {
      t.Errorf("Sent %s, got back %s", msg, string(rcv))
    }
  }
}
<|endoftext|>


<commit_before>// Package connectionbroker is a layer on top of remotes that returns
// a gRPC connection to a manager. The connection may be a local connection
// using a local socket such as a UNIX socket.
package connectionbroker

import (
	"sync"

	"github.com/docker/swarmkit/api"
	"github.com/docker/swarmkit/remotes"
	"google.golang.org/grpc"
)

// Broker is a simple connection broker. It can either return a fresh
// connection to a remote manager selected with weighted randomization, or a
// local gRPC connection to the local manager.
type Broker struct {
	mu        sync.Mutex
	remotes   remotes.Remotes
	localConn *grpc.ClientConn
}

// New creates a new connection broker.
func New(remotes remotes.Remotes) *Broker {
	return &Broker{
		remotes: remotes,
	}
}

// SetLocalConn changes the local gRPC connection used by the connection broker.
func (b *Broker) SetLocalConn(localConn *grpc.ClientConn) {
	b.mu.Lock()
	defer b.mu.Unlock()

	b.localConn = localConn
}

// Select a manager from the set of available managers, and return a connection.
func (b *Broker) Select(dialOpts ...grpc.DialOption) (*Conn, error) {
	b.mu.Lock()
	localConn := b.localConn
	b.mu.Unlock()

	if localConn != nil {
		return &Conn{
			ClientConn: localConn,
			isLocal:    true,
		}, nil
	}

	return b.SelectRemote(dialOpts...)
}

// SelectRemote chooses a manager from the remotes, and returns a TCP
// connection.
func (b *Broker) SelectRemote(dialOpts ...grpc.DialOption) (*Conn, error) {
	peer, err := b.remotes.Select()
	if err != nil {
		return nil, err
	}

	cc, err := grpc.Dial(peer.Addr, dialOpts...)
	if err != nil {
		b.remotes.ObserveIfExists(peer, -remotes.DefaultObservationWeight)
		return nil, err
	}

	return &Conn{
		ClientConn: cc,
		remotes:    b.remotes,
		peer:       peer,
	}, nil
}

// Remotes returns the remotes interface used by the broker, so the caller
// can make observations or see weights directly.
func (b *Broker) Remotes() remotes.Remotes {
	return b.remotes
}

// Conn is a wrapper around a gRPC client connection.
type Conn struct {
	*grpc.ClientConn
	isLocal bool
	remotes remotes.Remotes
	peer    api.Peer
}

// Close closes the client connection if it is a remote connection. It also
// records a positive experience with the remote peer if success is true,
// otherwise it records a negative experience. If a local connection is in use,
// Close is a noop.
func (c *Conn) Close(success bool) error {
	if c.isLocal {
		return nil
	}

	if success {
		c.remotes.ObserveIfExists(c.peer, -remotes.DefaultObservationWeight)
	} else {
		c.remotes.ObserveIfExists(c.peer, remotes.DefaultObservationWeight)
	}

	return c.ClientConn.Close()
}
<commit_msg>connectionbroker: fix weight observing on close<commit_after>// Package connectionbroker is a layer on top of remotes that returns
// a gRPC connection to a manager. The connection may be a local connection
// using a local socket such as a UNIX socket.
package connectionbroker

import (
	"sync"

	"github.com/docker/swarmkit/api"
	"github.com/docker/swarmkit/remotes"
	"google.golang.org/grpc"
)

// Broker is a simple connection broker. It can either return a fresh
// connection to a remote manager selected with weighted randomization, or a
// local gRPC connection to the local manager.
type Broker struct {
	mu        sync.Mutex
	remotes   remotes.Remotes
	localConn *grpc.ClientConn
}

// New creates a new connection broker.
func New(remotes remotes.Remotes) *Broker {
	return &Broker{
		remotes: remotes,
	}
}

// SetLocalConn changes the local gRPC connection used by the connection broker.
func (b *Broker) SetLocalConn(localConn *grpc.ClientConn) {
	b.mu.Lock()
	defer b.mu.Unlock()

	b.localConn = localConn
}

// Select a manager from the set of available managers, and return a connection.
func (b *Broker) Select(dialOpts ...grpc.DialOption) (*Conn, error) {
	b.mu.Lock()
	localConn := b.localConn
	b.mu.Unlock()

	if localConn != nil {
		return &Conn{
			ClientConn: localConn,
			isLocal:    true,
		}, nil
	}

	return b.SelectRemote(dialOpts...)
}

// SelectRemote chooses a manager from the remotes, and returns a TCP
// connection.
func (b *Broker) SelectRemote(dialOpts ...grpc.DialOption) (*Conn, error) {
	peer, err := b.remotes.Select()
	if err != nil {
		return nil, err
	}

	cc, err := grpc.Dial(peer.Addr, dialOpts...)
	if err != nil {
		b.remotes.ObserveIfExists(peer, -remotes.DefaultObservationWeight)
		return nil, err
	}

	return &Conn{
		ClientConn: cc,
		remotes:    b.remotes,
		peer:       peer,
	}, nil
}

// Remotes returns the remotes interface used by the broker, so the caller
// can make observations or see weights directly.
func (b *Broker) Remotes() remotes.Remotes {
	return b.remotes
}

// Conn is a wrapper around a gRPC client connection.
type Conn struct {
	*grpc.ClientConn
	isLocal bool
	remotes remotes.Remotes
	peer    api.Peer
}

// Close closes the client connection if it is a remote connection. It also
// records a positive experience with the remote peer if success is true,
// otherwise it records a negative experience. If a local connection is in use,
// Close is a noop.
func (c *Conn) Close(success bool) error {
	if c.isLocal {
		return nil
	}

	if success {
		c.remotes.ObserveIfExists(c.peer, remotes.DefaultObservationWeight)
	} else {
		c.remotes.ObserveIfExists(c.peer, -remotes.DefaultObservationWeight)
	}

	return c.ClientConn.Close()
}
<|endoftext|>


<commit_before>package scheduler

import (
	"sync/atomic"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	log "github.com/golang/glog"
	"github.com/mesos/mesos-go/mesos"
)

type OfferFilter func(*mesos.Offer) bool

type OfferRegistry interface {
	// Initialize the instance, spawning necessary housekeeping go routines.
	Init()
	Add([]*mesos.Offer)
	AwaitNew(id string, f OfferFilter) <-chan empty

	// invoked when offers are rescinded or expired
	Delete(string)

	Get(offerId string) (PerishableOffer, bool)

	Walk(Walker) error

	// invalidate one or all (when offerId="") offers; offers are not declined,
	// but are simply flagged as expired in the offer history
	Invalidate(offerId string)
}

// callback that is invoked during a walk through a series of active offers,
// returning with stop=true when the walk should stop.
type Walker func(offer PerishableOffer) (stop bool, err error)

type OfferRegistryConfig struct {
	declineOffer  func(offerId string) error
	ttl           time.Duration // determines a perishable offer's expiration deadline: now+ttl
	lingerTtl     time.Duration // if zero, offers will not linger in the FIFO past their expiration deadline
	walkDelay     time.Duration // specifies the sleep time between expired offer harvests
	listenerDelay time.Duration // specifies the sleep time between offer listener notifications
}

type offerStorage struct {
	OfferRegistryConfig
	offers    *cache.FIFO // collection of timedOffer's
	listeners *cache.FIFO // collection of <-chan empty
}

type timedOffer struct {
	*mesos.Offer
	expiration time.Time
	acquired   int32 // 1 = acquired, 0 = free
}

type expiredOffer struct{}

type PerishableOffer interface {
	// returns true if this offer has expired
	hasExpired() bool
	// if not yet expired, return mesos offer details; otherwise nil
	details() *mesos.Offer
	// mark this offer as acquired, returning true if it was previously unacquired. thread-safe.
	acquire() bool
	// mark this offer as un-acquired. thread-safe.
	release()
}

func (e *expiredOffer) hasExpired() bool {
	return true
}

func (e *expiredOffer) details() *mesos.Offer {
	return nil
}

func (e *expiredOffer) acquire() bool {
	return false
}

func (e *expiredOffer) release() {}

func (to *timedOffer) hasExpired() bool {
	return time.Now().After(to.expiration)
}

func (to *timedOffer) details() *mesos.Offer {
	return to.Offer
}

func (to *timedOffer) acquire() bool {
	return atomic.CompareAndSwapInt32(&to.acquired, 0, 1) && !to.hasExpired()
}

func (to *timedOffer) release() {
	atomic.CompareAndSwapInt32(&to.acquired, 1, 0)
}

func CreateOfferRegistry(c OfferRegistryConfig) OfferRegistry {
	return &offerStorage{c, cache.NewFIFO(), cache.NewFIFO()}
}

func (s *offerStorage) Add(offers []*mesos.Offer) {
	now := time.Now()
	for _, offer := range offers {
		offerId := offer.Id.GetValue()
		log.V(3).Infof("Receiving offer %v", offerId)
		s.offers.Add(offerId, &timedOffer{offer, now.Add(s.ttl), 0})
	}
}

// delete an offer from storage, meaning that we expire it
func (s *offerStorage) Delete(offerId string) {
	if offer, ok := s.Get(offerId); ok {
		log.V(3).Infof("Deleting offer %v", offerId)
		// attempt to block others from consuming the offer. if it's already been
		// claimed and is not yet lingering then don't decline it - just mark it as
		// expired in the history: allow a prior claimant to attempt to launch with it
		myoffer := offer.acquire()
		if offer.details() != nil && myoffer {
			log.V(3).Infof("Declining offer %v", offerId)
			if err := s.declineOffer(offerId); err != nil {
				log.Warningf("Failed to decline offer %v: %v", offerId, err)
			}
		}
		s.expireOffer(offer)
	} // else, ignore offers not in the history
}

// expire all known, live offers
func (s *offerStorage) Invalidate(offerId string) {
	if offerId != "" {
		s.invalidateOne(offerId)
		return
	}
	obj := s.offers.List()
	for _, o := range obj {
		offer, ok := o.(PerishableOffer)
		if !ok {
			log.Errorf("Expected perishable offer, not %v", o)
			continue
		}
		offer.acquire() // attempt to block others from using it
		s.expireOffer(offer)
		// don't reject it from the controller, we already know that it's an invalid offer
	}
}

func (s *offerStorage) invalidateOne(offerId string) {
	if offer, ok := s.Get(offerId); ok {
		offer.acquire() // attempt to block others from using it
		s.expireOffer(offer)
		// don't reject it from the controller, we already know that it's an invalid offer
	}
}

// Walk the collection of offers, Delete()ing those that have expired,
// visiting those that haven't with the Walked. The walk stops either as
// indicated by the Walker or when the end of the offer list is reached.
func (s *offerStorage) Walk(w Walker) error {
	for offerId := range s.offers.Contains() {
		offer, ok := s.Get(offerId)
		if !ok {
			// offer disappeared...
			continue
		}
		if offer.hasExpired() {
			s.Delete(offerId)
			continue
		}
		if stop, err := w(offer); err != nil {
			return err
		} else if stop {
			return nil
		}
	}
	return nil
}

func (s *offerStorage) expireOffer(offer PerishableOffer) {
	// the offer may or may not be expired due to TTL so check for details
	// since that's a more reliable determinant of lingering status
	if details := offer.details(); details != nil {
		// recently expired, should linger
		offerId := details.Id.GetValue()
		log.V(3).Infof("Expiring offer %v", offerId)
		if s.lingerTtl > 0 {
			log.V(3).Infof("offer will linger: %v", offerId)
			s.offers.Update(offerId, &expiredOffer{})
			go func() {
				time.Sleep(s.lingerTtl)
				log.V(3).Infof("Delete lingering offer: %v", offerId)
				s.offers.Delete(offerId)
			}()
		} else {
			log.V(3).Infof("Permanently deleting offer %v", offerId)
			s.offers.Delete(offerId)
		}
	} // else, it's still lingering...
}

func (s *offerStorage) Get(id string) (PerishableOffer, bool) {
	if obj, ok := s.offers.Get(id); !ok {
		return nil, false
	} else {
		to, ok := obj.(PerishableOffer)
		if !ok {
			log.Errorf("invalid offer object in fifo '%v'", obj)
		}
		return to, ok
	}
}

type offerListener struct {
	id     string
	filter OfferFilter
	notify chan<- empty
	age    int
}

// register a listener for new offers, whom we'll notify upon receiving such.
func (s *offerStorage) AwaitNew(id string, f OfferFilter) <-chan empty {
	if f == nil {
		return nil
	}
	ch := make(chan empty, 1)
	listen := &offerListener{
		id:     id,
		filter: f,
		notify: ch,
		age:    0,
	}
	log.V(3).Infof("Registering offer listener %s", listen.id)
	s.listeners.Add(id, listen)
	return ch
}

func (s *offerStorage) makeOfferHarvestFunc() func() {
	return func() {
		s.Walk(func(PerishableOffer) (bool, error) {
			// noop; simply walking will expire offers past their TTL
			return false, nil
		})
	}
}

func (s *offerStorage) makeOfferListenerFunc() func() {
	return func() {
		var listen *offerListener
		var ok bool
		// get the next offer listener
		for {
			obj := s.listeners.Pop()
			if listen, ok = obj.(*offerListener); ok {
				break
			}
			log.Warningf("unexpected listener object %v", obj)
		}
		// notify if we find an acceptable offer
		for id := range s.offers.Contains() {
			var offer PerishableOffer
			if offer, ok = s.Get(id); !ok || offer.hasExpired() {
				continue
			}
			if listen.filter(offer.details()) {
				log.V(3).Infof("Notifying offer listener %s", listen.id)
				listen.notify <- empty{}
				return
			}
		}
		// no interesting offers found, re-queue the listener
		listen.age++
		if listen.age < 20 {
			// if the same listener has re-registered in the meantime we don't want to
			// destroy the newer listener channel. this is racy since a newer listener
			// can register between the Get() and Update(), but the consequences aren't
			// very dire - the listener merely has to wait their full backoff period.
			if _, ok := s.listeners.Get(listen.id); !ok {
				log.V(3).Infof("Re-registering offer listener %s", listen.id)
				s.listeners.Update(listen.id, listen)
			}
		} // else, you're gc'd
	}
}

func (s *offerStorage) Init() {
	go util.Forever(s.makeOfferHarvestFunc(), s.walkDelay)

	// periodically gc the listeners FIFO.
	// to avoid a rush on offers, we add a short delay between each registered
	// listener, so as to allow the most recently notified listener a bit of time
	// to act on the offer.
	go util.Forever(s.makeOfferListenerFunc(), s.listenerDelay)
}
<commit_msg>avoid re-deleting offers that have are simply lingering<commit_after>package scheduler

import (
	"sync/atomic"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	log "github.com/golang/glog"
	"github.com/mesos/mesos-go/mesos"
)

type OfferFilter func(*mesos.Offer) bool

type OfferRegistry interface {
	// Initialize the instance, spawning necessary housekeeping go routines.
	Init()
	Add([]*mesos.Offer)
	AwaitNew(id string, f OfferFilter) <-chan empty

	// invoked when offers are rescinded or expired
	Delete(string)

	Get(offerId string) (PerishableOffer, bool)

	Walk(Walker) error

	// invalidate one or all (when offerId="") offers; offers are not declined,
	// but are simply flagged as expired in the offer history
	Invalidate(offerId string)
}

// callback that is invoked during a walk through a series of active offers,
// returning with stop=true when the walk should stop.
type Walker func(offer PerishableOffer) (stop bool, err error)

type OfferRegistryConfig struct {
	declineOffer  func(offerId string) error
	ttl           time.Duration // determines a perishable offer's expiration deadline: now+ttl
	lingerTtl     time.Duration // if zero, offers will not linger in the FIFO past their expiration deadline
	walkDelay     time.Duration // specifies the sleep time between expired offer harvests
	listenerDelay time.Duration // specifies the sleep time between offer listener notifications
}

type offerStorage struct {
	OfferRegistryConfig
	offers    *cache.FIFO // collection of timedOffer's
	listeners *cache.FIFO // collection of <-chan empty
}

type timedOffer struct {
	*mesos.Offer
	expiration time.Time
	acquired   int32 // 1 = acquired, 0 = free
}

type expiredOffer struct{}

type PerishableOffer interface {
	// returns true if this offer has expired
	hasExpired() bool
	// if not yet expired, return mesos offer details; otherwise nil
	details() *mesos.Offer
	// mark this offer as acquired, returning true if it was previously unacquired. thread-safe.
	acquire() bool
	// mark this offer as un-acquired. thread-safe.
	release()
}

func (e *expiredOffer) hasExpired() bool {
	return true
}

func (e *expiredOffer) details() *mesos.Offer {
	return nil
}

func (e *expiredOffer) acquire() bool {
	return false
}

func (e *expiredOffer) release() {}

func (to *timedOffer) hasExpired() bool {
	return time.Now().After(to.expiration)
}

func (to *timedOffer) details() *mesos.Offer {
	return to.Offer
}

func (to *timedOffer) acquire() bool {
	return atomic.CompareAndSwapInt32(&to.acquired, 0, 1) && !to.hasExpired()
}

func (to *timedOffer) release() {
	atomic.CompareAndSwapInt32(&to.acquired, 1, 0)
}

func CreateOfferRegistry(c OfferRegistryConfig) OfferRegistry {
	return &offerStorage{c, cache.NewFIFO(), cache.NewFIFO()}
}

func (s *offerStorage) Add(offers []*mesos.Offer) {
	now := time.Now()
	for _, offer := range offers {
		offerId := offer.Id.GetValue()
		log.V(3).Infof("Receiving offer %v", offerId)
		s.offers.Add(offerId, &timedOffer{offer, now.Add(s.ttl), 0})
	}
}

// delete an offer from storage, meaning that we expire it
func (s *offerStorage) Delete(offerId string) {
	if offer, ok := s.Get(offerId); ok {
		log.V(3).Infof("Deleting offer %v", offerId)
		// attempt to block others from consuming the offer. if it's already been
		// claimed and is not yet lingering then don't decline it - just mark it as
		// expired in the history: allow a prior claimant to attempt to launch with it
		myoffer := offer.acquire()
		if offer.details() != nil && myoffer {
			log.V(3).Infof("Declining offer %v", offerId)
			if err := s.declineOffer(offerId); err != nil {
				log.Warningf("Failed to decline offer %v: %v", offerId, err)
			}
		}
		s.expireOffer(offer)
	} // else, ignore offers not in the history
}

// expire all known, live offers
func (s *offerStorage) Invalidate(offerId string) {
	if offerId != "" {
		s.invalidateOne(offerId)
		return
	}
	obj := s.offers.List()
	for _, o := range obj {
		offer, ok := o.(PerishableOffer)
		if !ok {
			log.Errorf("Expected perishable offer, not %v", o)
			continue
		}
		offer.acquire() // attempt to block others from using it
		s.expireOffer(offer)
		// don't reject it from the controller, we already know that it's an invalid offer
	}
}

func (s *offerStorage) invalidateOne(offerId string) {
	if offer, ok := s.Get(offerId); ok {
		offer.acquire() // attempt to block others from using it
		s.expireOffer(offer)
		// don't reject it from the controller, we already know that it's an invalid offer
	}
}

// Walk the collection of offers, Delete()ing those that have expired,
// visiting those that haven't with the Walked. The walk stops either as
// indicated by the Walker or when the end of the offer list is reached.
func (s *offerStorage) Walk(w Walker) error {
	for offerId := range s.offers.Contains() {
		offer, ok := s.Get(offerId)
		if !ok {
			// offer disappeared...
			continue
		}
		// delete any non-lingering offers that have expired
		if offer.hasExpired() && offer.details() != nil {
			s.Delete(offerId)
			continue
		}
		if stop, err := w(offer); err != nil {
			return err
		} else if stop {
			return nil
		}
	}
	return nil
}

func (s *offerStorage) expireOffer(offer PerishableOffer) {
	// the offer may or may not be expired due to TTL so check for details
	// since that's a more reliable determinant of lingering status
	if details := offer.details(); details != nil {
		// recently expired, should linger
		offerId := details.Id.GetValue()
		log.V(3).Infof("Expiring offer %v", offerId)
		if s.lingerTtl > 0 {
			log.V(3).Infof("offer will linger: %v", offerId)
			s.offers.Update(offerId, &expiredOffer{})
			go func() {
				time.Sleep(s.lingerTtl)
				log.V(3).Infof("Delete lingering offer: %v", offerId)
				s.offers.Delete(offerId)
			}()
		} else {
			log.V(3).Infof("Permanently deleting offer %v", offerId)
			s.offers.Delete(offerId)
		}
	} // else, it's still lingering...
}

func (s *offerStorage) Get(id string) (PerishableOffer, bool) {
	if obj, ok := s.offers.Get(id); !ok {
		return nil, false
	} else {
		to, ok := obj.(PerishableOffer)
		if !ok {
			log.Errorf("invalid offer object in fifo '%v'", obj)
		}
		return to, ok
	}
}

type offerListener struct {
	id     string
	filter OfferFilter
	notify chan<- empty
	age    int
}

// register a listener for new offers, whom we'll notify upon receiving such.
func (s *offerStorage) AwaitNew(id string, f OfferFilter) <-chan empty {
	if f == nil {
		return nil
	}
	ch := make(chan empty, 1)
	listen := &offerListener{
		id:     id,
		filter: f,
		notify: ch,
		age:    0,
	}
	log.V(3).Infof("Registering offer listener %s", listen.id)
	s.listeners.Add(id, listen)
	return ch
}

func (s *offerStorage) makeOfferHarvestFunc() func() {
	return func() {
		s.Walk(func(PerishableOffer) (bool, error) {
			// noop; simply walking will expire offers past their TTL
			return false, nil
		})
	}
}

func (s *offerStorage) makeOfferListenerFunc() func() {
	return func() {
		var listen *offerListener
		var ok bool
		// get the next offer listener
		for {
			obj := s.listeners.Pop()
			if listen, ok = obj.(*offerListener); ok {
				break
			}
			log.Warningf("unexpected listener object %v", obj)
		}
		// notify if we find an acceptable offer
		for id := range s.offers.Contains() {
			var offer PerishableOffer
			if offer, ok = s.Get(id); !ok || offer.hasExpired() {
				continue
			}
			if listen.filter(offer.details()) {
				log.V(3).Infof("Notifying offer listener %s", listen.id)
				listen.notify <- empty{}
				return
			}
		}
		// no interesting offers found, re-queue the listener
		listen.age++
		if listen.age < 20 {
			// if the same listener has re-registered in the meantime we don't want to
			// destroy the newer listener channel. this is racy since a newer listener
			// can register between the Get() and Update(), but the consequences aren't
			// very dire - the listener merely has to wait their full backoff period.
			if _, ok := s.listeners.Get(listen.id); !ok {
				log.V(3).Infof("Re-registering offer listener %s", listen.id)
				s.listeners.Update(listen.id, listen)
			}
		} // else, you're gc'd
	}
}

func (s *offerStorage) Init() {
	go util.Forever(s.makeOfferHarvestFunc(), s.walkDelay)

	// periodically gc the listeners FIFO.
	// to avoid a rush on offers, we add a short delay between each registered
	// listener, so as to allow the most recently notified listener a bit of time
	// to act on the offer.
	go util.Forever(s.makeOfferListenerFunc(), s.listenerDelay)
}
<|endoftext|>


<commit_before>package core

import (
	"bufio"
	"os"
	"regexp"
	"strings"

	log "github.com/Sirupsen/logrus"

	"github.com/codedellemc/scaleio-framework/scaleio-executor/native/exec"
	"github.com/codedellemc/scaleio-framework/scaleio-executor/native/installers"
	"github.com/codedellemc/scaleio-framework/scaleio-executor/native/installers/deb"
	types "github.com/codedellemc/scaleio-framework/scaleio-scheduler/types"
)

//constants for verifying that the command lines executed properly
const (
	rexrayBintrayRootURI = "https://dl.bintray.com/emccode/rexray/"
	rexrayInstallCheck   = "rexray has been installed to"
	rexrayStopCheck      = "SUCCESS!|os: process already finished|REX-Ray is already stopped"
	rexrayStartCheck     = "SUCCESS!|REX-Ray already running at"
	rexrayRunningCheck   = "REX-Ray is running at PID"
	rexrayEnableCheck    = "Adding system startup for"

	rexrayBintrayRootURI = "https://dl.bintray.com/emccode/rexray/"
)

func getRexrayVersionFromBintray(state *types.ScaleIOFramework) (string, error) {
	url := rexrayBintrayRootURI + state.Rexray.Branch
	version, err := installers.GetVersionFromBintray(url)
	return version, err
}

func getRexrayVersionToInstall(state *types.ScaleIOFramework) (string, error) {
	if state.Rexray.Version == "latest" {
		version, err := getRexrayVersionFromBintray(state)
		return version, err
	}

	return state.Rexray.Version, nil
}

func doesSciniExistInRexrayInitD() (bool, error) {
	log.Debugln("doesSciniExistInRexrayInitD LEAVE")

	file, err := os.Open("/etc/init.d/rexray")
	if err != nil {
		log.Debugln("Failed on file Open:", err)
		log.Debugln("doesSciniExistInRexrayInitD LEAVE")
		return false, err
	}
	defer file.Close()

	r, err := regexp.Compile("scini")
	if err != nil {
		log.Debugln("regexp is invalid")
		log.Debugln("doesSciniExistInRexrayInitD LEAVE")
		return false, err
	}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		log.Debugln("Line:", line)
		if len(line) == 0 {
			continue
		}

		strings := r.FindStringSubmatch(line)
		if strings != nil || len(strings) == 1 {
			log.Debugln("Match found:", line)
			log.Debugln("doesSciniExistInRexrayInitD LEAVE")
			return true, nil
		}
	}

	log.Debugln("Scini is not configured in the rexray init.d")
	log.Debugln("doesSciniExistInRexrayInitD LEAVE")
	return false, nil
}

func rexraySetup(state *types.ScaleIOFramework) (bool, error) {
	log.Infoln("RexraySetup ENTER")

	//REX-Ray Install
	rrVer, rrVerErr := getRexrayVersionToInstall(state)
	rrInst, rrInstErr := deb.GetInstalledVersion(types.RexRayPackageName, false)
	rrInst = installers.CorrectVersionFromDeb(rrInst)
	log.Debugln("rrVer:", rrVer)
	log.Debugln("rrVerErr:", rrVerErr)
	log.Debugln("rrInst:", rrInst)
	log.Debugln("rrInstErr:", rrInstErr)

	if rrVerErr != nil || rrInstErr != nil || rrVer != rrInst {
		gateway, err := getGatewayAddress(state)
		if err != nil {
			log.Errorln("Unable to find the Gateway IP Address")
			log.Infoln("RexraySetup LEAVE")
			return false, err
		}

		//REX-Ray Install
		rexrayInstallCmdline := "curl -ksSL https://dl.bintray.com/emccode/rexray/install " +
			"| INSECURE=1 sh -"
		if strings.Compare(state.Rexray.Version, "latest") != 0 {
			rexrayInstallCmdline = "curl -ksSL https://dl.bintray.com/emccode/rexray/install | INSECURE=1 sh -s -- " +
				state.Rexray.Branch + " " + state.Rexray.Version
		} else if strings.Compare(state.Rexray.Branch, "stable") != 0 {
			rexrayInstallCmdline = "curl -ksSL https://dl.bintray.com/emccode/rexray/install | INSECURE=1 sh -s -- " +
				state.Rexray.Branch
		}

		err = exec.RunCommand(rexrayInstallCmdline, rexrayInstallCheck, "")
		if err != nil {
			log.Errorln("Install REX-Ray Failed:", err)
			log.Infoln("RexraySetup LEAVE")
			return false, err
		}

		systemIdenifier := "systemName: " + state.ScaleIO.ClusterName
		if state.ScaleIO.ClusterID != "" {
			systemIdenifier = "systemId: " + state.ScaleIO.ClusterID
		}

		rexrayConfig := `rexray:
  logLevel: debug
libstorage:
  integration:
    volume:
      operations:
        mount:
          preempt: true
        unmount:
          ignoreUsedCount: true
  service: scaleio
  server:
    services:
      scaleio:
        driver: scaleio
        scaleio:
          endpoint: https://{IP_ADDRESS}/api
          insecure: true
          thinOrThick: ThinProvisioned
          userName: admin
          password: {PASSWORD}
          {SYSTEMIDENTIFIER}
          protectionDomainName: {PROTECTIONDOMAINNAME}
          storagePoolName: {STORAGEPOOLNAME}`

		rexrayConfig = strings.Replace(rexrayConfig, "{IP_ADDRESS}", gateway, -1)
		rexrayConfig = strings.Replace(rexrayConfig, "{PASSWORD}",
			state.ScaleIO.AdminPassword, -1)
		rexrayConfig = strings.Replace(rexrayConfig, "{SYSTEMIDENTIFIER}",
			systemIdenifier, -1)
		rexrayConfig = strings.Replace(rexrayConfig, "{PROTECTIONDOMAINNAME}",
			state.ScaleIO.ProtectionDomain, -1)
		rexrayConfig = strings.Replace(rexrayConfig, "{STORAGEPOOLNAME}",
			state.ScaleIO.StoragePool, -1)

		file, err := os.OpenFile("/etc/rexray/config.yml",
			os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
		if err != nil {
			log.Errorln("Writing Config File Failed:", err)
			log.Infoln("RexraySetup LEAVE")
			return false, err
		}

		file.WriteString(rexrayConfig)
		file.Close()

		found, errInitd := doesSciniExistInRexrayInitD()
		if errInitd == nil {
			if !found {
				log.Debugln("Modify REX-Ray init.d to add Scini dependency")

				writeSciniCmdline := "sed -i 's/\\/usr\\/bin\\/rexray start/if \\[ -e \\/etc\\/init.d\\/scini \\]\\; then \\/etc\\/init.d\\/scini start; fi\\n    \\/usr\\/bin\\/rexray start/' /etc/init.d/rexray"
				output, errScini := exec.RunCommandOutput(writeSciniCmdline)
				if errScini != nil || len(output) > 0 {
					log.Errorln("Failed to add Scini dependency:", errScini)
					log.Infoln("GatewaySetup LEAVE")
					return false, errScini
				}
			} else {
				log.Debugln("Scini has already been configured as a dependency on REX-Ray initd")
			}
		} else {
			log.Infoln("doesSciniExistInRexrayInitD failed:", errInitd)
			log.Infoln("RexraySetup LEAVE")
			return false, errInitd
		}

	} else {
		log.Infoln(types.RexRayPackageName, "is already installed")
	}

	if rrInst == "" && rrInstErr == nil {
		log.Debugln("No previous install of", types.RexRayPackageName,
			"exists. Reboot required!")
		log.Infoln("RexraySetup LEAVE")
		return true, nil
	}

	log.Infoln("RexraySetup Succeeded")
	log.Infoln("RexraySetup LEAVE")
	return false, nil
}

/*
func rexrayServerSetup(state *types.ScaleIOFramework) error {
	log.Infoln("RexrayServerSetup ENTER")

	pri, err := getPrimaryMdmNode(state)
	if err != nil {
		log.Errorln("Unable to find the Primary MDM node")
		log.Infoln("RexrayServerSetup LEAVE")
		return err
	}

	//REX-Ray Install
	rexrayInstallCmdline := "curl -ksSL https://dl.bintray.com/emccode/rexray/install | INSECURE=1 sh -"
	err = exec.RunCommand(rexrayInstallCmdline, rexrayInstallCheck, "")
	if err != nil {
		log.Errorln("Install REX-Ray Failed:", err)
		log.Infoln("RexrayServerSetup LEAVE")
		return err
	}

	rexrayConfig := `rexray:
  logLevel: debug
libstorage:
  host: tcp://127.0.0.1:7979
  embedded: true
  integration:
    volume:
      operations:
        mount:
          preempt: true
        unmount:
          ignoreUsedCount: true
  service: scaleio
  server:
    endpoints:
      public:
        address: tcp://:7979
    services:
      scaleio:
        driver: scaleio
        scaleio:
          endpoint: https://{IP_ADDRESS}/api
          insecure: true
          userName: admin
          password: {PASSWORD}
          systemName: {SYSTEMNAME}
          protectionDomainName: {PROTECTIONDOMAINNAME}
          storagePoolName: {STORAGEPOOLNAME}`

	gatewayIP := pri.IPAddress
	log.Infoln("Gateway IP to Use:", gatewayIP)
	if len(state.ScaleIO.LbGateway) > 0 {
		gatewayIP = state.ScaleIO.LbGateway
		log.Infoln("LbGateway Set. Using IP:", gatewayIP)
	}

	rexrayConfig = strings.Replace(rexrayConfig, "{IP_ADDRESS}", gatewayIP, -1)
	rexrayConfig = strings.Replace(rexrayConfig, "{PASSWORD}",
		state.ScaleIO.AdminPassword, -1)
	rexrayConfig = strings.Replace(rexrayConfig, "{SYSTEMNAME}",
		state.ScaleIO.ClusterName, -1)
	rexrayConfig = strings.Replace(rexrayConfig, "{PROTECTIONDOMAINNAME}",
		state.ScaleIO.ProtectionDomain, -1)
	rexrayConfig = strings.Replace(rexrayConfig, "{STORAGEPOOLNAME}",
		state.ScaleIO.StoragePool, -1)

	file, err := os.OpenFile("/etc/rexray/config.yml",
		os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
	if err != nil {
		log.Errorln("Writing Config File Failed:", err)
		log.Infoln("RexrayServerSetup LEAVE")
		return err
	}

	file.WriteString(rexrayConfig)
	file.Close()

	time.Sleep(time.Duration(DelayBetweenCommandsInSeconds) * time.Second)

	rexrayStopCmdline := "rexray service stop -l debug"
	err = exec.RunCommandEx(rexrayStopCmdline, rexrayStopCheck, "", 20)
	if err != nil {
		log.Warnln("REX-Ray stop Failed:", err)
	}

	time.Sleep(time.Duration(DelayBetweenCommandsInSeconds) * time.Second)

	rexrayStartCmdline := "rexray service start -l debug"
	err = exec.RunCommandEx(rexrayStartCmdline, rexrayStartCheck, "", 20)
	if err != nil {
		log.Errorln("REX-Ray start Failed:", err)
		log.Infoln("RexrayServerSetup LEAVE")
		return err
	}

	log.Infoln("RexrayServerSetup Succeeded")
	log.Infoln("RexrayServerSetup LEAVE")
	return nil
}

func rexrayClientSetup(state *types.ScaleIOFramework) error {
	log.Infoln("RexrayClientSetup ENTER")

	pri, err := getPrimaryMdmNode(state)
	if err != nil {
		log.Errorln("Unable to find the Primary MDM node")
		log.Infoln("RexrayClientSetup LEAVE")
		return err
	}

	//REX-Ray Install
	rexrayInstallCmdline := "curl -ksSL https://dl.bintray.com/emccode/rexray/install | INSECURE=1 sh -"
	err = exec.RunCommand(rexrayInstallCmdline, rexrayInstallCheck, "")
	if err != nil {
		log.Errorln("Install REX-Ray Failed:", err)
		log.Infoln("RexrayClientSetup LEAVE")
		return err
	}

	rexrayConfig := `rexray:
  logLevel: debug
libstorage:
  host:    tcp://{IP_ADDRESS}:7979
  service: scaleio`

	gatewayIP := pri.IPAddress
	log.Infoln("Gateway IP to Use:", gatewayIP)
	if len(state.ScaleIO.LbGateway) > 0 {
		gatewayIP = state.ScaleIO.LbGateway
		log.Infoln("LbGateway Set. Using IP:", gatewayIP)
	}

	rexrayConfig = strings.Replace(rexrayConfig, "{IP_ADDRESS}", gatewayIP, -1)

	file, err := os.OpenFile("/etc/rexray/config.yml",
		os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
	if err != nil {
		log.Errorln("Writing Config File Failed:", err)
		log.Infoln("RexrayClientSetup LEAVE")
		return err
	}

	file.WriteString(rexrayConfig)
	file.Close()

	time.Sleep(time.Duration(DelayBetweenCommandsInSeconds) * time.Second)

	rexrayStopCmdline := "rexray service stop -l debug"
	err = exec.RunCommandEx(rexrayStopCmdline, rexrayStopCheck, "", 20)
	if err != nil {
		log.Warnln("REX-Ray stop Failed:", err)
	}

	time.Sleep(time.Duration(DelayBetweenCommandsInSeconds) * time.Second)

	rexrayStartCmdline := "rexray service start -l debug"
	err = exec.RunCommandEx(rexrayStartCmdline, rexrayStartCheck, "", 20)
	if err != nil {
		log.Errorln("REX-Ray start Failed:", err)
		log.Infoln("RexrayClientSetup LEAVE")
		return err
	}

	log.Infoln("RexrayClientSetup Succeeded")
	log.Infoln("RexrayClientSetup LEAVE")
	return nil
}
*/
<commit_msg>Remove duplicate<commit_after>package core

import (
	"bufio"
	"os"
	"regexp"
	"strings"

	log "github.com/Sirupsen/logrus"

	"github.com/codedellemc/scaleio-framework/scaleio-executor/native/exec"
	"github.com/codedellemc/scaleio-framework/scaleio-executor/native/installers"
	"github.com/codedellemc/scaleio-framework/scaleio-executor/native/installers/deb"
	types "github.com/codedellemc/scaleio-framework/scaleio-scheduler/types"
)

//constants for verifying that the command lines executed properly
const (
	rexrayInstallCheck = "rexray has been installed to"
	rexrayStopCheck    = "SUCCESS!|os: process already finished|REX-Ray is already stopped"
	rexrayStartCheck   = "SUCCESS!|REX-Ray already running at"
	rexrayRunningCheck = "REX-Ray is running at PID"
	rexrayEnableCheck  = "Adding system startup for"

	rexrayBintrayRootURI = "https://dl.bintray.com/emccode/rexray/"
)

func getRexrayVersionFromBintray(state *types.ScaleIOFramework) (string, error) {
	url := rexrayBintrayRootURI + state.Rexray.Branch
	version, err := installers.GetVersionFromBintray(url)
	return version, err
}

func getRexrayVersionToInstall(state *types.ScaleIOFramework) (string, error) {
	if state.Rexray.Version == "latest" {
		version, err := getRexrayVersionFromBintray(state)
		return version, err
	}

	return state.Rexray.Version, nil
}

func doesSciniExistInRexrayInitD() (bool, error) {
	log.Debugln("doesSciniExistInRexrayInitD LEAVE")

	file, err := os.Open("/etc/init.d/rexray")
	if err != nil {
		log.Debugln("Failed on file Open:", err)
		log.Debugln("doesSciniExistInRexrayInitD LEAVE")
		return false, err
	}
	defer file.Close()

	r, err := regexp.Compile("scini")
	if err != nil {
		log.Debugln("regexp is invalid")
		log.Debugln("doesSciniExistInRexrayInitD LEAVE")
		return false, err
	}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		log.Debugln("Line:", line)
		if len(line) == 0 {
			continue
		}

		strings := r.FindStringSubmatch(line)
		if strings != nil || len(strings) == 1 {
			log.Debugln("Match found:", line)
			log.Debugln("doesSciniExistInRexrayInitD LEAVE")
			return true, nil
		}
	}

	log.Debugln("Scini is not configured in the rexray init.d")
	log.Debugln("doesSciniExistInRexrayInitD LEAVE")
	return false, nil
}

func rexraySetup(state *types.ScaleIOFramework) (bool, error) {
	log.Infoln("RexraySetup ENTER")

	//REX-Ray Install
	rrVer, rrVerErr := getRexrayVersionToInstall(state)
	rrInst, rrInstErr := deb.GetInstalledVersion(types.RexRayPackageName, false)
	rrInst = installers.CorrectVersionFromDeb(rrInst)
	log.Debugln("rrVer:", rrVer)
	log.Debugln("rrVerErr:", rrVerErr)
	log.Debugln("rrInst:", rrInst)
	log.Debugln("rrInstErr:", rrInstErr)

	if rrVerErr != nil || rrInstErr != nil || rrVer != rrInst {
		gateway, err := getGatewayAddress(state)
		if err != nil {
			log.Errorln("Unable to find the Gateway IP Address")
			log.Infoln("RexraySetup LEAVE")
			return false, err
		}

		//REX-Ray Install
		rexrayInstallCmdline := "curl -ksSL https://dl.bintray.com/emccode/rexray/install " +
			"| INSECURE=1 sh -"
		if strings.Compare(state.Rexray.Version, "latest") != 0 {
			rexrayInstallCmdline = "curl -ksSL https://dl.bintray.com/emccode/rexray/install | INSECURE=1 sh -s -- " +
				state.Rexray.Branch + " " + state.Rexray.Version
		} else if strings.Compare(state.Rexray.Branch, "stable") != 0 {
			rexrayInstallCmdline = "curl -ksSL https://dl.bintray.com/emccode/rexray/install | INSECURE=1 sh -s -- " +
				state.Rexray.Branch
		}

		err = exec.RunCommand(rexrayInstallCmdline, rexrayInstallCheck, "")
		if err != nil {
			log.Errorln("Install REX-Ray Failed:", err)
			log.Infoln("RexraySetup LEAVE")
			return false, err
		}

		systemIdenifier := "systemName: " + state.ScaleIO.ClusterName
		if state.ScaleIO.ClusterID != "" {
			systemIdenifier = "systemId: " + state.ScaleIO.ClusterID
		}

		rexrayConfig := `rexray:
  logLevel: debug
libstorage:
  integration:
    volume:
      operations:
        mount:
          preempt: true
        unmount:
          ignoreUsedCount: true
  service: scaleio
  server:
    services:
      scaleio:
        driver: scaleio
        scaleio:
          endpoint: https://{IP_ADDRESS}/api
          insecure: true
          thinOrThick: ThinProvisioned
          userName: admin
          password: {PASSWORD}
          {SYSTEMIDENTIFIER}
          protectionDomainName: {PROTECTIONDOMAINNAME}
          storagePoolName: {STORAGEPOOLNAME}`

		rexrayConfig = strings.Replace(rexrayConfig, "{IP_ADDRESS}", gateway, -1)
		rexrayConfig = strings.Replace(rexrayConfig, "{PASSWORD}",
			state.ScaleIO.AdminPassword, -1)
		rexrayConfig = strings.Replace(rexrayConfig, "{SYSTEMIDENTIFIER}",
			systemIdenifier, -1)
		rexrayConfig = strings.Replace(rexrayConfig, "{PROTECTIONDOMAINNAME}",
			state.ScaleIO.ProtectionDomain, -1)
		rexrayConfig = strings.Replace(rexrayConfig, "{STORAGEPOOLNAME}",
			state.ScaleIO.StoragePool, -1)

		file, err := os.OpenFile("/etc/rexray/config.yml",
			os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
		if err != nil {
			log.Errorln("Writing Config File Failed:", err)
			log.Infoln("RexraySetup LEAVE")
			return false, err
		}

		file.WriteString(rexrayConfig)
		file.Close()

		found, errInitd := doesSciniExistInRexrayInitD()
		if errInitd == nil {
			if !found {
				log.Debugln("Modify REX-Ray init.d to add Scini dependency")

				writeSciniCmdline := "sed -i 's/\\/usr\\/bin\\/rexray start/if \\[ -e \\/etc\\/init.d\\/scini \\]\\; then \\/etc\\/init.d\\/scini start; fi\\n    \\/usr\\/bin\\/rexray start/' /etc/init.d/rexray"
				output, errScini := exec.RunCommandOutput(writeSciniCmdline)
				if errScini != nil || len(output) > 0 {
					log.Errorln("Failed to add Scini dependency:", errScini)
					log.Infoln("GatewaySetup LEAVE")
					return false, errScini
				}
			} else {
				log.Debugln("Scini has already been configured as a dependency on REX-Ray initd")
			}
		} else {
			log.Infoln("doesSciniExistInRexrayInitD failed:", errInitd)
			log.Infoln("RexraySetup LEAVE")
			return false, errInitd
		}

	} else {
		log.Infoln(types.RexRayPackageName, "is already installed")
	}

	if rrInst == "" && rrInstErr == nil {
		log.Debugln("No previous install of", types.RexRayPackageName,
			"exists. Reboot required!")
		log.Infoln("RexraySetup LEAVE")
		return true, nil
	}

	log.Infoln("RexraySetup Succeeded")
	log.Infoln("RexraySetup LEAVE")
	return false, nil
}

/*
func rexrayServerSetup(state *types.ScaleIOFramework) error {
	log.Infoln("RexrayServerSetup ENTER")

	pri, err := getPrimaryMdmNode(state)
	if err != nil {
		log.Errorln("Unable to find the Primary MDM node")
		log.Infoln("RexrayServerSetup LEAVE")
		return err
	}

	//REX-Ray Install
	rexrayInstallCmdline := "curl -ksSL https://dl.bintray.com/emccode/rexray/install | INSECURE=1 sh -"
	err = exec.RunCommand(rexrayInstallCmdline, rexrayInstallCheck, "")
	if err != nil {
		log.Errorln("Install REX-Ray Failed:", err)
		log.Infoln("RexrayServerSetup LEAVE")
		return err
	}

	rexrayConfig := `rexray:
  logLevel: debug
libstorage:
  host: tcp://127.0.0.1:7979
  embedded: true
  integration:
    volume:
      operations:
        mount:
          preempt: true
        unmount:
          ignoreUsedCount: true
  service: scaleio
  server:
    endpoints:
      public:
        address: tcp://:7979
    services:
      scaleio:
        driver: scaleio
        scaleio:
          endpoint: https://{IP_ADDRESS}/api
          insecure: true
          userName: admin
          password: {PASSWORD}
          systemName: {SYSTEMNAME}
          protectionDomainName: {PROTECTIONDOMAINNAME}
          storagePoolName: {STORAGEPOOLNAME}`

	gatewayIP := pri.IPAddress
	log.Infoln("Gateway IP to Use:", gatewayIP)
	if len(state.ScaleIO.LbGateway) > 0 {
		gatewayIP = state.ScaleIO.LbGateway
		log.Infoln("LbGateway Set. Using IP:", gatewayIP)
	}

	rexrayConfig = strings.Replace(rexrayConfig, "{IP_ADDRESS}", gatewayIP, -1)
	rexrayConfig = strings.Replace(rexrayConfig, "{PASSWORD}",
		state.ScaleIO.AdminPassword, -1)
	rexrayConfig = strings.Replace(rexrayConfig, "{SYSTEMNAME}",
		state.ScaleIO.ClusterName, -1)
	rexrayConfig = strings.Replace(rexrayConfig, "{PROTECTIONDOMAINNAME}",
		state.ScaleIO.ProtectionDomain, -1)
	rexrayConfig = strings.Replace(rexrayConfig, "{STORAGEPOOLNAME}",
		state.ScaleIO.StoragePool, -1)

	file, err := os.OpenFile("/etc/rexray/config.yml",
		os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
	if err != nil {
		log.Errorln("Writing Config File Failed:", err)
		log.Infoln("RexrayServerSetup LEAVE")
		return err
	}

	file.WriteString(rexrayConfig)
	file.Close()

	time.Sleep(time.Duration(DelayBetweenCommandsInSeconds) * time.Second)

	rexrayStopCmdline := "rexray service stop -l debug"
	err = exec.RunCommandEx(rexrayStopCmdline, rexrayStopCheck, "", 20)
	if err != nil {
		log.Warnln("REX-Ray stop Failed:", err)
	}

	time.Sleep(time.Duration(DelayBetweenCommandsInSeconds) * time.Second)

	rexrayStartCmdline := "rexray service start -l debug"
	err = exec.RunCommandEx(rexrayStartCmdline, rexrayStartCheck, "", 20)
	if err != nil {
		log.Errorln("REX-Ray start Failed:", err)
		log.Infoln("RexrayServerSetup LEAVE")
		return err
	}

	log.Infoln("RexrayServerSetup Succeeded")
	log.Infoln("RexrayServerSetup LEAVE")
	return nil
}

func rexrayClientSetup(state *types.ScaleIOFramework) error {
	log.Infoln("RexrayClientSetup ENTER")

	pri, err := getPrimaryMdmNode(state)
	if err != nil {
		log.Errorln("Unable to find the Primary MDM node")
		log.Infoln("RexrayClientSetup LEAVE")
		return err
	}

	//REX-Ray Install
	rexrayInstallCmdline := "curl -ksSL https://dl.bintray.com/emccode/rexray/install | INSECURE=1 sh -"
	err = exec.RunCommand(rexrayInstallCmdline, rexrayInstallCheck, "")
	if err != nil {
		log.Errorln("Install REX-Ray Failed:", err)
		log.Infoln("RexrayClientSetup LEAVE")
		return err
	}

	rexrayConfig := `rexray:
  logLevel: debug
libstorage:
  host:    tcp://{IP_ADDRESS}:7979
  service: scaleio`

	gatewayIP := pri.IPAddress
	log.Infoln("Gateway IP to Use:", gatewayIP)
	if len(state.ScaleIO.LbGateway) > 0 {
		gatewayIP = state.ScaleIO.LbGateway
		log.Infoln("LbGateway Set. Using IP:", gatewayIP)
	}

	rexrayConfig = strings.Replace(rexrayConfig, "{IP_ADDRESS}", gatewayIP, -1)

	file, err := os.OpenFile("/etc/rexray/config.yml",
		os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
	if err != nil {
		log.Errorln("Writing Config File Failed:", err)
		log.Infoln("RexrayClientSetup LEAVE")
		return err
	}

	file.WriteString(rexrayConfig)
	file.Close()

	time.Sleep(time.Duration(DelayBetweenCommandsInSeconds) * time.Second)

	rexrayStopCmdline := "rexray service stop -l debug"
	err = exec.RunCommandEx(rexrayStopCmdline, rexrayStopCheck, "", 20)
	if err != nil {
		log.Warnln("REX-Ray stop Failed:", err)
	}

	time.Sleep(time.Duration(DelayBetweenCommandsInSeconds) * time.Second)

	rexrayStartCmdline := "rexray service start -l debug"
	err = exec.RunCommandEx(rexrayStartCmdline, rexrayStartCheck, "", 20)
	if err != nil {
		log.Errorln("REX-Ray start Failed:", err)
		log.Infoln("RexrayClientSetup LEAVE")
		return err
	}

	log.Infoln("RexrayClientSetup Succeeded")
	log.Infoln("RexrayClientSetup LEAVE")
	return nil
}
*/
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/itsjamie/gin-cors"
	"github.com/jkomoros/boardgame"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

type Server struct {
	managers managerMap
	storage  StorageManager
	//We store the last error so that next time viewHandler is called we can
	//display it. Yes, this is a hack.
	lastErrorMessage string
	config           *ConfigMode
}

type Renderer struct {
	c        *gin.Context
	rendered bool
}

type Config struct {
	Dev  *ConfigMode
	Prod *ConfigMode
}

type ConfigMode struct {
	AllowedOrigins    string
	DefaultPort       string
	FirebaseProjectId string
	AdminUserIds      []string
	//This is a dangerous config. Only enable in Dev!
	DisableAdminChecking bool
}

type MoveForm struct {
	Name        string
	Description string
	Fields      []*MoveFormField
}

type MoveFormFieldType int

type MoveFormField struct {
	Name         string
	Type         boardgame.PropertyType
	DefaultValue interface{}
}

func (c *ConfigMode) Validate() error {
	if c.DefaultPort == "" {
		return errors.New("No default port provided")
	}
	//AllowedOrigins will just be default allow
	if c.AllowedOrigins == "" {
		log.Println("No AllowedOrigins found. Defaulting to '*'")
		c.AllowedOrigins = "*"
	}
	return nil
}

const (
	configFileName = "config.SECRET.json"
)

type managerMap map[string]*boardgame.GameManager

/*

Overview of the types of handlers and methods

server.fooHandler take a context. They grab all of the dependencies and pass them to the doers.
server.doFoo takes a renderer and all dependencies. It renders the result.
server.getRequestFoo fetches an argument from the context's request and nothing else
server.getFoo grabs a thing that was stored in Context and nothing else
server.setFoo sets a thing into context and nothing else
server.calcFoo takes dependencies and returns a result, with no touching context.
*/

/*
NewServer returns a new server. Get it to run by calling Start(). storage
should be the same underlying storage manager that is in use for manager.

Use it like so:

	func main() {
		storage := server.NewDefaultStorageManager()
		defer storage.Close()
		server.NewServer(storage, mygame.NewManager(storage)).Start()
	}

*/
func NewServer(storage StorageManager, managers ...*boardgame.GameManager) *Server {
	result := &Server{
		managers: make(managerMap),
		storage:  storage,
	}

	for _, manager := range managers {
		name := manager.Delegate().Name()
		result.managers[name] = manager
	}

	return result

}

func NewRenderer(c *gin.Context) *Renderer {
	return &Renderer{
		c,
		false,
	}
}

func (r *Renderer) Error(message string) {
	if r.rendered {
		panic("Error called on already-rendered renderer")
	}
	r.c.JSON(http.StatusOK, gin.H{
		"Status": "Failure",
		"Error":  message,
	})

	r.rendered = true
}

func (r *Renderer) Success(keys gin.H) {

	if r.rendered {
		panic("Success called on alread-rendered renderer")
	}

	if keys == nil {
		keys = gin.H{}
	}

	result := gin.H{}

	for key, val := range keys {
		result[key] = val
	}

	result["Status"] = "Success"

	r.c.JSON(http.StatusOK, result)

	r.rendered = true
}

//gameAPISetup fetches the game configured in the URL and puts it in context.
func (s *Server) gameAPISetup(c *gin.Context) {

	id := s.getRequestGameId(c)

	gameName := s.getRequestGameName(c)

	manager := s.managers[gameName]

	if manager == nil {
		log.Println("Couldnt' find manager for", gameName)
		return
	}

	game := manager.Game(id)

	//TODO: figure out a way to return a meaningful error

	if game == nil {
		log.Println("Couldn't find game with id", id)
		return
	}

	if game.Name() != c.Param("name") {
		log.Println("The name of the game was not what we were expecting. Wanted", c.Param("name"), "got", game.Name())
		return
	}

	s.setGame(c, game)

	cookie := s.getRequestCookie(c)

	if cookie == "" {
		log.Println("No cookie set")
		return
	}

	user := s.storage.GetUserByCookie(cookie)

	if user == nil {
		log.Println("No user associated with that cookie")
		return
	}

	s.setUser(c, user)

	s.setAdminAllowed(c, s.calcAdminAllowed(user))

	userIds := s.storage.UserIdsForGame(id)

	if userIds == nil {
		log.Println("No userIds associated with game")
	}

	effectiveViewingAsPlayer, inGame := s.calcViewingAsPlayer(userIds, user)

	if !inGame {
		//We aren't yet in game, so we need to join it.

		if effectiveViewingAsPlayer == boardgame.ObserverPlayerIndex {
			//I guess there weren't any slots.
			log.Println("The user is not in the game, but there are no empty slots to join in as.")
		} else {
			if err := s.storage.SetPlayerForGame(id, effectiveViewingAsPlayer, user.Id); err != nil {
				log.Println("Tried to set this user as player", effectiveViewingAsPlayer, "but failed:", err)
			} else {
				log.Println("User joined game", id, "as player", effectiveViewingAsPlayer)
			}
		}
	}

	s.setViewingAsPlayer(c, effectiveViewingAsPlayer)

}

func (s *Server) gameStatusHandler(c *gin.Context) {
	//This handler is designed to be a very simple status marker for the
	//current version of the specific game. It will be hit hard by all
	//clients, repeatedly, so it should be very fast.

	//TODO: use memcache for this handler

	game := s.getGame(c)

	r := NewRenderer(c)

	s.doGameStatus(r, game)

}

func (s *Server) doGameStatus(r *Renderer, game *boardgame.Game) {
	if game == nil {
		r.Error("Not Found")
		return
	}

	r.Success(gin.H{
		"Version": game.Version(),
	})
}

func (s *Server) newGameHandler(c *gin.Context) {

	r := NewRenderer(c)

	managerId := s.getRequestManager(c)

	manager := s.managers[managerId]

	s.doNewGame(r, manager)

}

func (s *Server) doNewGame(r *Renderer, manager *boardgame.GameManager) {

	if manager == nil {
		r.Error("No manager provided")
		return
	}

	game := boardgame.NewGame(manager)

	if game == nil {
		r.Error("No game could be created")
		return
	}

	if err := game.SetUp(0); err != nil {
		//TODO: communicate the error state back to the client in a sane way
		r.Error("Couldn't set up game: " + err.Error())
		return
	}

	r.Success(gin.H{
		"GameId":   game.Id(),
		"GameName": game.Name(),
	})
}

func (s *Server) listGamesHandler(c *gin.Context) {

	r := NewRenderer(c)

	r.Success(gin.H{
		"Games": s.storage.ListGames(10),
	})
}

func (s *Server) listManagerHandler(c *gin.Context) {
	var managerNames []string
	for name, _ := range s.managers {
		managerNames = append(managerNames, name)
	}

	r := NewRenderer(c)

	r.Success(gin.H{
		"Managers": managerNames,
	})
}

func (s *Server) gameViewHandler(c *gin.Context) {

	game := s.getGame(c)

	playerIndex := s.effectivePlayerIndex(c)

	r := NewRenderer(c)

	s.gameView(r, game, playerIndex)

}

func (s *Server) gameView(r *Renderer, game *boardgame.Game, playerIndex boardgame.PlayerIndex) {
	if game == nil {
		r.Error("Couldn't find game")
		return
	}

	if playerIndex == invalidPlayerIndex {
		r.Error("Got invalid playerIndex")
		return
	}

	args := gin.H{
		"Diagram":         game.CurrentState().SanitizedForPlayer(playerIndex).Diagram(),
		"Chest":           s.renderChest(game),
		"Forms":           s.generateForms(game),
		"Game":            game.JSONForPlayer(playerIndex),
		"Error":           s.lastErrorMessage,
		"ViewingAsPlayer": playerIndex,
	}

	s.lastErrorMessage = ""

	r.Success(args)

}

func (s *Server) moveHandler(c *gin.Context) {
	if c.Request.Method != http.MethodPost {
		panic("This can only be called as a post.")
	}

	r := NewRenderer(c)

	game := s.getGame(c)

	if game == nil {
		r.Error("Game not found")
		return
	}

	viewingPlayerIndex := s.getViewingAsPlayer(c)

	adminAllowed := s.getAdminAllowed(c)
	requestAdmin := s.getRequestAdmin(c)

	isAdmin := s.calcIsAdmin(adminAllowed, requestAdmin)

	proposer := viewingPlayerIndex

	if isAdmin {
		proposer = boardgame.AdminPlayerIndex
	}

	move, err := s.getMoveFromForm(c, game)

	if move == nil {

		errString := "No move returned"

		if err != nil {
			errString = err.Error()
		}

		r.Error("Couldn't get move: " + errString)
		return
	}

	if err := s.makeMove(game, proposer, move); err != nil {
		r.Error("Couldn't mave move: " + err.Error())
		return
	}

	r.Success(nil)
}

func (s *Server) makeMove(game *boardgame.Game, proposer boardgame.PlayerIndex, move boardgame.Move) error {

	if err := <-game.ProposeMove(move, proposer); err != nil {
		return errors.New(fmt.Sprint("Applying move failed: ", err))
	}
	//TODO: it would be nice if we could show which fixup moves we made, too,
	//somehow.

	return nil
}

func (s *Server) generateForms(game *boardgame.Game) []*MoveForm {

	var result []*MoveForm

	for _, move := range game.PlayerMoves() {

		move.DefaultsForState(game.CurrentState())

		moveItem := &MoveForm{
			Name:        move.Name(),
			Description: move.Description(),
			Fields:      formFields(move),
		}
		result = append(result, moveItem)
	}

	return result
}

func formFields(move boardgame.Move) []*MoveFormField {

	var result []*MoveFormField

	for fieldName, fieldType := range move.ReadSetter().Props() {

		val, _ := move.ReadSetter().Prop(fieldName)

		result = append(result, &MoveFormField{
			Name:         fieldName,
			Type:         fieldType,
			DefaultValue: val,
		})

	}

	return result
}

func (s *Server) renderChest(game *boardgame.Game) map[string][]interface{} {
	//Substantially copied from cli.renderChest().

	deck := make(map[string][]interface{})

	for _, name := range game.Chest().DeckNames() {

		components := game.Chest().Deck(name).Components()

		values := make([]interface{}, len(components))

		for i, component := range components {
			values[i] = struct {
				Index  int
				Values interface{}
			}{
				i,
				component.Values,
			}
		}

		deck[name] = values
	}

	return deck
}

//Start is where you start the server, and it never returns until it's time to shut down.
func (s *Server) Start() {

	if _, err := os.Stat(configFileName); os.IsNotExist(err) {
		log.Println("Couldn't find a " + configFileName + " in current directory. This file is required. Copy a starter one from boardgame/server/api/config.SAMPLE.json")
		return
	}

	contents, err := ioutil.ReadFile(configFileName)

	if err != nil {
		log.Println("Couldn't read config file:", err)
		return
	}

	var config Config

	if err := json.Unmarshal(contents, &config); err != nil {
		log.Println("couldn't unmarshal config file:", err)
		return
	}

	log.Println("Environment Variables")
	//Dbug print out the current environment
	for _, config := range os.Environ() {
		log.Println("Environ:", config)
	}

	if v := os.Getenv("GIN_MODE"); v == "release" {
		log.Println("Using release mode config")
		s.config = config.Prod
	} else {
		log.Println("Using dev mode config")
		s.config = config.Dev
	}

	if err := s.config.Validate(); err != nil {
		log.Println("The provided config was not valid: ", err)
		return
	}

	router := gin.Default()

	//We have everything prefixed by /api just in case at some point we do
	//want to host both static and api on the same logical server.
	mainGroup := router.Group("/api")
	mainGroup.Use(cors.Middleware(cors.Config{
		Origins:        s.config.AllowedOrigins,
		RequestHeaders: "content-type, Origin",
		ExposedHeaders: "content-type",
		Methods:        "GET, POST",
		Credentials:    true,
	}))

	{
		mainGroup.GET("list/game", s.listGamesHandler)
		mainGroup.POST("new/game", s.newGameHandler)
		mainGroup.GET("list/manager", s.listManagerHandler)

		mainGroup.POST("auth/cookie", s.authCookieHandler)
		mainGroup.OPTIONS("auth/cookie", s.authCookieHandler)

		gameAPIGroup := mainGroup.Group("game/:name/:id")
		gameAPIGroup.Use(s.gameAPISetup)
		{
			gameAPIGroup.GET("view", s.gameViewHandler)
			gameAPIGroup.POST("move", s.moveHandler)
			gameAPIGroup.GET("status", s.gameStatusHandler)
		}
	}

	if p := os.Getenv("PORT"); p != "" {
		router.Run(":" + p)
	} else {
		router.Run(":" + s.config.DefaultPort)
	}

}
<commit_msg>Updated listGames to fit the ideal. Part of #92.<commit_after>package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/itsjamie/gin-cors"
	"github.com/jkomoros/boardgame"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

type Server struct {
	managers managerMap
	storage  StorageManager
	//We store the last error so that next time viewHandler is called we can
	//display it. Yes, this is a hack.
	lastErrorMessage string
	config           *ConfigMode
}

type Renderer struct {
	c        *gin.Context
	rendered bool
}

type Config struct {
	Dev  *ConfigMode
	Prod *ConfigMode
}

type ConfigMode struct {
	AllowedOrigins    string
	DefaultPort       string
	FirebaseProjectId string
	AdminUserIds      []string
	//This is a dangerous config. Only enable in Dev!
	DisableAdminChecking bool
}

type MoveForm struct {
	Name        string
	Description string
	Fields      []*MoveFormField
}

type MoveFormFieldType int

type MoveFormField struct {
	Name         string
	Type         boardgame.PropertyType
	DefaultValue interface{}
}

func (c *ConfigMode) Validate() error {
	if c.DefaultPort == "" {
		return errors.New("No default port provided")
	}
	//AllowedOrigins will just be default allow
	if c.AllowedOrigins == "" {
		log.Println("No AllowedOrigins found. Defaulting to '*'")
		c.AllowedOrigins = "*"
	}
	return nil
}

const (
	configFileName = "config.SECRET.json"
)

type managerMap map[string]*boardgame.GameManager

/*

Overview of the types of handlers and methods

server.fooHandler take a context. They grab all of the dependencies and pass them to the doers.
server.doFoo takes a renderer and all dependencies. It renders the result.
server.getRequestFoo fetches an argument from the context's request and nothing else
server.getFoo grabs a thing that was stored in Context and nothing else
server.setFoo sets a thing into context and nothing else
server.calcFoo takes dependencies and returns a result, with no touching context.
*/

/*
NewServer returns a new server. Get it to run by calling Start(). storage
should be the same underlying storage manager that is in use for manager.

Use it like so:

	func main() {
		storage := server.NewDefaultStorageManager()
		defer storage.Close()
		server.NewServer(storage, mygame.NewManager(storage)).Start()
	}

*/
func NewServer(storage StorageManager, managers ...*boardgame.GameManager) *Server {
	result := &Server{
		managers: make(managerMap),
		storage:  storage,
	}

	for _, manager := range managers {
		name := manager.Delegate().Name()
		result.managers[name] = manager
	}

	return result

}

func NewRenderer(c *gin.Context) *Renderer {
	return &Renderer{
		c,
		false,
	}
}

func (r *Renderer) Error(message string) {
	if r.rendered {
		panic("Error called on already-rendered renderer")
	}
	r.c.JSON(http.StatusOK, gin.H{
		"Status": "Failure",
		"Error":  message,
	})

	r.rendered = true
}

func (r *Renderer) Success(keys gin.H) {

	if r.rendered {
		panic("Success called on alread-rendered renderer")
	}

	if keys == nil {
		keys = gin.H{}
	}

	result := gin.H{}

	for key, val := range keys {
		result[key] = val
	}

	result["Status"] = "Success"

	r.c.JSON(http.StatusOK, result)

	r.rendered = true
}

//gameAPISetup fetches the game configured in the URL and puts it in context.
func (s *Server) gameAPISetup(c *gin.Context) {

	id := s.getRequestGameId(c)

	gameName := s.getRequestGameName(c)

	manager := s.managers[gameName]

	if manager == nil {
		log.Println("Couldnt' find manager for", gameName)
		return
	}

	game := manager.Game(id)

	//TODO: figure out a way to return a meaningful error

	if game == nil {
		log.Println("Couldn't find game with id", id)
		return
	}

	if game.Name() != c.Param("name") {
		log.Println("The name of the game was not what we were expecting. Wanted", c.Param("name"), "got", game.Name())
		return
	}

	s.setGame(c, game)

	cookie := s.getRequestCookie(c)

	if cookie == "" {
		log.Println("No cookie set")
		return
	}

	user := s.storage.GetUserByCookie(cookie)

	if user == nil {
		log.Println("No user associated with that cookie")
		return
	}

	s.setUser(c, user)

	s.setAdminAllowed(c, s.calcAdminAllowed(user))

	userIds := s.storage.UserIdsForGame(id)

	if userIds == nil {
		log.Println("No userIds associated with game")
	}

	effectiveViewingAsPlayer, inGame := s.calcViewingAsPlayer(userIds, user)

	if !inGame {
		//We aren't yet in game, so we need to join it.

		if effectiveViewingAsPlayer == boardgame.ObserverPlayerIndex {
			//I guess there weren't any slots.
			log.Println("The user is not in the game, but there are no empty slots to join in as.")
		} else {
			if err := s.storage.SetPlayerForGame(id, effectiveViewingAsPlayer, user.Id); err != nil {
				log.Println("Tried to set this user as player", effectiveViewingAsPlayer, "but failed:", err)
			} else {
				log.Println("User joined game", id, "as player", effectiveViewingAsPlayer)
			}
		}
	}

	s.setViewingAsPlayer(c, effectiveViewingAsPlayer)

}

func (s *Server) gameStatusHandler(c *gin.Context) {
	//This handler is designed to be a very simple status marker for the
	//current version of the specific game. It will be hit hard by all
	//clients, repeatedly, so it should be very fast.

	//TODO: use memcache for this handler

	game := s.getGame(c)

	r := NewRenderer(c)

	s.doGameStatus(r, game)

}

func (s *Server) doGameStatus(r *Renderer, game *boardgame.Game) {
	if game == nil {
		r.Error("Not Found")
		return
	}

	r.Success(gin.H{
		"Version": game.Version(),
	})
}

func (s *Server) newGameHandler(c *gin.Context) {

	r := NewRenderer(c)

	managerId := s.getRequestManager(c)

	manager := s.managers[managerId]

	s.doNewGame(r, manager)

}

func (s *Server) doNewGame(r *Renderer, manager *boardgame.GameManager) {

	if manager == nil {
		r.Error("No manager provided")
		return
	}

	game := boardgame.NewGame(manager)

	if game == nil {
		r.Error("No game could be created")
		return
	}

	if err := game.SetUp(0); err != nil {
		//TODO: communicate the error state back to the client in a sane way
		r.Error("Couldn't set up game: " + err.Error())
		return
	}

	r.Success(gin.H{
		"GameId":   game.Id(),
		"GameName": game.Name(),
	})
}

func (s *Server) listGamesHandler(c *gin.Context) {

	r := NewRenderer(c)
	s.doListGames(r)
}

func (s *Server) doListGames(r *Renderer) {
	r.Success(gin.H{
		"Games": s.storage.ListGames(10),
	})
}

func (s *Server) listManagerHandler(c *gin.Context) {
	var managerNames []string
	for name, _ := range s.managers {
		managerNames = append(managerNames, name)
	}

	r := NewRenderer(c)

	r.Success(gin.H{
		"Managers": managerNames,
	})
}

func (s *Server) gameViewHandler(c *gin.Context) {

	game := s.getGame(c)

	playerIndex := s.effectivePlayerIndex(c)

	r := NewRenderer(c)

	s.gameView(r, game, playerIndex)

}

func (s *Server) gameView(r *Renderer, game *boardgame.Game, playerIndex boardgame.PlayerIndex) {
	if game == nil {
		r.Error("Couldn't find game")
		return
	}

	if playerIndex == invalidPlayerIndex {
		r.Error("Got invalid playerIndex")
		return
	}

	args := gin.H{
		"Diagram":         game.CurrentState().SanitizedForPlayer(playerIndex).Diagram(),
		"Chest":           s.renderChest(game),
		"Forms":           s.generateForms(game),
		"Game":            game.JSONForPlayer(playerIndex),
		"Error":           s.lastErrorMessage,
		"ViewingAsPlayer": playerIndex,
	}

	s.lastErrorMessage = ""

	r.Success(args)

}

func (s *Server) moveHandler(c *gin.Context) {
	if c.Request.Method != http.MethodPost {
		panic("This can only be called as a post.")
	}

	r := NewRenderer(c)

	game := s.getGame(c)

	if game == nil {
		r.Error("Game not found")
		return
	}

	viewingPlayerIndex := s.getViewingAsPlayer(c)

	adminAllowed := s.getAdminAllowed(c)
	requestAdmin := s.getRequestAdmin(c)

	isAdmin := s.calcIsAdmin(adminAllowed, requestAdmin)

	proposer := viewingPlayerIndex

	if isAdmin {
		proposer = boardgame.AdminPlayerIndex
	}

	move, err := s.getMoveFromForm(c, game)

	if move == nil {

		errString := "No move returned"

		if err != nil {
			errString = err.Error()
		}

		r.Error("Couldn't get move: " + errString)
		return
	}

	if err := s.makeMove(game, proposer, move); err != nil {
		r.Error("Couldn't mave move: " + err.Error())
		return
	}

	r.Success(nil)
}

func (s *Server) makeMove(game *boardgame.Game, proposer boardgame.PlayerIndex, move boardgame.Move) error {

	if err := <-game.ProposeMove(move, proposer); err != nil {
		return errors.New(fmt.Sprint("Applying move failed: ", err))
	}
	//TODO: it would be nice if we could show which fixup moves we made, too,
	//somehow.

	return nil
}

func (s *Server) generateForms(game *boardgame.Game) []*MoveForm {

	var result []*MoveForm

	for _, move := range game.PlayerMoves() {

		move.DefaultsForState(game.CurrentState())

		moveItem := &MoveForm{
			Name:        move.Name(),
			Description: move.Description(),
			Fields:      formFields(move),
		}
		result = append(result, moveItem)
	}

	return result
}

func formFields(move boardgame.Move) []*MoveFormField {

	var result []*MoveFormField

	for fieldName, fieldType := range move.ReadSetter().Props() {

		val, _ := move.ReadSetter().Prop(fieldName)

		result = append(result, &MoveFormField{
			Name:         fieldName,
			Type:         fieldType,
			DefaultValue: val,
		})

	}

	return result
}

func (s *Server) renderChest(game *boardgame.Game) map[string][]interface{} {
	//Substantially copied from cli.renderChest().

	deck := make(map[string][]interface{})

	for _, name := range game.Chest().DeckNames() {

		components := game.Chest().Deck(name).Components()

		values := make([]interface{}, len(components))

		for i, component := range components {
			values[i] = struct {
				Index  int
				Values interface{}
			}{
				i,
				component.Values,
			}
		}

		deck[name] = values
	}

	return deck
}

//Start is where you start the server, and it never returns until it's time to shut down.
func (s *Server) Start() {

	if _, err := os.Stat(configFileName); os.IsNotExist(err) {
		log.Println("Couldn't find a " + configFileName + " in current directory. This file is required. Copy a starter one from boardgame/server/api/config.SAMPLE.json")
		return
	}

	contents, err := ioutil.ReadFile(configFileName)

	if err != nil {
		log.Println("Couldn't read config file:", err)
		return
	}

	var config Config

	if err := json.Unmarshal(contents, &config); err != nil {
		log.Println("couldn't unmarshal config file:", err)
		return
	}

	log.Println("Environment Variables")
	//Dbug print out the current environment
	for _, config := range os.Environ() {
		log.Println("Environ:", config)
	}

	if v := os.Getenv("GIN_MODE"); v == "release" {
		log.Println("Using release mode config")
		s.config = config.Prod
	} else {
		log.Println("Using dev mode config")
		s.config = config.Dev
	}

	if err := s.config.Validate(); err != nil {
		log.Println("The provided config was not valid: ", err)
		return
	}

	router := gin.Default()

	//We have everything prefixed by /api just in case at some point we do
	//want to host both static and api on the same logical server.
	mainGroup := router.Group("/api")
	mainGroup.Use(cors.Middleware(cors.Config{
		Origins:        s.config.AllowedOrigins,
		RequestHeaders: "content-type, Origin",
		ExposedHeaders: "content-type",
		Methods:        "GET, POST",
		Credentials:    true,
	}))

	{
		mainGroup.GET("list/game", s.listGamesHandler)
		mainGroup.POST("new/game", s.newGameHandler)
		mainGroup.GET("list/manager", s.listManagerHandler)

		mainGroup.POST("auth/cookie", s.authCookieHandler)
		mainGroup.OPTIONS("auth/cookie", s.authCookieHandler)

		gameAPIGroup := mainGroup.Group("game/:name/:id")
		gameAPIGroup.Use(s.gameAPISetup)
		{
			gameAPIGroup.GET("view", s.gameViewHandler)
			gameAPIGroup.POST("move", s.moveHandler)
			gameAPIGroup.GET("status", s.gameStatusHandler)
		}
	}

	if p := os.Getenv("PORT"); p != "" {
		router.Run(":" + p)
	} else {
		router.Run(":" + s.config.DefaultPort)
	}

}
<|endoftext|>