<commit_before>// Copyright 2010 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 dict implements the Dictionary Server Protocol
// as defined in RFC 2229.
package dict // import "golang.org/x/net/dict"

import (
	"net/textproto"
	"strconv"
	"strings"
)

// A Client represents a client connection to a dictionary server.
type Client struct {
	text *textproto.Conn
}

// Dial returns a new client connected to a dictionary server at
// addr on the given network.
func Dial(network, addr string) (*Client, error) {
	text, err := textproto.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	_, _, err = text.ReadCodeLine(220)
	if err != nil {
		text.Close()
		return nil, err
	}
	return &Client{text: text}, nil
}

// Close closes the connection to the dictionary server.
func (c *Client) Close() error {
	return c.text.Close()
}

// A Dict represents a dictionary available on the server.
type Dict struct {
	Name string // short name of dictionary
	Desc string // long description
}

// Dicts returns a list of the dictionaries available on the server.
func (c *Client) Dicts() ([]Dict, error) {
	id, err := c.text.Cmd("SHOW DB")
	if err != nil {
		return nil, err
	}

	c.text.StartResponse(id)
	defer c.text.EndResponse(id)

	_, _, err = c.text.ReadCodeLine(110)
	if err != nil {
		return nil, err
	}
	lines, err := c.text.ReadDotLines()
	if err != nil {
		return nil, err
	}
	_, _, err = c.text.ReadCodeLine(250)

	dicts := make([]Dict, len(lines))
	for i := range dicts {
		d := &dicts[i]
		a, _ := fields(lines[i])
		if len(a) < 2 {
			return nil, textproto.ProtocolError("invalid dictionary: " + lines[i])
		}
		d.Name = a[0]
		d.Desc = a[1]
	}
	return dicts, err
}

// A Defn represents a definition.
type Defn struct {
	Dict Dict   // Dict where definition was found
	Word string // Word being defined
	Text []byte // Definition text, typically multiple lines
}

// Define requests the definition of the given word.
// The argument dict names the dictionary to use,
// the Name field of a Dict returned by Dicts.
//
// The special dictionary name "*" means to look in all the
// server's dictionaries.
// The special dictionary name "!" means to look in all the
// server's dictionaries in turn, stopping after finding the word
// in one of them.
func (c *Client) Define(dict, word string) ([]*Defn, error) {
	id, err := c.text.Cmd("DEFINE %s %q", dict, word)
	if err != nil {
		return nil, err
	}

	c.text.StartResponse(id)
	defer c.text.EndResponse(id)

	_, line, err := c.text.ReadCodeLine(150)
	if err != nil {
		return nil, err
	}
	a, _ := fields(line)
	if len(a) < 1 {
		return nil, textproto.ProtocolError("malformed response: " + line)
	}
	n, err := strconv.Atoi(a[0])
	if err != nil {
		return nil, textproto.ProtocolError("invalid definition count: " + a[0])
	}
	def := make([]*Defn, n)
	for i := 0; i < n; i++ {
		_, line, err = c.text.ReadCodeLine(151)
		if err != nil {
			return nil, err
		}
		a, _ := fields(line)
		if len(a) < 3 {
			// skip it, to keep protocol in sync
			i--
			n--
			def = def[0:n]
			continue
		}
		d := &Defn{Word: a[0], Dict: Dict{a[1], a[2]}}
		d.Text, err = c.text.ReadDotBytes()
		if err != nil {
			return nil, err
		}
		def[i] = d
	}
	_, _, err = c.text.ReadCodeLine(250)
	return def, err
}

// Fields returns the fields in s.
// Fields are space separated unquoted words
// or quoted with single or double quote.
func fields(s string) ([]string, error) {
	var v []string
	i := 0
	for {
		for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
			i++
		}
		if i >= len(s) {
			break
		}
		if s[i] == '"' || s[i] == '\'' {
			q := s[i]
			// quoted string
			var j int
			for j = i + 1; ; j++ {
				if j >= len(s) {
					return nil, textproto.ProtocolError("malformed quoted string")
				}
				if s[j] == '\\' {
					j++
					continue
				}
				if s[j] == q {
					j++
					break
				}
			}
			v = append(v, unquote(s[i+1:j-1]))
			i = j
		} else {
			// atom
			var j int
			for j = i; j < len(s); j++ {
				if s[j] == ' ' || s[j] == '\t' || s[j] == '\\' || s[j] == '"' || s[j] == '\'' {
					break
				}
			}
			v = append(v, s[i:j])
			i = j
		}
		if i < len(s) {
			c := s[i]
			if c != ' ' && c != '\t' {
				return nil, textproto.ProtocolError("quotes not on word boundaries")
			}
		}
	}
	return v, nil
}

func unquote(s string) string {
	if strings.Index(s, "\\") < 0 {
		return s
	}
	b := []byte(s)
	w := 0
	for r := 0; r < len(b); r++ {
		c := b[r]
		if c == '\\' {
			r++
			c = b[r]
		}
		b[w] = c
		w++
	}
	return string(b[0:w])
}
<commit_msg>dict: document package as frozen<commit_after>// Copyright 2010 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 dict implements the Dictionary Server Protocol
// as defined in RFC 2229.
//
// The dict package is frozen and is not accepting new features.
package dict // import "golang.org/x/net/dict"

import (
	"net/textproto"
	"strconv"
	"strings"
)

// A Client represents a client connection to a dictionary server.
type Client struct {
	text *textproto.Conn
}

// Dial returns a new client connected to a dictionary server at
// addr on the given network.
func Dial(network, addr string) (*Client, error) {
	text, err := textproto.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	_, _, err = text.ReadCodeLine(220)
	if err != nil {
		text.Close()
		return nil, err
	}
	return &Client{text: text}, nil
}

// Close closes the connection to the dictionary server.
func (c *Client) Close() error {
	return c.text.Close()
}

// A Dict represents a dictionary available on the server.
type Dict struct {
	Name string // short name of dictionary
	Desc string // long description
}

// Dicts returns a list of the dictionaries available on the server.
func (c *Client) Dicts() ([]Dict, error) {
	id, err := c.text.Cmd("SHOW DB")
	if err != nil {
		return nil, err
	}

	c.text.StartResponse(id)
	defer c.text.EndResponse(id)

	_, _, err = c.text.ReadCodeLine(110)
	if err != nil {
		return nil, err
	}
	lines, err := c.text.ReadDotLines()
	if err != nil {
		return nil, err
	}
	_, _, err = c.text.ReadCodeLine(250)

	dicts := make([]Dict, len(lines))
	for i := range dicts {
		d := &dicts[i]
		a, _ := fields(lines[i])
		if len(a) < 2 {
			return nil, textproto.ProtocolError("invalid dictionary: " + lines[i])
		}
		d.Name = a[0]
		d.Desc = a[1]
	}
	return dicts, err
}

// A Defn represents a definition.
type Defn struct {
	Dict Dict   // Dict where definition was found
	Word string // Word being defined
	Text []byte // Definition text, typically multiple lines
}

// Define requests the definition of the given word.
// The argument dict names the dictionary to use,
// the Name field of a Dict returned by Dicts.
//
// The special dictionary name "*" means to look in all the
// server's dictionaries.
// The special dictionary name "!" means to look in all the
// server's dictionaries in turn, stopping after finding the word
// in one of them.
func (c *Client) Define(dict, word string) ([]*Defn, error) {
	id, err := c.text.Cmd("DEFINE %s %q", dict, word)
	if err != nil {
		return nil, err
	}

	c.text.StartResponse(id)
	defer c.text.EndResponse(id)

	_, line, err := c.text.ReadCodeLine(150)
	if err != nil {
		return nil, err
	}
	a, _ := fields(line)
	if len(a) < 1 {
		return nil, textproto.ProtocolError("malformed response: " + line)
	}
	n, err := strconv.Atoi(a[0])
	if err != nil {
		return nil, textproto.ProtocolError("invalid definition count: " + a[0])
	}
	def := make([]*Defn, n)
	for i := 0; i < n; i++ {
		_, line, err = c.text.ReadCodeLine(151)
		if err != nil {
			return nil, err
		}
		a, _ := fields(line)
		if len(a) < 3 {
			// skip it, to keep protocol in sync
			i--
			n--
			def = def[0:n]
			continue
		}
		d := &Defn{Word: a[0], Dict: Dict{a[1], a[2]}}
		d.Text, err = c.text.ReadDotBytes()
		if err != nil {
			return nil, err
		}
		def[i] = d
	}
	_, _, err = c.text.ReadCodeLine(250)
	return def, err
}

// Fields returns the fields in s.
// Fields are space separated unquoted words
// or quoted with single or double quote.
func fields(s string) ([]string, error) {
	var v []string
	i := 0
	for {
		for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
			i++
		}
		if i >= len(s) {
			break
		}
		if s[i] == '"' || s[i] == '\'' {
			q := s[i]
			// quoted string
			var j int
			for j = i + 1; ; j++ {
				if j >= len(s) {
					return nil, textproto.ProtocolError("malformed quoted string")
				}
				if s[j] == '\\' {
					j++
					continue
				}
				if s[j] == q {
					j++
					break
				}
			}
			v = append(v, unquote(s[i+1:j-1]))
			i = j
		} else {
			// atom
			var j int
			for j = i; j < len(s); j++ {
				if s[j] == ' ' || s[j] == '\t' || s[j] == '\\' || s[j] == '"' || s[j] == '\'' {
					break
				}
			}
			v = append(v, s[i:j])
			i = j
		}
		if i < len(s) {
			c := s[i]
			if c != ' ' && c != '\t' {
				return nil, textproto.ProtocolError("quotes not on word boundaries")
			}
		}
	}
	return v, nil
}

func unquote(s string) string {
	if strings.Index(s, "\\") < 0 {
		return s
	}
	b := []byte(s)
	w := 0
	for r := 0; r < len(b); r++ {
		c := b[r]
		if c == '\\' {
			r++
			c = b[r]
		}
		b[w] = c
		w++
	}
	return string(b[0:w])
}
<|endoftext|>


<commit_before>// Experiments with creating an alternate to diff3 and merge, motivated
// by my annoyance with these issues:
//
// 1) Ordered lists are not auto-merged. For example, C++ include lists
//    and build dependencies are typically in lexically sorted ordered,
//    so the merge tool should check to see if conflicting lines are
//    part of such a list in all 3 files, and if so treat them as
//		ordered sets, where
//    we don't care about the conflicting lines, but rather about the
//    set operations performed: insert and remove; and those operations
//    won't be in conflict.
//
// 2) Mis-alignment of matches for added functions. For example, we  desire
//    this, where are the lines of the new function have been marked new:
//
//      $diff -y file2 file1
//      void func1() {            void func1() {
//        x += 1                     x += 1
//      }                         }
//
//                              > void functhreehalves() {
//                              >   x += 1.5
//                              > }
//                              >
//      void func2() {            void func2() {
//        x += 2                    x += 2
//      }                         }
//
//    However, we often get this from diff, and hence from merge:
//
//      $diff -y file2 file1
//      void func1() {            void func1() {
//        x += 1                     x += 1
//      }                       > }
//                              >
//                              > void functhreehalves() {
//                              >   x += 1.5
//                                }
//
//      void func2() {            void func2() {
//        x += 2                    x += 2
//      }                         }
//
//    (example from http://fabiensanglard.net/git_code_review/diff.php).
//
// 3) Non-conflicting intra-line changes are not recognized as such; for example:
//
//      Base                 |   Yours                |   Theirs
//      if (y == x + 19) {   |   if (y == x + 17) {   |   if (z == x + 19) {
//                           |                ^^      |       ^
//
//    merge considers the lines in conflict:
//
//      <<<<<<< Yours
//      if (oldVar == x + 17) {
//      =======
//      if (newVar == x + 19) {
//      >>>>>>> Theirs
//
// 4) Moves of a block of lines should not be considered adds of new lines and
//    deletes of old lines, but rather recognized as a move so that changes
//    within the block of lines can be automatically merged.
//
// 5) If you've renamed a symbol in your file (i.e. changed ALL lines that
//    contain that symbol), and the other file adds uses of that symbol,
//    the tool should offer some means of automatically applying the renaming
//    to the new uses.  BONUS.
//
// 6) Changing the indentation of lines is treated as changing the entire line,
//    rather than being treated as separate from changing the characters to the
//    right of the indentation.

package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"io"
	"io/ioutil"
	"bytes"

	"github.com/golang/glog"

	"github.com/jamessynge/diffmerge/dm"
)

var (
	pDiff3Flag = flag.Bool(
		"diff3", false, "Find difference between 3 files.")

	pStatusOnlyFlag = flag.Bool(
		"brief", false, "Report (via exit code) whether there were differences "+
			" (for diff) or conflicts (for diff3/merge) found.")

	pSideBySideFlag = flag.Bool(
		"side-by-side", true, "For diff of two files, display results side-by-side.")
)

// TODO Add support for merge(1)'s -L (label) flag, which can appear up to
// 3 times in the command line args. See https://play.golang.org/p/Ig5sm7jA14
// for an example of how to do this.

type CmdStatus int
const (
	ConflictFree CmdStatus = iota
	SomeConflicts
	AnError
	NoDifferences = ConflictFree
	SomeDifferences = SomeConflicts
)

func FailWithMessage(showUsage bool, format string, a ...interface{}) {
	msg := fmt.Sprintf(format, a...)
	glog.Error(msg)
	fmt.Fprintln(os.Stderr, msg)
	if showUsage {
		flag.Usage()
	}
	os.Exit(int(AnError) & 0xff)
}

type cmdInputs struct {
	readingStdin bool
	fileNames    []string
	files        []*dm.File
	perm os.FileMode

	// Output used for merge with 3 inputs and one output.
	outputFileName string

	diffConfig dm.DifferencerConfig
}

func (p *cmdInputs) AddInputFile(fileName string) {
	if fileName == "-" {
		if p.readingStdin {
			FailWithMessage(false, "Only one input file maybe '-' (for standard input).")
		}
		p.readingStdin = true
	}
	file, err := dm.ReadFile(fileName)
	if err != nil {
		FailWithMessage(false, "Failed to read file %s: %s", fileName, err)
		os.Exit(int(AnError) & 0xff)
	}
	p.fileNames = append(p.fileNames, fileName)
	p.files = append(p.files, file)
	if p.perm == 0 && fileName != "-" {
		fi, err := os.Stat(fileName)
		if err == nil {
			p.perm = fi.Mode().Perm()
		}
	}
}

func (p *cmdInputs) diff2Files(fromFile, toFile *dm.File) (pairs dm.BlockPairs, status CmdStatus) {
	pairs = dm.PerformDiff2(fromFile, toFile, p.diffConfig)
	glog.Flush()
	if len(pairs) == 1 && pairs[0].IsMatch {
		status = NoDifferences
	} else {
		status = SomeDifferences
	}
	return
}

func (p *cmdInputs) outputFile(f *dm.File) {
	if p.outputFileName != "" {
		err := ioutil.WriteFile(p.outputFileName, f.Body, p.perm)
		if err != nil {
			FailWithMessage(false, "Failed writing to %s; error: %s", p.outputFileName, err)
		}
	} else {
		r := bytes.NewReader(f.Body)
		_, err := io.Copy(os.Stdout, r)
		if err != nil {
			FailWithMessage(false, "Failed writing to stdout; error: %s", err)
		}
	}
}

func (p *cmdInputs) PerformDiff2() CmdStatus {
	fromFile, toFile := p.files[0], p.files[1]
	pairs, status := p.diff2Files(fromFile, toFile)
	if *pSideBySideFlag {
		dm.FormatSideBySide(fromFile, toFile, pairs, false, os.Stdout, dm.DefaultSideBySideConfig)
	} else {
		dm.FormatInterleaved(pairs, false, fromFile, toFile, os.Stdout, true)
	}
	return status
}

func (p *cmdInputs) PerformDiff3() CmdStatus {
	return AnError
}

func (p *cmdInputs) PerformMerge() CmdStatus {
	yours, base, theirs := p.files[0], p.files[1], p.files[2]
	b2yPairs, b2yStatus := p.diff2Files(base, yours)
	b2tPairs, b2tStatus := p.diff2Files(base, theirs)

	// len(.) == 1 expression included temporarily to supporess complaints about
	// unused vars.  TODO Remove.
	if b2yStatus == NoDifferences && len(b2yPairs) == 1 {
		// No changes in your file, so there can be no conflicts; output their file.
		p.outputFile(theirs)
		return ConflictFree
	} else if b2tStatus == NoDifferences  && len(b2tPairs) == 1 {
		// No changes in their file, so there can be no conflicts; output your file.
		p.outputFile(yours)
		return ConflictFree
	}

	// Determine if there are conflicts.

	return AnError // TODO Replace with correct value.
}

func main() {
	diffConfig := &dm.DifferencerConfig{}
	diffConfig.CreateFlags(flag.CommandLine)
	flag.Parse() // Scan the arguments list

	cmd := filepath.Base(os.Args[0])
	glog.V(1).Infoln("cmd =", cmd)

	nArgs := flag.NArg()
	if !(2 <= nArgs && nArgs <= 4) {
		FailWithMessage(true, "Wrong number of file arguments")
	}
	var ci cmdInputs
	ci.diffConfig = *diffConfig
	ci.AddInputFile(flag.Arg(0))
	ci.AddInputFile(flag.Arg(1))
	if nArgs > 2 {
		ci.AddInputFile(flag.Arg(2))
		if nArgs > 3 {
			ci.outputFileName = flag.Arg(3)
		}
	}

	var status CmdStatus
	if nArgs == 2 {
		status = ci.PerformDiff2()
	} else if *pDiff3Flag {
		status = ci.PerformDiff3()
	} else {
		status = ci.PerformMerge()
	}

	os.Exit(int(status) & 0xff)
}
<commit_msg>Snapshot and go fmt.<commit_after>// Experiments with creating an alternate to diff3 and merge, motivated
// by my annoyance with these issues:
//
// 1) Ordered lists are not auto-merged. For example, C++ include lists
//    and build dependencies are typically in lexically sorted ordered,
//    so the merge tool should check to see if conflicting lines are
//    part of such a list in all 3 files, and if so treat them as
//		ordered sets, where
//    we don't care about the conflicting lines, but rather about the
//    set operations performed: insert and remove; and those operations
//    won't be in conflict.
//
// 2) Mis-alignment of matches for added functions. For example, we  desire
//    this, where are the lines of the new function have been marked new:
//
//      $diff -y file2 file1
//      void func1() {            void func1() {
//        x += 1                     x += 1
//      }                         }
//
//                              > void functhreehalves() {
//                              >   x += 1.5
//                              > }
//                              >
//      void func2() {            void func2() {
//        x += 2                    x += 2
//      }                         }
//
//    However, we often get this from diff, and hence from merge:
//
//      $diff -y file2 file1
//      void func1() {            void func1() {
//        x += 1                     x += 1
//      }                       > }
//                              >
//                              > void functhreehalves() {
//                              >   x += 1.5
//                                }
//
//      void func2() {            void func2() {
//        x += 2                    x += 2
//      }                         }
//
//    (example from http://fabiensanglard.net/git_code_review/diff.php).
//
// 3) Non-conflicting intra-line changes are not recognized as such; for example:
//
//      Base                 |   Yours                |   Theirs
//      if (y == x + 19) {   |   if (y == x + 17) {   |   if (z == x + 19) {
//                           |                ^^      |       ^
//
//    merge considers the lines in conflict:
//
//      <<<<<<< Yours
//      if (oldVar == x + 17) {
//      =======
//      if (newVar == x + 19) {
//      >>>>>>> Theirs
//
// 4) Moves of a block of lines should not be considered adds of new lines and
//    deletes of old lines, but rather recognized as a move so that changes
//    within the block of lines can be automatically merged.
//
// 5) If you've renamed a symbol in your file (i.e. changed ALL lines that
//    contain that symbol), and the other file adds uses of that symbol,
//    the tool should offer some means of automatically applying the renaming
//    to the new uses.  BONUS.
//
// 6) Changing the indentation of lines is treated as changing the entire line,
//    rather than being treated as separate from changing the characters to the
//    right of the indentation.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/golang/glog"

	"github.com/jamessynge/diffmerge/dm"
)

var (
	pDiff3Flag = flag.Bool(
		"diff3", false, "Find difference between 3 files.")

	pStatusOnlyFlag = flag.Bool(
		"brief", false, "Report (via exit code) whether there were differences "+
			" (for diff) or conflicts (for diff3/merge) found.")

	pSideBySideFlag = flag.Bool(
		"side-by-side", true, "For diff of two files, display results side-by-side.")
)

// TODO Add support for merge(1)'s -L (label) flag, which can appear up to
// 3 times in the command line args. See https://play.golang.org/p/Ig5sm7jA14
// for an example of how to do this.

type CmdStatus int

const (
	ConflictFree CmdStatus = iota
	SomeConflicts
	AnError
	NoDifferences   = ConflictFree
	SomeDifferences = SomeConflicts
)

func FailWithMessage(showUsage bool, format string, a ...interface{}) {
	msg := fmt.Sprintf(format, a...)
	glog.Error(msg)
	fmt.Fprintln(os.Stderr, msg)
	if showUsage {
		flag.Usage()
	}
	os.Exit(int(AnError) & 0xff)
}

type cmdInputs struct {
	readingStdin bool
	fileNames    []string
	files        []*dm.File
	perm         os.FileMode

	// Output used for merge with 3 inputs and one output.
	outputFileName string

	diffConfig dm.DifferencerConfig
}

func (p *cmdInputs) AddInputFile(fileName string) {
	if fileName == "-" {
		if p.readingStdin {
			FailWithMessage(false, "Only one input file maybe '-' (for standard input).")
		}
		p.readingStdin = true
	}
	file, err := dm.ReadFile(fileName)
	if err != nil {
		FailWithMessage(false, "Failed to read file %s: %s", fileName, err)
		os.Exit(int(AnError) & 0xff)
	}
	p.fileNames = append(p.fileNames, fileName)
	p.files = append(p.files, file)
	if p.perm == 0 && fileName != "-" {
		fi, err := os.Stat(fileName)
		if err == nil {
			p.perm = fi.Mode().Perm()
		}
	}
}

func (p *cmdInputs) diff2Files(fromFile, toFile *dm.File) (pairs dm.BlockPairs, status CmdStatus) {
	pairs = dm.PerformDiff2(fromFile, toFile, p.diffConfig)
	glog.Flush()
	if len(pairs) == 1 && pairs[0].IsMatch {
		status = NoDifferences
	} else {
		status = SomeDifferences
	}
	return
}

func (p *cmdInputs) outputFile(f *dm.File) {
	if p.outputFileName != "" {
		err := ioutil.WriteFile(p.outputFileName, f.Body, p.perm)
		if err != nil {
			FailWithMessage(false, "Failed writing to %s; error: %s", p.outputFileName, err)
		}
	} else {
		r := bytes.NewReader(f.Body)
		_, err := io.Copy(os.Stdout, r)
		if err != nil {
			FailWithMessage(false, "Failed writing to stdout; error: %s", err)
		}
	}
}

func (p *cmdInputs) PerformDiff2() CmdStatus {
	fromFile, toFile := p.files[0], p.files[1]
	pairs, status := p.diff2Files(fromFile, toFile)
	if *pSideBySideFlag {
		dm.FormatSideBySide(fromFile, toFile, pairs, false, os.Stdout, dm.DefaultSideBySideConfig)
	} else {
		dm.FormatInterleaved(pairs, false, fromFile, toFile, os.Stdout, true)
	}
	return status
}

func (p *cmdInputs) PerformDiff3() CmdStatus {
	return AnError
}

func (p *cmdInputs) PerformMerge() CmdStatus {
	yours, base, theirs := p.files[0], p.files[1], p.files[2]
	b2yPairs, b2yStatus := p.diff2Files(base, yours)
	b2tPairs, b2tStatus := p.diff2Files(base, theirs)

	// len(.) == 1 expression included temporarily to supporess complaints about
	// unused vars.  TODO Remove.
	if b2yStatus == NoDifferences && len(b2yPairs) == 1 {
		// No changes in your file, so there can be no conflicts; output their file.
		p.outputFile(theirs)
		return ConflictFree
	} else if b2tStatus == NoDifferences && len(b2tPairs) == 1 {
		// No changes in their file, so there can be no conflicts; output your file.
		p.outputFile(yours)
		return ConflictFree
	}

	// Determine if there are possible conflicts. If so, then maybe do a diff of
	// yours vs. theirs, which may help eliminate the diff.
	// Start by sorting on the same index, base.
	dm.SortBlockPairsByAIndex(b2yPairs)
	dm.SortBlockPairsByAIndex(b2tPairs)

	return AnError // TODO Replace with correct value.
}

func main() {
	diffConfig := &dm.DifferencerConfig{}
	diffConfig.CreateFlags(flag.CommandLine)
	flag.Parse() // Scan the arguments list

	cmd := filepath.Base(os.Args[0])
	glog.V(1).Infoln("cmd =", cmd)

	nArgs := flag.NArg()
	if !(2 <= nArgs && nArgs <= 4) {
		FailWithMessage(true, "Wrong number of file arguments")
	}
	var ci cmdInputs
	ci.diffConfig = *diffConfig
	ci.AddInputFile(flag.Arg(0))
	ci.AddInputFile(flag.Arg(1))
	if nArgs > 2 {
		ci.AddInputFile(flag.Arg(2))
		if nArgs > 3 {
			ci.outputFileName = flag.Arg(3)
		}
	}

	var status CmdStatus
	if nArgs == 2 {
		status = ci.PerformDiff2()
	} else if *pDiff3Flag {
		status = ci.PerformDiff3()
	} else {
		status = ci.PerformMerge()
	}

	os.Exit(int(status) & 0xff)
}
<|endoftext|>


<commit_before>package terraform

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"reflect"
	"strings"

	"terraform-resource/models"
	"terraform-resource/storage"
)

type Client struct {
	Model         models.Terraform
	StorageDriver storage.Storage
	LogWriter     io.Writer
}

func (c Client) Apply() error {
	var sourcePath string
	if c.Model.PlanRun {
		sourcePath = c.Model.PlanFileLocalPath
	} else {
		var err error
		sourcePath, err = c.fetchSource()
		if err != nil {
			return err
		}
	}

	applyArgs := []string{
		"apply",
		"-backup='-'",  // no need to backup state file
		"-input=false", // do not prompt for inputs
	}
	if c.Model.PlanRun == false {
		applyArgs = append(applyArgs, c.varFlags()...)
		applyArgs = append(applyArgs, fmt.Sprintf("-state=%s", c.Model.StateFileLocalPath))
	} else {
		applyArgs = append(applyArgs, fmt.Sprintf("-state-out=%s", c.Model.StateFileLocalPath))
	}

	applyArgs = append(applyArgs, sourcePath)

	applyCmd := c.terraformCmd(applyArgs)
	applyCmd.Stdout = c.LogWriter
	applyCmd.Stderr = c.LogWriter
	err := applyCmd.Run()
	if err != nil {
		return fmt.Errorf("Failed to run Terraform command: %s", err)
	}

	return nil
}

func (c Client) Destroy() error {
	sourcePath, err := c.fetchSource()
	if err != nil {
		return err
	}

	destroyArgs := []string{
		"destroy",
		"-backup='-'", // no need to backup state file
		"-force",      // do not prompt for confirmation
		fmt.Sprintf("-state=%s", c.Model.StateFileLocalPath),
	}
	destroyArgs = append(destroyArgs, c.varFlags()...)
	destroyArgs = append(destroyArgs, sourcePath)

	destroyCmd := c.terraformCmd(destroyArgs)
	destroyCmd.Stdout = c.LogWriter
	destroyCmd.Stderr = c.LogWriter
	err = destroyCmd.Run()
	if err != nil {
		return fmt.Errorf("Failed to run Terraform command: %s", err)
	}

	return nil
}

func (c Client) Plan() error {
	sourcePath, err := c.fetchSource()
	if err != nil {
		return err
	}

	planArgs := []string{
		"plan",
		"-input=false", // do not prompt for inputs
		fmt.Sprintf("-out=%s", c.Model.PlanFileLocalPath),
		fmt.Sprintf("-state=%s", c.Model.StateFileLocalPath),
	}
	planArgs = append(planArgs, c.varFlags()...)
	planArgs = append(planArgs, sourcePath)

	planCmd := c.terraformCmd(planArgs)
	planCmd.Stdout = c.LogWriter
	planCmd.Stderr = c.LogWriter
	err = planCmd.Run()
	if err != nil {
		return fmt.Errorf("Failed to run Terraform command: %s", err)
	}

	return nil
}

func (c Client) Output() (map[string]map[string]interface{}, error) {
	outputArgs := []string{
		"output",
		"-json",
		fmt.Sprintf("-state=%s", c.Model.StateFileLocalPath),
	}

	if c.Model.OutputModule != "" {
		outputArgs = append(outputArgs, fmt.Sprintf("-module=%s", c.Model.OutputModule))
	}

	outputCmd := c.terraformCmd(outputArgs)

	rawOutput, err := outputCmd.CombinedOutput()
	if err != nil {
		// TF CLI currently doesn't provide a nice way to detect an empty set of outputs
		// https://github.com/hashicorp/terraform/issues/11696
		if strings.Contains(string(rawOutput), "no outputs defined") {
			rawOutput = []byte("{}")
		} else {
			return nil, fmt.Errorf("Failed to retrieve output.\nError: %s\nOutput: %s", err, rawOutput)
		}
	}

	tfOutput := map[string]map[string]interface{}{}
	if err = json.Unmarshal(rawOutput, &tfOutput); err != nil {
		return nil, fmt.Errorf("Failed to unmarshal JSON output.\nError: %s\nOutput: %s", err, rawOutput)
	}

	return tfOutput, nil
}

func (c Client) Version() (string, error) {
	outputCmd := c.terraformCmd([]string{
		"-v",
	})
	output, err := outputCmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("Failed to retrieve version.\nError: %s\nOutput: %s", err, output)
	}

	return strings.TrimSpace(string(output)), nil
}

func (c Client) Import() error {
	if len(c.Model.Imports) == 0 {
		return nil
	}

	sourcePath, err := c.fetchSource()
	if err != nil {
		return err
	}

	for tfID, iaasID := range c.Model.Imports {
		exists, err := c.resourceExists(tfID)
		if err != nil {
			return fmt.Errorf("Failed to check for existence of resource %s %s.\nError: %s", tfID, iaasID, err)
		}
		if exists {
			c.LogWriter.Write([]byte(fmt.Sprintf("Skipping import of `%s: %s` as it already exists in the statefile...\n", tfID, iaasID)))
			continue
		}

		c.LogWriter.Write([]byte(fmt.Sprintf("Importing `%s: %s`...\n", tfID, iaasID)))
		importArgs := []string{
			"import",
			fmt.Sprintf("-state=%s", c.Model.StateFileLocalPath),
			fmt.Sprintf("-config=%s", sourcePath),
		}
		importArgs = append(importArgs, c.varFlags()...)
		importArgs = append(importArgs, tfID)
		importArgs = append(importArgs, iaasID)

		importCmd := c.terraformCmd(importArgs)
		rawOutput, err := importCmd.CombinedOutput()
		if err != nil {
			return fmt.Errorf("Failed to import resource %s %s.\nError: %s\nOutput: %s", tfID, iaasID, err, rawOutput)
		}
	}

	return nil
}

func (c Client) resourceExists(tfID string) (bool, error) {
	if _, err := os.Stat(c.Model.StateFileLocalPath); os.IsNotExist(err) {
		return false, nil
	}

	cmd := c.terraformCmd([]string{
		"state",
		"list",
		fmt.Sprintf("-state=%s", c.Model.StateFileLocalPath),
		tfID,
	})
	rawOutput, err := cmd.CombinedOutput()
	if err != nil {
		return false, fmt.Errorf("Error: %s, Output: %s", err, rawOutput)
	}

	// command returns the ID of the resource if it exists
	return (len(strings.TrimSpace(string(rawOutput))) > 0), nil
}

func (c Client) terraformCmd(args []string) *exec.Cmd {
	cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("terraform %s", strings.Join(args, " ")))

	cmd.Env = []string{
		fmt.Sprintf("PATH=%s", os.Getenv("PATH")),
		"CHECKPOINT_DISABLE=1",
	}
	for key, value := range c.Model.Env {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, value))
	}

	return cmd
}

func (c Client) fetchSource() (string, error) {
	sourceDir := c.Model.Source
	getCmd := c.terraformCmd([]string{
		"get",
		"-update",
		sourceDir,
	})
	if getOutput, getErr := getCmd.CombinedOutput(); getErr != nil {
		return "", fmt.Errorf("terraform get command failed.\nError: %s\nOutput: %s", getErr, getOutput)
	}

	return sourceDir, nil
}

func (c Client) varFlags() []string {
	args := []string{}
	for key, val := range c.Model.Vars {
		args = append(args, "-var", fmt.Sprintf("'%s=%s'", key, formatVar(val)))
	}
	return args
}

func formatVar(value interface{}) string {
	valType := reflect.TypeOf(value)
	if valType == nil {
		return "null"
	}

	switch valType.Kind() {
	case reflect.Slice:
		valSlice, _ := value.([]interface{})
		sliceVars := []string{}
		for _, v := range valSlice {
			sliceVars = append(sliceVars, formatVar(v))
		}
		return fmt.Sprintf("[%s]", strings.Join(sliceVars, ","))
	case reflect.Map:
		valMap, _ := value.(map[string]interface{})
		mapVars := []string{}
		for k, v := range valMap {
			mapVars = append(mapVars, fmt.Sprintf("%s=%s", k, formatVar(v)))
		}
		return fmt.Sprintf("{%s}", strings.Join(mapVars, ","))
	}
	return fmt.Sprintf("%#v", value)
}
<commit_msg>Run terraform init to fetch plugins<commit_after>package terraform

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"reflect"
	"strings"

	"terraform-resource/models"
	"terraform-resource/storage"
)

type Client struct {
	Model         models.Terraform
	StorageDriver storage.Storage
	LogWriter     io.Writer
}

func (c Client) Apply() error {
	err := c.runInit()
	if err != nil {
		return err
	}

	var sourcePath string
	if c.Model.PlanRun {
		sourcePath = c.Model.PlanFileLocalPath
	} else {
		sourcePath = c.Model.Source
	}

	applyArgs := []string{
		"apply",
		"-backup='-'",  // no need to backup state file
		"-input=false", // do not prompt for inputs
	}
	if c.Model.PlanRun == false {
		applyArgs = append(applyArgs, c.varFlags()...)
		applyArgs = append(applyArgs, fmt.Sprintf("-state=%s", c.Model.StateFileLocalPath))
	} else {
		applyArgs = append(applyArgs, fmt.Sprintf("-state-out=%s", c.Model.StateFileLocalPath))
	}

	applyArgs = append(applyArgs, sourcePath)

	applyCmd := c.terraformCmd(applyArgs)
	applyCmd.Stdout = c.LogWriter
	applyCmd.Stderr = c.LogWriter
	err = applyCmd.Run()
	if err != nil {
		return fmt.Errorf("Failed to run Terraform command: %s", err)
	}

	return nil
}

func (c Client) Destroy() error {
	err := c.runInit()
	if err != nil {
		return err
	}

	destroyArgs := []string{
		"destroy",
		"-backup='-'", // no need to backup state file
		"-force",      // do not prompt for confirmation
		fmt.Sprintf("-state=%s", c.Model.StateFileLocalPath),
	}
	destroyArgs = append(destroyArgs, c.varFlags()...)
	destroyArgs = append(destroyArgs, c.Model.Source)

	destroyCmd := c.terraformCmd(destroyArgs)
	destroyCmd.Stdout = c.LogWriter
	destroyCmd.Stderr = c.LogWriter
	err = destroyCmd.Run()
	if err != nil {
		return fmt.Errorf("Failed to run Terraform command: %s", err)
	}

	return nil
}

func (c Client) Plan() error {
	err := c.runInit()
	if err != nil {
		return err
	}

	planArgs := []string{
		"plan",
		"-input=false", // do not prompt for inputs
		fmt.Sprintf("-out=%s", c.Model.PlanFileLocalPath),
		fmt.Sprintf("-state=%s", c.Model.StateFileLocalPath),
	}
	planArgs = append(planArgs, c.varFlags()...)
	planArgs = append(planArgs, c.Model.Source)

	planCmd := c.terraformCmd(planArgs)
	planCmd.Stdout = c.LogWriter
	planCmd.Stderr = c.LogWriter
	err = planCmd.Run()
	if err != nil {
		return fmt.Errorf("Failed to run Terraform command: %s", err)
	}

	return nil
}

func (c Client) Output() (map[string]map[string]interface{}, error) {
	outputArgs := []string{
		"output",
		"-json",
		fmt.Sprintf("-state=%s", c.Model.StateFileLocalPath),
	}

	if c.Model.OutputModule != "" {
		outputArgs = append(outputArgs, fmt.Sprintf("-module=%s", c.Model.OutputModule))
	}

	outputCmd := c.terraformCmd(outputArgs)

	rawOutput, err := outputCmd.CombinedOutput()
	if err != nil {
		// TF CLI currently doesn't provide a nice way to detect an empty set of outputs
		// https://github.com/hashicorp/terraform/issues/11696
		if strings.Contains(string(rawOutput), "no outputs defined") {
			rawOutput = []byte("{}")
		} else {
			return nil, fmt.Errorf("Failed to retrieve output.\nError: %s\nOutput: %s", err, rawOutput)
		}
	}

	tfOutput := map[string]map[string]interface{}{}
	if err = json.Unmarshal(rawOutput, &tfOutput); err != nil {
		return nil, fmt.Errorf("Failed to unmarshal JSON output.\nError: %s\nOutput: %s", err, rawOutput)
	}

	return tfOutput, nil
}

func (c Client) Version() (string, error) {
	outputCmd := c.terraformCmd([]string{
		"-v",
	})
	output, err := outputCmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("Failed to retrieve version.\nError: %s\nOutput: %s", err, output)
	}

	return strings.TrimSpace(string(output)), nil
}

func (c Client) Import() error {
	if len(c.Model.Imports) == 0 {
		return nil
	}

	err := c.runInit()
	if err != nil {
		return err
	}

	for tfID, iaasID := range c.Model.Imports {
		exists, err := c.resourceExists(tfID)
		if err != nil {
			return fmt.Errorf("Failed to check for existence of resource %s %s.\nError: %s", tfID, iaasID, err)
		}
		if exists {
			c.LogWriter.Write([]byte(fmt.Sprintf("Skipping import of `%s: %s` as it already exists in the statefile...\n", tfID, iaasID)))
			continue
		}

		c.LogWriter.Write([]byte(fmt.Sprintf("Importing `%s: %s`...\n", tfID, iaasID)))
		importArgs := []string{
			"import",
			fmt.Sprintf("-state=%s", c.Model.StateFileLocalPath),
			fmt.Sprintf("-config=%s", c.Model.Source),
		}
		importArgs = append(importArgs, c.varFlags()...)
		importArgs = append(importArgs, tfID)
		importArgs = append(importArgs, iaasID)

		importCmd := c.terraformCmd(importArgs)
		rawOutput, err := importCmd.CombinedOutput()
		if err != nil {
			return fmt.Errorf("Failed to import resource %s %s.\nError: %s\nOutput: %s", tfID, iaasID, err, rawOutput)
		}
	}

	return nil
}

func (c Client) resourceExists(tfID string) (bool, error) {
	if _, err := os.Stat(c.Model.StateFileLocalPath); os.IsNotExist(err) {
		return false, nil
	}

	cmd := c.terraformCmd([]string{
		"state",
		"list",
		fmt.Sprintf("-state=%s", c.Model.StateFileLocalPath),
		tfID,
	})
	rawOutput, err := cmd.CombinedOutput()
	if err != nil {
		return false, fmt.Errorf("Error: %s, Output: %s", err, rawOutput)
	}

	// command returns the ID of the resource if it exists
	return (len(strings.TrimSpace(string(rawOutput))) > 0), nil
}

func (c Client) terraformCmd(args []string) *exec.Cmd {
	cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("terraform %s", strings.Join(args, " ")))

	cmd.Env = []string{
		fmt.Sprintf("PATH=%s", os.Getenv("PATH")),
		"CHECKPOINT_DISABLE=1",
	}
	for key, value := range c.Model.Env {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, value))
	}

	return cmd
}

func (c Client) runInit() error {
	initCmd := c.terraformCmd([]string{
		"init",
		"-input=false",
		"-get=true",
		"-backend=false", // resource doesn't support built-in backends yet
		c.Model.Source,
	})
	if output, err := initCmd.CombinedOutput(); err != nil {
		return fmt.Errorf("terraform init command failed.\nError: %s\nOutput: %s", err, output)
	}

	return nil
}

func (c Client) varFlags() []string {
	args := []string{}
	for key, val := range c.Model.Vars {
		args = append(args, "-var", fmt.Sprintf("'%s=%s'", key, formatVar(val)))
	}
	return args
}

func formatVar(value interface{}) string {
	valType := reflect.TypeOf(value)
	if valType == nil {
		return "null"
	}

	switch valType.Kind() {
	case reflect.Slice:
		valSlice, _ := value.([]interface{})
		sliceVars := []string{}
		for _, v := range valSlice {
			sliceVars = append(sliceVars, formatVar(v))
		}
		return fmt.Sprintf("[%s]", strings.Join(sliceVars, ","))
	case reflect.Map:
		valMap, _ := value.(map[string]interface{})
		mapVars := []string{}
		for k, v := range valMap {
			mapVars = append(mapVars, fmt.Sprintf("%s=%s", k, formatVar(v)))
		}
		return fmt.Sprintf("{%s}", strings.Join(mapVars, ","))
	}
	return fmt.Sprintf("%#v", value)
}
<|endoftext|>


<commit_before>package tent

import (
	"bytes"
	"errors"
	"io"
	"mime"

	"code.google.com/p/go.net/html"
	"github.com/tent/http-link-go"
)

var ErrNotTentEntity = errors.New("tent: not a valid Tent entity")

func Discover(entity string) (*MetaPost, error) {
	req, err := NewRequest("HEAD", entity, nil, nil)
	if req.URL.Path == "" {
		req.URL.Path = "/"
	}
	res, err := HTTP.Do(req)
	if err != nil {
		return nil, newRequestError(err, req)
	}
	res.Body.Close()
	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return nil, newResponseError(ErrBadStatusCode, res)
	}

	if linkHeader := res.Header.Get("Link"); linkHeader != "" {
		links, err := link.Parse(linkHeader)
		if err != nil {
			return nil, err
		}
		var metaLinks []string
		for _, l := range links {
			if l.Rel == RelMetaPost {
				metaLinks = append(metaLinks, l.URI)
			}
		}
		if len(metaLinks) > 0 {
			return getMetaPost(metaLinks, res.Request.URL)
		}
	}

	// we didn't get anything with the HEAD request, so let's try to GET HTML links
	req, _ = NewRequest("GET", entity, nil, nil)
	req.Header.Set("Accept", "text/html")
	res, err = HTTP.Do(req)
	if err != nil {
		return nil, newRequestError(err, req)
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return nil, newResponseError(ErrBadStatusCode, res)
	}
	contentType := res.Header.Get("Content-Type")
	if contentType == "" {
		return nil, ErrNotTentEntity
	}
	mediaType, _, err := mime.ParseMediaType(contentType)
	if err != nil {
		return nil, err
	}
	if mediaType != "text/html" {
		return nil, ErrNotTentEntity
	}

	var links []string
	if ok := timeoutRead(res.Body, func() {
		links, err = parseHTMLMetaLinks(res.Body)
	}); !ok {
		return nil, newResponseError(ErrReadTimeout, res)
	}
	if err != nil {
		return nil, err
	}
	if len(links) > 0 {
		return getMetaPost(links, res.Request.URL)
	}

	return nil, ErrNotTentEntity
}

func parseHTMLMetaLinks(data io.Reader) (links []string, err error) {
	t := html.NewTokenizer(data)
loop:
	for {
		switch t.Next() {
		case html.ErrorToken:
			err = t.Err()
			if err == io.EOF {
				err = nil
			}
			break loop
		case html.StartTagToken, html.SelfClosingTagToken:
			name, attrs := t.TagName()
			if !attrs || !bytes.Equal(name, []byte("link")) {
				continue loop
			}
			var href string
			var haveRel, metaRel bool
			for {
				key, val, more := t.TagAttr()
				if bytes.Equal(key, []byte("href")) {
					href = string(val)
				} else if bytes.Equal(key, []byte("rel")) {
					haveRel = true
					if bytes.Equal(val, []byte(RelMetaPost)) {
						metaRel = true
					}
				}
				if !more || haveRel && !metaRel || metaRel && href != "" {
					break
				}
			}
			if metaRel && href != "" {
				links = append(links, href)
			}
		}
	}
	return
}
<commit_msg>Add ErrInvalidLink<commit_after>package tent

import (
	"bytes"
	"errors"
	"io"
	"mime"

	"code.google.com/p/go.net/html"
	"github.com/tent/http-link-go"
)

var ErrNotTentEntity = errors.New("tent: not a valid Tent entity")
var ErrInvalidLink = errors.New("tent: invalid meta Link")

func Discover(entity string) (*MetaPost, error) {
	req, err := NewRequest("HEAD", entity, nil, nil)
	if req.URL.Path == "" {
		req.URL.Path = "/"
	}
	res, err := HTTP.Do(req)
	if err != nil {
		return nil, newRequestError(err, req)
	}
	res.Body.Close()
	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return nil, newResponseError(ErrBadStatusCode, res)
	}

	if linkHeader := res.Header.Get("Link"); linkHeader != "" {
		links, err := link.Parse(linkHeader)
		if err != nil {
			return nil, ErrInvalidLink
		}
		var metaLinks []string
		for _, l := range links {
			if l.Rel == RelMetaPost {
				metaLinks = append(metaLinks, l.URI)
			}
		}
		if len(metaLinks) > 0 {
			return getMetaPost(metaLinks, res.Request.URL)
		}
	}

	// we didn't get anything with the HEAD request, so let's try to GET HTML links
	req, _ = NewRequest("GET", entity, nil, nil)
	req.Header.Set("Accept", "text/html")
	res, err = HTTP.Do(req)
	if err != nil {
		return nil, newRequestError(err, req)
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return nil, newResponseError(ErrBadStatusCode, res)
	}
	contentType := res.Header.Get("Content-Type")
	if contentType == "" {
		return nil, ErrNotTentEntity
	}
	mediaType, _, err := mime.ParseMediaType(contentType)
	if err != nil {
		return nil, err
	}
	if mediaType != "text/html" {
		return nil, ErrNotTentEntity
	}

	var links []string
	if ok := timeoutRead(res.Body, func() {
		links, err = parseHTMLMetaLinks(res.Body)
	}); !ok {
		return nil, newResponseError(ErrReadTimeout, res)
	}
	if err != nil {
		return nil, err
	}
	if len(links) > 0 {
		return getMetaPost(links, res.Request.URL)
	}

	return nil, ErrNotTentEntity
}

func parseHTMLMetaLinks(data io.Reader) (links []string, err error) {
	t := html.NewTokenizer(data)
loop:
	for {
		switch t.Next() {
		case html.ErrorToken:
			err = t.Err()
			if err == io.EOF {
				err = nil
			}
			break loop
		case html.StartTagToken, html.SelfClosingTagToken:
			name, attrs := t.TagName()
			if !attrs || !bytes.Equal(name, []byte("link")) {
				continue loop
			}
			var href string
			var haveRel, metaRel bool
			for {
				key, val, more := t.TagAttr()
				if bytes.Equal(key, []byte("href")) {
					href = string(val)
				} else if bytes.Equal(key, []byte("rel")) {
					haveRel = true
					if bytes.Equal(val, []byte(RelMetaPost)) {
						metaRel = true
					}
				}
				if !more || haveRel && !metaRel || metaRel && href != "" {
					break
				}
			}
			if metaRel && href != "" {
				links = append(links, href)
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>// @author Robin Verlangen
// Discovery service used to detect cluster

package main

// Imports
import (
	"log"
)

// Node (entity in the Dispenso cluster)
type Node struct {
	Host string // Fully qualified hostname
	Port int // Port on which Dispenso runs
}

// Message (payload transmitted between nodes containing instructions)
type Message struct {
	Type MessageType // Type of message
	Payload string // JSON payload
}

// Message types, enum-like datastructure, use "MessageType" as wrapper
type MessageType struct {
	code messageType
}
type messageType int
const (
	discoveryPing	messageType = iota+1 // Initial discovery ping
	disocveryResponse // Discovery response
	discoveryMeta // Metadata beyond initial discovery
	configuration // Used to update configuration in the cluster
	taskRequest // New task submission
	taskApproval // Approve task
	taskReject // Reject task
	taskExecution // After being approved a task execution will be sent to the nodes
)

// Discovery service
type DiscoveryService struct {
	Nodes []Node // List of nodes
}

// Create discovery service
func NewDiscoveryService() *DiscoveryService {
	return &DiscoveryService{}
}

// Set seeds
func (*DiscoveryService) SetSeeds(seeds []string) error {
	for _,seed := range seeds {
		log.Println(seed)
	}
	return nil
}

// Run discovery service
func (*DiscoveryService) Start() {
	go func() {
		log.Println("Starting discovery")
		// @todo Implement
		shutdown <- true
	}()
}<commit_msg>Default port<commit_after>// @author Robin Verlangen
// Discovery service used to detect cluster

package main

// Imports
import (
	"log"
	"time"
	"net"
	"fmt"
	"strings"
	"strconv"
)

// Node (entity in the Dispenso cluster)
type Node struct {
	Host string // Fully qualified hostname
	Port int // Port on which Dispenso runs
}

// Ping a node
const PING_TIMEOUT = 30 * time.Second
func (n *Node) Ping() bool {
	conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", n.Host, n.Port), PING_TIMEOUT)
	if err != nil {
		return false
	}
	conn.Close()
	return true
}

// Message (payload transmitted between nodes containing instructions)
type Message struct {
	Type MessageType // Type of message
	Payload string // JSON payload
}

// Message types, enum-like datastructure, use "MessageType" as wrapper
type MessageType struct {
	code messageType
}
type messageType int
const (
	discoveryPing	messageType = iota+1 // Initial discovery ping
	disocveryResponse // Discovery response
	discoveryMeta // Metadata beyond initial discovery
	configuration // Used to update configuration in the cluster
	taskRequest // New task submission
	taskApproval // Approve task
	taskReject // Reject task
	taskExecution // After being approved a task execution will be sent to the nodes
)

// Discovery service
type DiscoveryService struct {
	Nodes []*Node // List of nodes
}

// Create discovery service
func NewDiscoveryService() *DiscoveryService {
	return &DiscoveryService{}
}

// Set seeds
func (d *DiscoveryService) SetSeeds(seeds []string) error {
	for _,seed := range seeds {
		// Simple seed validation
		split := strings.Split(seed, ":")
		if len(split) > 2 {
			log.Println(fmt.Sprintf("ERR: Seed %s host:port format invalid", seed))
			continue
		} else if len(split) == 1 {
			// Default port
			split[1] = fmt.Sprintf("%s" ,defaultPort)
		}
		port, err := strconv.Atoi(split[1])
		if err != nil {
			log.Println(fmt.Sprintf("ERR: Seed %s port format invalid", seed))
			continue
		}

		// Add node
		n := &Node{
			Host: split[0],
			Port: port,
		}
		d.Nodes = append(d.Nodes, n)
	}
	return nil
}

// Run discovery service
func (d *DiscoveryService) Start() {
	go func() {
		log.Println("Starting discovery")

		// Iterate nodes
		for _,node := range d.Nodes {
			node.Ping()
		}

		// @todo Run every once in a while, and remove shutdown

		shutdown <- true
	}()
}<|endoftext|>


<commit_before>package client

import (
	"github.com/cloudfoundry-community/go-cfclient/v3/resource"
	"github.com/cloudfoundry-community/go-cfclient/v3/testutil"
	"net/http"
	"testing"
)

func TestServiceCredentialBindings(t *testing.T) {
	g := testutil.NewObjectJSONGenerator(1)
	scb := g.ServiceCredentialBinding().JSON
	scb2 := g.ServiceCredentialBinding().JSON
	scb3 := g.ServiceCredentialBinding().JSON
	scb4 := g.ServiceCredentialBinding().JSON
	scbd := g.ServiceCredentialBindingDetails().JSON
	app := g.Application().JSON
	app2 := g.Application().JSON
	app3 := g.Application().JSON
	app4 := g.Application().JSON
	si := g.ServiceInstance().JSON

	tests := []RouteTest{
		{
			Description: "Create service credential binding",
			Route: testutil.MockRoute{
				Method:   "POST",
				Endpoint: "/v3/service_credential_bindings",
				Output:   g.Single(scb),
				Status:   http.StatusCreated,
				PostForm: `{
					"type": "app",
					"name": "some-binding-name",
					"relationships": {
					  "service_instance": {
						"data": {
						  "guid": "7304bc3c-7010-11ea-8840-48bf6bec2d78"
						}
					  },
					  "app": {
						"data": {
						  "guid": "e0e4417c-74ee-11ea-a604-48bf6bec2d78"
						}
					  }
					},
					"parameters": {
					  "key1": "value1",
					  "key2": "value2"
					}
				  }`,
			},
			Expected: scb,
			Action: func(c *Client, t *testing.T) (any, error) {
				r := resource.NewServiceCredentialBindingCreateApp(
					"7304bc3c-7010-11ea-8840-48bf6bec2d78", "e0e4417c-74ee-11ea-a604-48bf6bec2d78").
					WithName("some-binding-name").
					WithJSONParameters(`{
					  "key1": "value1",
					  "key2": "value2"
					}`)
				return c.ServiceCredentialBindings.Create(r)
			},
		},
		{
			Description: "Delete service credential binding",
			Route: testutil.MockRoute{
				Method:   "DELETE",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d",
				Status:   http.StatusAccepted,
			},
			Action: func(c *Client, t *testing.T) (any, error) {
				return nil, c.ServiceCredentialBindings.Delete("59ba6d78-6a21-4321-83a9-f7eacd88b08d")
			},
		},
		{
			Description: "Get service credential binding",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d",
				Output:   g.Single(scb),
				Status:   http.StatusOK},
			Expected: scb,
			Action: func(c *Client, t *testing.T) (any, error) {
				return c.ServiceCredentialBindings.Get("59ba6d78-6a21-4321-83a9-f7eacd88b08d")
			},
		},
		{
			Description: "Get service credential binding detail",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d/details",
				Output:   g.Single(scbd),
				Status:   http.StatusOK},
			Expected: scbd,
			Action: func(c *Client, t *testing.T) (any, error) {
				return c.ServiceCredentialBindings.GetDetails("59ba6d78-6a21-4321-83a9-f7eacd88b08d")
			},
		},
		{
			Description: "Get service credential binding parameters",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d/parameters",
				Output:   g.Single(`{ "foo": "bar", "foz": "baz" }`),
				Status:   http.StatusOK},
			Expected: `{ "foo": "bar", "foz": "baz" }`,
			Action: func(c *Client, t *testing.T) (any, error) {
				return c.ServiceCredentialBindings.GetParameters("59ba6d78-6a21-4321-83a9-f7eacd88b08d")
			},
		},
		{
			Description: "Get service credential binding and app",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d",
				Output: g.ResourceWithInclude(testutil.ResourceResult{
					Resource: scb,
					Apps:     []string{app},
				}),
				Status: http.StatusOK,
			},
			Expected:  scb,
			Expected2: app,
			Action2: func(c *Client, t *testing.T) (any, any, error) {
				return c.ServiceCredentialBindings.GetIncludeApp("59ba6d78-6a21-4321-83a9-f7eacd88b08d")
			},
		},
		{
			Description: "Get service credential binding and service instance",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d",
				Output: g.ResourceWithInclude(testutil.ResourceResult{
					Resource:         scb,
					ServiceInstances: []string{si},
				}),
				Status: http.StatusOK,
			},
			Expected:  scb,
			Expected2: si,
			Action2: func(c *Client, t *testing.T) (any, any, error) {
				return c.ServiceCredentialBindings.GetIncludeServiceInstance("59ba6d78-6a21-4321-83a9-f7eacd88b08d")
			},
		},
		{
			Description: "List all service credential bindings",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings",
				Output:   g.Paged([]string{scb}, []string{scb2}),
				Status:   http.StatusOK},
			Expected: g.Array(scb, scb2),
			Action: func(c *Client, t *testing.T) (any, error) {
				return c.ServiceCredentialBindings.ListAll(nil)
			},
		},
		{
			Description: "List all service credential bindings and include apps",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings",
				Output: g.PagedWithInclude(
					testutil.PagedResult{
						Resources: []string{scb, scb2},
						Apps:      []string{app, app2},
					},
					testutil.PagedResult{
						Resources: []string{scb3, scb4},
						Apps:      []string{app3, app4},
					}),
				Status: http.StatusOK,
			},
			Expected:  g.Array(scb, scb2, scb3, scb4),
			Expected2: g.Array(app, app2, app3, app4),
			Action2: func(c *Client, t *testing.T) (any, any, error) {
				return c.ServiceCredentialBindings.ListIncludeAppsAll(nil)
			},
		},
		{
			Description: "List all service credential bindings and include service instances",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings",
				Output: g.PagedWithInclude(
					testutil.PagedResult{
						Resources:        []string{scb, scb2},
						ServiceInstances: []string{si},
					},
					testutil.PagedResult{
						Resources: []string{scb3, scb4},
					}),
				Status: http.StatusOK,
			},
			Expected:  g.Array(scb, scb2, scb3, scb4),
			Expected2: g.Array(si),
			Action2: func(c *Client, t *testing.T) (any, any, error) {
				return c.ServiceCredentialBindings.ListIncludeServiceInstancesAll(nil)
			},
		},
		{
			Description: "Update service credential binding",
			Route: testutil.MockRoute{
				Method:   "PATCH",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d",
				Output:   g.Single(scb),
				Status:   http.StatusOK,
				PostForm: `{ "metadata": { "labels": {"foo": "bar"}, "annotations": {"baz": "qux"} }}`,
			},
			Expected: scb,
			Action: func(c *Client, t *testing.T) (any, error) {
				r := &resource.ServiceCredentialBindingUpdate{
					Metadata: resource.Metadata{
						Labels: map[string]string{
							"foo": "bar",
						},
						Annotations: map[string]string{
							"baz": "qux",
						},
					},
				}
				return c.ServiceCredentialBindings.Update("59ba6d78-6a21-4321-83a9-f7eacd88b08d", r)
			},
		},
	}
	ExecuteTests(tests, t)
}
<commit_msg>Add fix for Create app service credential binding test<commit_after>package client

import (
	"github.com/cloudfoundry-community/go-cfclient/v3/resource"
	"github.com/cloudfoundry-community/go-cfclient/v3/testutil"
	"net/http"
	"testing"
)

func TestServiceCredentialBindings(t *testing.T) {
	g := testutil.NewObjectJSONGenerator(1)
	scb := g.ServiceCredentialBinding().JSON
	scb2 := g.ServiceCredentialBinding().JSON
	scb3 := g.ServiceCredentialBinding().JSON
	scb4 := g.ServiceCredentialBinding().JSON
	scbd := g.ServiceCredentialBindingDetails().JSON
	app := g.Application().JSON
	app2 := g.Application().JSON
	app3 := g.Application().JSON
	app4 := g.Application().JSON
	si := g.ServiceInstance().JSON

	tests := []RouteTest{
		{
			Description: "Create app service credential binding for user provided service",
			Route: testutil.MockRoute{
				Method:   "POST",
				Endpoint: "/v3/service_credential_bindings",
				Output:   g.Single(scb),
				Status:   http.StatusCreated,
				PostForm: `{
					"type": "app",
					"name": "some-binding-name",
					"relationships": {
					  "service_instance": {
						"data": {
						  "guid": "7304bc3c-7010-11ea-8840-48bf6bec2d78"
						}
					  },
					  "app": {
						"data": {
						  "guid": "e0e4417c-74ee-11ea-a604-48bf6bec2d78"
						}
					  }
					},
					"parameters": {
					  "key1": "value1",
					  "key2": "value2"
					}
				  }`,
			},
			Expected: scb,
			Action: func(c *Client, t *testing.T) (any, error) {
				r := resource.NewServiceCredentialBindingCreateApp(
					"7304bc3c-7010-11ea-8840-48bf6bec2d78", "e0e4417c-74ee-11ea-a604-48bf6bec2d78").
					WithName("some-binding-name").
					WithJSONParameters(`{
					  "key1": "value1",
					  "key2": "value2"
					}`)
				_, binding, err := c.ServiceCredentialBindings.Create(r)
				return binding, err
			},
		},
		{
			Description: "Create app service credential binding for managed service instance",
			Route: testutil.MockRoute{
				Method:           "POST",
				Endpoint:         "/v3/service_credential_bindings",
				Output:           g.Single(scb),
				Status:           http.StatusCreated,
				RedirectLocation: "https://api.example.org/v3/jobs/af5c57f6-8769-41fa-a499-2c84ed896788",
				PostForm: `{
					"type": "app",
					"name": "some-binding-name",
					"relationships": {
					  "service_instance": {
						"data": {
						  "guid": "7304bc3c-7010-11ea-8840-48bf6bec2d78"
						}
					  },
					  "app": {
						"data": {
						  "guid": "e0e4417c-74ee-11ea-a604-48bf6bec2d78"
						}
					  }
					},
					"parameters": {
					  "key1": "value1",
					  "key2": "value2"
					}
				  }`,
			},
			Expected: "af5c57f6-8769-41fa-a499-2c84ed896788",
			Action2: func(c *Client, t *testing.T) (any, any, error) {
				r := resource.NewServiceCredentialBindingCreateApp(
					"7304bc3c-7010-11ea-8840-48bf6bec2d78", "e0e4417c-74ee-11ea-a604-48bf6bec2d78").
					WithName("some-binding-name").
					WithJSONParameters(`{
					  "key1": "value1",
					  "key2": "value2"
					}`)
				return c.ServiceCredentialBindings.Create(r)
			},
		},
		{
			Description: "Delete service credential binding",
			Route: testutil.MockRoute{
				Method:   "DELETE",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d",
				Status:   http.StatusAccepted,
			},
			Action: func(c *Client, t *testing.T) (any, error) {
				return nil, c.ServiceCredentialBindings.Delete("59ba6d78-6a21-4321-83a9-f7eacd88b08d")
			},
		},
		{
			Description: "Get service credential binding",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d",
				Output:   g.Single(scb),
				Status:   http.StatusOK},
			Expected: scb,
			Action: func(c *Client, t *testing.T) (any, error) {
				return c.ServiceCredentialBindings.Get("59ba6d78-6a21-4321-83a9-f7eacd88b08d")
			},
		},
		{
			Description: "Get service credential binding detail",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d/details",
				Output:   g.Single(scbd),
				Status:   http.StatusOK},
			Expected: scbd,
			Action: func(c *Client, t *testing.T) (any, error) {
				return c.ServiceCredentialBindings.GetDetails("59ba6d78-6a21-4321-83a9-f7eacd88b08d")
			},
		},
		{
			Description: "Get service credential binding parameters",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d/parameters",
				Output:   g.Single(`{ "foo": "bar", "foz": "baz" }`),
				Status:   http.StatusOK},
			Expected: `{ "foo": "bar", "foz": "baz" }`,
			Action: func(c *Client, t *testing.T) (any, error) {
				return c.ServiceCredentialBindings.GetParameters("59ba6d78-6a21-4321-83a9-f7eacd88b08d")
			},
		},
		{
			Description: "Get service credential binding and app",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d",
				Output: g.ResourceWithInclude(testutil.ResourceResult{
					Resource: scb,
					Apps:     []string{app},
				}),
				Status: http.StatusOK,
			},
			Expected:  scb,
			Expected2: app,
			Action2: func(c *Client, t *testing.T) (any, any, error) {
				return c.ServiceCredentialBindings.GetIncludeApp("59ba6d78-6a21-4321-83a9-f7eacd88b08d")
			},
		},
		{
			Description: "Get service credential binding and service instance",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d",
				Output: g.ResourceWithInclude(testutil.ResourceResult{
					Resource:         scb,
					ServiceInstances: []string{si},
				}),
				Status: http.StatusOK,
			},
			Expected:  scb,
			Expected2: si,
			Action2: func(c *Client, t *testing.T) (any, any, error) {
				return c.ServiceCredentialBindings.GetIncludeServiceInstance("59ba6d78-6a21-4321-83a9-f7eacd88b08d")
			},
		},
		{
			Description: "List all service credential bindings",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings",
				Output:   g.Paged([]string{scb}, []string{scb2}),
				Status:   http.StatusOK},
			Expected: g.Array(scb, scb2),
			Action: func(c *Client, t *testing.T) (any, error) {
				return c.ServiceCredentialBindings.ListAll(nil)
			},
		},
		{
			Description: "List all service credential bindings and include apps",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings",
				Output: g.PagedWithInclude(
					testutil.PagedResult{
						Resources: []string{scb, scb2},
						Apps:      []string{app, app2},
					},
					testutil.PagedResult{
						Resources: []string{scb3, scb4},
						Apps:      []string{app3, app4},
					}),
				Status: http.StatusOK,
			},
			Expected:  g.Array(scb, scb2, scb3, scb4),
			Expected2: g.Array(app, app2, app3, app4),
			Action2: func(c *Client, t *testing.T) (any, any, error) {
				return c.ServiceCredentialBindings.ListIncludeAppsAll(nil)
			},
		},
		{
			Description: "List all service credential bindings and include service instances",
			Route: testutil.MockRoute{
				Method:   "GET",
				Endpoint: "/v3/service_credential_bindings",
				Output: g.PagedWithInclude(
					testutil.PagedResult{
						Resources:        []string{scb, scb2},
						ServiceInstances: []string{si},
					},
					testutil.PagedResult{
						Resources: []string{scb3, scb4},
					}),
				Status: http.StatusOK,
			},
			Expected:  g.Array(scb, scb2, scb3, scb4),
			Expected2: g.Array(si),
			Action2: func(c *Client, t *testing.T) (any, any, error) {
				return c.ServiceCredentialBindings.ListIncludeServiceInstancesAll(nil)
			},
		},
		{
			Description: "Update service credential binding",
			Route: testutil.MockRoute{
				Method:   "PATCH",
				Endpoint: "/v3/service_credential_bindings/59ba6d78-6a21-4321-83a9-f7eacd88b08d",
				Output:   g.Single(scb),
				Status:   http.StatusOK,
				PostForm: `{ "metadata": { "labels": {"foo": "bar"}, "annotations": {"baz": "qux"} }}`,
			},
			Expected: scb,
			Action: func(c *Client, t *testing.T) (any, error) {
				r := &resource.ServiceCredentialBindingUpdate{
					Metadata: resource.Metadata{
						Labels: map[string]string{
							"foo": "bar",
						},
						Annotations: map[string]string{
							"baz": "qux",
						},
					},
				}
				return c.ServiceCredentialBindings.Update("59ba6d78-6a21-4321-83a9-f7eacd88b08d", r)
			},
		},
	}
	ExecuteTests(tests, t)
}
<|endoftext|>


<commit_before>package music

import (
	"fmt"

	"github.com/hajimehoshi/ebiten/audio"
	"github.com/hajimehoshi/ebiten/audio/mp3"
	"github.com/kemokemo/kuronan-dash/assets/audios"
)

func getMusicPlayer(dt DiscType) (*audio.Player, error) {
	b, err := getMusicByteData(dt)
	if err != nil {
		return nil, err
	}
	s, err := mp3.Decode(audioContext, audio.BytesReadSeekCloser(b))
	if err != nil {
		return nil, err
	}
	return audio.NewPlayer(audioContext, s)
}

func getMusicByteData(dt DiscType) ([]byte, error) {
	switch dt {
	case Title:
		return audios.Hashire_kurona_mp3, nil
	case Stage01:
		return audios.Shibugaki_no_kuroneko_mp3, nil
	default:
		return nil, fmt.Errorf("unknown disc type %v", dt)
	}
}
<commit_msg>fix: fix the byte data link.<commit_after>package music

import (
	"fmt"

	"github.com/hajimehoshi/ebiten/audio"
	"github.com/hajimehoshi/ebiten/audio/mp3"
	"github.com/kemokemo/kuronan-dash/assets/audios"
)

func getMusicPlayer(dt DiscType) (*audio.Player, error) {
	b, err := getMusicByteData(dt)
	if err != nil {
		return nil, err
	}
	s, err := mp3.Decode(audioContext, audio.BytesReadSeekCloser(b))
	if err != nil {
		return nil, err
	}
	return audio.NewPlayer(audioContext, s)
}

func getMusicByteData(dt DiscType) ([]byte, error) {
	switch dt {
	case Title:
		return audios.Shibugaki_no_kuroneko_mp3, nil
	case Stage01:
		return audios.Hashire_kurona_mp3, nil
	default:
		return nil, fmt.Errorf("unknown disc type %v", dt)
	}
}
<|endoftext|>


<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 options

import (
	"fmt"
	"net"
	"os"
	"strconv"

	"github.com/golang/glog"
	"github.com/spf13/pflag"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	apiserveroptions "k8s.io/apiserver/pkg/server/options"
	utilfeature "k8s.io/apiserver/pkg/util/feature"
	"k8s.io/client-go/informers"
	clientset "k8s.io/client-go/kubernetes"
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
	restclient "k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/client-go/tools/record"
	controlleroptions "k8s.io/kubernetes/cmd/controller-manager/app/options"
	schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config"
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	"k8s.io/kubernetes/pkg/apis/componentconfig"
	componentconfigv1alpha1 "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1"
	"k8s.io/kubernetes/pkg/client/leaderelectionconfig"
	"k8s.io/kubernetes/pkg/scheduler/factory"
)

// Options has all the params needed to run a Scheduler
type Options struct {
	// The default values. These are overridden if ConfigFile is set or by values in InsecureServing.
	ComponentConfig componentconfig.KubeSchedulerConfiguration

	SecureServing           *apiserveroptions.SecureServingOptions
	CombinedInsecureServing *CombinedInsecureServingOptions
	Authentication          *apiserveroptions.DelegatingAuthenticationOptions
	Authorization           *apiserveroptions.DelegatingAuthorizationOptions
	Deprecated              *DeprecatedOptions

	// ConfigFile is the location of the scheduler server's configuration file.
	ConfigFile string

	// WriteConfigTo is the path where the default configuration will be written.
	WriteConfigTo string

	Master string
}

// NewOptions returns default scheduler app options.
func NewOptions() (*Options, error) {
	cfg, err := newDefaultComponentConfig()
	if err != nil {
		return nil, err
	}

	hhost, hport, err := splitHostIntPort(cfg.HealthzBindAddress)
	if err != nil {
		return nil, err
	}

	o := &Options{
		ComponentConfig: *cfg,
		SecureServing:   nil, // TODO: enable with apiserveroptions.NewSecureServingOptions()
		CombinedInsecureServing: &CombinedInsecureServingOptions{
			Healthz: &controlleroptions.InsecureServingOptions{
				BindNetwork: "tcp",
			},
			Metrics: &controlleroptions.InsecureServingOptions{
				BindNetwork: "tcp",
			},
			BindPort:    hport,
			BindAddress: hhost,
		},
		Authentication: nil, // TODO: enable with apiserveroptions.NewDelegatingAuthenticationOptions()
		Authorization:  nil, // TODO: enable with apiserveroptions.NewDelegatingAuthorizationOptions()
		Deprecated: &DeprecatedOptions{
			UseLegacyPolicyConfig:    false,
			PolicyConfigMapNamespace: metav1.NamespaceSystem,
		},
	}

	return o, nil
}

func splitHostIntPort(s string) (string, int, error) {
	host, port, err := net.SplitHostPort(s)
	if err != nil {
		return "", 0, err
	}
	portInt, err := strconv.Atoi(port)
	if err != nil {
		return "", 0, err
	}
	return host, portInt, err
}

func newDefaultComponentConfig() (*componentconfig.KubeSchedulerConfiguration, error) {
	cfgv1alpha1 := componentconfigv1alpha1.KubeSchedulerConfiguration{}
	configScheme.Default(&cfgv1alpha1)
	cfg := componentconfig.KubeSchedulerConfiguration{}
	if err := configScheme.Convert(&cfgv1alpha1, &cfg, nil); err != nil {
		return nil, err
	}
	return &cfg, nil
}

// AddFlags adds flags for the scheduler options.
func (o *Options) AddFlags(fs *pflag.FlagSet) {
	fs.StringVar(&o.ConfigFile, "config", o.ConfigFile, "The path to the configuration file. Flags override values in this file.")
	fs.StringVar(&o.WriteConfigTo, "write-config-to", o.WriteConfigTo, "If set, write the configuration values to this file and exit.")
	fs.StringVar(&o.Master, "master", o.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig)")

	o.SecureServing.AddFlags(fs)
	o.CombinedInsecureServing.AddFlags(fs)
	o.Authentication.AddFlags(fs)
	o.Authorization.AddFlags(fs)
	o.Deprecated.AddFlags(fs, &o.ComponentConfig)

	leaderelectionconfig.BindFlags(&o.ComponentConfig.LeaderElection.LeaderElectionConfiguration, fs)
	utilfeature.DefaultFeatureGate.AddFlag(fs)
}

// ApplyTo applies the scheduler options to the given scheduler app configuration.
func (o *Options) ApplyTo(c *schedulerappconfig.Config) error {
	if len(o.ConfigFile) == 0 && len(o.WriteConfigTo) == 0 {
		glog.Warning("WARNING: all flags other than --config, --write-config-to, and --cleanup are deprecated. Please begin using a config file ASAP.")
	}
	if len(o.ConfigFile) == 0 {
		c.ComponentConfig = o.ComponentConfig

		// only apply deprecated flags if no config file is loaded (this is the old behaviour).
		if err := o.Deprecated.ApplyTo(&c.ComponentConfig); err != nil {
			return err
		}
		if err := o.CombinedInsecureServing.ApplyTo(c, &c.ComponentConfig); err != nil {
			return err
		}
	} else {
		cfg, err := loadConfigFromFile(o.ConfigFile)
		if err != nil {
			return err
		}

		// use the loaded config file only, with the exception of --address and --port. This means that
		// none of the deprectated flags in o.Deprecated are taken into consideration. This is the old
		// behaviour of the flags we have to keep.
		c.ComponentConfig = *cfg

		if err := o.CombinedInsecureServing.ApplyToFromLoadedConfig(c, &c.ComponentConfig); err != nil {
			return err
		}
	}

	if err := o.SecureServing.ApplyTo(&c.SecureServing); err != nil {
		return err
	}
	if err := o.Authentication.ApplyTo(&c.Authentication, c.SecureServing, nil); err != nil {
		return err
	}
	return o.Authorization.ApplyTo(&c.Authorization)
}

// Validate validates all the required options.
func (o *Options) Validate() []error {
	var errs []error

	errs = append(errs, o.SecureServing.Validate()...)
	errs = append(errs, o.CombinedInsecureServing.Validate()...)
	errs = append(errs, o.Authentication.Validate()...)
	errs = append(errs, o.Authorization.Validate()...)
	errs = append(errs, o.Deprecated.Validate()...)

	return errs
}

// Config return a scheduler config object
func (o *Options) Config() (*schedulerappconfig.Config, error) {
	c := &schedulerappconfig.Config{}
	if err := o.ApplyTo(c); err != nil {
		return nil, err
	}

	// prepare kube clients.
	client, leaderElectionClient, eventClient, err := createClients(c.ComponentConfig.ClientConnection, o.Master)
	if err != nil {
		return nil, err
	}

	// Prepare event clients.
	eventBroadcaster := record.NewBroadcaster()
	recorder := eventBroadcaster.NewRecorder(legacyscheme.Scheme, corev1.EventSource{Component: c.ComponentConfig.SchedulerName})

	// Set up leader election if enabled.
	var leaderElectionConfig *leaderelection.LeaderElectionConfig
	if c.ComponentConfig.LeaderElection.LeaderElect {
		leaderElectionConfig, err = makeLeaderElectionConfig(c.ComponentConfig.LeaderElection, leaderElectionClient, recorder)
		if err != nil {
			return nil, err
		}
	}

	c.Client = client
	c.InformerFactory = informers.NewSharedInformerFactory(client, 0)
	c.PodInformer = factory.NewPodInformer(client, 0)
	c.EventClient = eventClient
	c.Recorder = recorder
	c.Broadcaster = eventBroadcaster
	c.LeaderElection = leaderElectionConfig

	return c, nil
}

// makeLeaderElectionConfig builds a leader election configuration. It will
// create a new resource lock associated with the configuration.
func makeLeaderElectionConfig(config componentconfig.KubeSchedulerLeaderElectionConfiguration, client clientset.Interface, recorder record.EventRecorder) (*leaderelection.LeaderElectionConfig, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, fmt.Errorf("unable to get hostname: %v", err)
	}
	// add a uniquifier so that two processes on the same host don't accidentally both become active
	id := hostname + "_" + string(uuid.NewUUID())

	rl, err := resourcelock.New(config.ResourceLock,
		config.LockObjectNamespace,
		config.LockObjectName,
		client.CoreV1(),
		resourcelock.ResourceLockConfig{
			Identity:      id,
			EventRecorder: recorder,
		})
	if err != nil {
		return nil, fmt.Errorf("couldn't create resource lock: %v", err)
	}

	return &leaderelection.LeaderElectionConfig{
		Lock:          rl,
		LeaseDuration: config.LeaseDuration.Duration,
		RenewDeadline: config.RenewDeadline.Duration,
		RetryPeriod:   config.RetryPeriod.Duration,
	}, nil
}

// createClients creates a kube client and an event client from the given config and masterOverride.
// TODO remove masterOverride when CLI flags are removed.
func createClients(config componentconfig.ClientConnectionConfiguration, masterOverride string) (clientset.Interface, clientset.Interface, v1core.EventsGetter, error) {
	if len(config.KubeConfigFile) == 0 && len(masterOverride) == 0 {
		glog.Warningf("Neither --kubeconfig nor --master was specified. Using default API client. This might not work.")
	}

	// This creates a client, first loading any specified kubeconfig
	// file, and then overriding the Master flag, if non-empty.
	kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		&clientcmd.ClientConfigLoadingRules{ExplicitPath: config.KubeConfigFile},
		&clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterOverride}}).ClientConfig()
	if err != nil {
		return nil, nil, nil, err
	}

	kubeConfig.AcceptContentTypes = config.AcceptContentTypes
	kubeConfig.ContentType = config.ContentType
	kubeConfig.QPS = config.QPS
	//TODO make config struct use int instead of int32?
	kubeConfig.Burst = int(config.Burst)

	client, err := clientset.NewForConfig(restclient.AddUserAgent(kubeConfig, "scheduler"))
	if err != nil {
		return nil, nil, nil, err
	}

	leaderElectionClient, err := clientset.NewForConfig(restclient.AddUserAgent(kubeConfig, "leader-election"))
	if err != nil {
		return nil, nil, nil, err
	}

	eventClient, err := clientset.NewForConfig(kubeConfig)
	if err != nil {
		return nil, nil, nil, err
	}

	return client, leaderElectionClient, eventClient.CoreV1(), nil
}
<commit_msg>Remove scheduler config deprecated warning as the new component config is still in alpha<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 options

import (
	"fmt"
	"net"
	"os"
	"strconv"

	"github.com/golang/glog"
	"github.com/spf13/pflag"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	apiserveroptions "k8s.io/apiserver/pkg/server/options"
	utilfeature "k8s.io/apiserver/pkg/util/feature"
	"k8s.io/client-go/informers"
	clientset "k8s.io/client-go/kubernetes"
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
	restclient "k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/client-go/tools/record"
	controlleroptions "k8s.io/kubernetes/cmd/controller-manager/app/options"
	schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config"
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	"k8s.io/kubernetes/pkg/apis/componentconfig"
	componentconfigv1alpha1 "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1"
	"k8s.io/kubernetes/pkg/client/leaderelectionconfig"
	"k8s.io/kubernetes/pkg/scheduler/factory"
)

// Options has all the params needed to run a Scheduler
type Options struct {
	// The default values. These are overridden if ConfigFile is set or by values in InsecureServing.
	ComponentConfig componentconfig.KubeSchedulerConfiguration

	SecureServing           *apiserveroptions.SecureServingOptions
	CombinedInsecureServing *CombinedInsecureServingOptions
	Authentication          *apiserveroptions.DelegatingAuthenticationOptions
	Authorization           *apiserveroptions.DelegatingAuthorizationOptions
	Deprecated              *DeprecatedOptions

	// ConfigFile is the location of the scheduler server's configuration file.
	ConfigFile string

	// WriteConfigTo is the path where the default configuration will be written.
	WriteConfigTo string

	Master string
}

// NewOptions returns default scheduler app options.
func NewOptions() (*Options, error) {
	cfg, err := newDefaultComponentConfig()
	if err != nil {
		return nil, err
	}

	hhost, hport, err := splitHostIntPort(cfg.HealthzBindAddress)
	if err != nil {
		return nil, err
	}

	o := &Options{
		ComponentConfig: *cfg,
		SecureServing:   nil, // TODO: enable with apiserveroptions.NewSecureServingOptions()
		CombinedInsecureServing: &CombinedInsecureServingOptions{
			Healthz: &controlleroptions.InsecureServingOptions{
				BindNetwork: "tcp",
			},
			Metrics: &controlleroptions.InsecureServingOptions{
				BindNetwork: "tcp",
			},
			BindPort:    hport,
			BindAddress: hhost,
		},
		Authentication: nil, // TODO: enable with apiserveroptions.NewDelegatingAuthenticationOptions()
		Authorization:  nil, // TODO: enable with apiserveroptions.NewDelegatingAuthorizationOptions()
		Deprecated: &DeprecatedOptions{
			UseLegacyPolicyConfig:    false,
			PolicyConfigMapNamespace: metav1.NamespaceSystem,
		},
	}

	return o, nil
}

func splitHostIntPort(s string) (string, int, error) {
	host, port, err := net.SplitHostPort(s)
	if err != nil {
		return "", 0, err
	}
	portInt, err := strconv.Atoi(port)
	if err != nil {
		return "", 0, err
	}
	return host, portInt, err
}

func newDefaultComponentConfig() (*componentconfig.KubeSchedulerConfiguration, error) {
	cfgv1alpha1 := componentconfigv1alpha1.KubeSchedulerConfiguration{}
	configScheme.Default(&cfgv1alpha1)
	cfg := componentconfig.KubeSchedulerConfiguration{}
	if err := configScheme.Convert(&cfgv1alpha1, &cfg, nil); err != nil {
		return nil, err
	}
	return &cfg, nil
}

// AddFlags adds flags for the scheduler options.
func (o *Options) AddFlags(fs *pflag.FlagSet) {
	fs.StringVar(&o.ConfigFile, "config", o.ConfigFile, "The path to the configuration file. Flags override values in this file.")
	fs.StringVar(&o.WriteConfigTo, "write-config-to", o.WriteConfigTo, "If set, write the configuration values to this file and exit.")
	fs.StringVar(&o.Master, "master", o.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig)")

	o.SecureServing.AddFlags(fs)
	o.CombinedInsecureServing.AddFlags(fs)
	o.Authentication.AddFlags(fs)
	o.Authorization.AddFlags(fs)
	o.Deprecated.AddFlags(fs, &o.ComponentConfig)

	leaderelectionconfig.BindFlags(&o.ComponentConfig.LeaderElection.LeaderElectionConfiguration, fs)
	utilfeature.DefaultFeatureGate.AddFlag(fs)
}

// ApplyTo applies the scheduler options to the given scheduler app configuration.
func (o *Options) ApplyTo(c *schedulerappconfig.Config) error {
	if len(o.ConfigFile) == 0 {
		c.ComponentConfig = o.ComponentConfig

		// only apply deprecated flags if no config file is loaded (this is the old behaviour).
		if err := o.Deprecated.ApplyTo(&c.ComponentConfig); err != nil {
			return err
		}
		if err := o.CombinedInsecureServing.ApplyTo(c, &c.ComponentConfig); err != nil {
			return err
		}
	} else {
		cfg, err := loadConfigFromFile(o.ConfigFile)
		if err != nil {
			return err
		}

		// use the loaded config file only, with the exception of --address and --port. This means that
		// none of the deprectated flags in o.Deprecated are taken into consideration. This is the old
		// behaviour of the flags we have to keep.
		c.ComponentConfig = *cfg

		if err := o.CombinedInsecureServing.ApplyToFromLoadedConfig(c, &c.ComponentConfig); err != nil {
			return err
		}
	}

	if err := o.SecureServing.ApplyTo(&c.SecureServing); err != nil {
		return err
	}
	if err := o.Authentication.ApplyTo(&c.Authentication, c.SecureServing, nil); err != nil {
		return err
	}
	return o.Authorization.ApplyTo(&c.Authorization)
}

// Validate validates all the required options.
func (o *Options) Validate() []error {
	var errs []error

	errs = append(errs, o.SecureServing.Validate()...)
	errs = append(errs, o.CombinedInsecureServing.Validate()...)
	errs = append(errs, o.Authentication.Validate()...)
	errs = append(errs, o.Authorization.Validate()...)
	errs = append(errs, o.Deprecated.Validate()...)

	return errs
}

// Config return a scheduler config object
func (o *Options) Config() (*schedulerappconfig.Config, error) {
	c := &schedulerappconfig.Config{}
	if err := o.ApplyTo(c); err != nil {
		return nil, err
	}

	// prepare kube clients.
	client, leaderElectionClient, eventClient, err := createClients(c.ComponentConfig.ClientConnection, o.Master)
	if err != nil {
		return nil, err
	}

	// Prepare event clients.
	eventBroadcaster := record.NewBroadcaster()
	recorder := eventBroadcaster.NewRecorder(legacyscheme.Scheme, corev1.EventSource{Component: c.ComponentConfig.SchedulerName})

	// Set up leader election if enabled.
	var leaderElectionConfig *leaderelection.LeaderElectionConfig
	if c.ComponentConfig.LeaderElection.LeaderElect {
		leaderElectionConfig, err = makeLeaderElectionConfig(c.ComponentConfig.LeaderElection, leaderElectionClient, recorder)
		if err != nil {
			return nil, err
		}
	}

	c.Client = client
	c.InformerFactory = informers.NewSharedInformerFactory(client, 0)
	c.PodInformer = factory.NewPodInformer(client, 0)
	c.EventClient = eventClient
	c.Recorder = recorder
	c.Broadcaster = eventBroadcaster
	c.LeaderElection = leaderElectionConfig

	return c, nil
}

// makeLeaderElectionConfig builds a leader election configuration. It will
// create a new resource lock associated with the configuration.
func makeLeaderElectionConfig(config componentconfig.KubeSchedulerLeaderElectionConfiguration, client clientset.Interface, recorder record.EventRecorder) (*leaderelection.LeaderElectionConfig, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, fmt.Errorf("unable to get hostname: %v", err)
	}
	// add a uniquifier so that two processes on the same host don't accidentally both become active
	id := hostname + "_" + string(uuid.NewUUID())

	rl, err := resourcelock.New(config.ResourceLock,
		config.LockObjectNamespace,
		config.LockObjectName,
		client.CoreV1(),
		resourcelock.ResourceLockConfig{
			Identity:      id,
			EventRecorder: recorder,
		})
	if err != nil {
		return nil, fmt.Errorf("couldn't create resource lock: %v", err)
	}

	return &leaderelection.LeaderElectionConfig{
		Lock:          rl,
		LeaseDuration: config.LeaseDuration.Duration,
		RenewDeadline: config.RenewDeadline.Duration,
		RetryPeriod:   config.RetryPeriod.Duration,
	}, nil
}

// createClients creates a kube client and an event client from the given config and masterOverride.
// TODO remove masterOverride when CLI flags are removed.
func createClients(config componentconfig.ClientConnectionConfiguration, masterOverride string) (clientset.Interface, clientset.Interface, v1core.EventsGetter, error) {
	if len(config.KubeConfigFile) == 0 && len(masterOverride) == 0 {
		glog.Warningf("Neither --kubeconfig nor --master was specified. Using default API client. This might not work.")
	}

	// This creates a client, first loading any specified kubeconfig
	// file, and then overriding the Master flag, if non-empty.
	kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		&clientcmd.ClientConfigLoadingRules{ExplicitPath: config.KubeConfigFile},
		&clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterOverride}}).ClientConfig()
	if err != nil {
		return nil, nil, nil, err
	}

	kubeConfig.AcceptContentTypes = config.AcceptContentTypes
	kubeConfig.ContentType = config.ContentType
	kubeConfig.QPS = config.QPS
	//TODO make config struct use int instead of int32?
	kubeConfig.Burst = int(config.Burst)

	client, err := clientset.NewForConfig(restclient.AddUserAgent(kubeConfig, "scheduler"))
	if err != nil {
		return nil, nil, nil, err
	}

	leaderElectionClient, err := clientset.NewForConfig(restclient.AddUserAgent(kubeConfig, "leader-election"))
	if err != nil {
		return nil, nil, nil, err
	}

	eventClient, err := clientset.NewForConfig(kubeConfig)
	if err != nil {
		return nil, nil, nil, err
	}

	return client, leaderElectionClient, eventClient.CoreV1(), nil
}
<|endoftext|>