<commit_before>// Copyright (C) 2017  Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"golang.org/x/tools/go/vcs"
)

// Implementation taken from "isStandardImportPath" in go's source.
func isStdLibPath(path string) bool {
	i := strings.Index(path, "/")
	if i < 0 {
		i = len(path)
	}
	elem := path[:i]
	return !strings.Contains(elem, ".")
}

// sortImports takes in an "import" body and returns it sorted
func sortImports(in []byte, sections []string) []byte {
	type importLine struct {
		index int    // index into inLines
		path  string // import path used for sorting
	}
	// imports holds all the import lines, separated by section. The
	// first section is for stdlib imports, the following sections
	// hold the user specified sections, the final section is for
	// everything else.
	imports := make([][]importLine, len(sections)+2)
	addImport := func(section, index int, importPath string) {
		imports[section] = append(imports[section], importLine{index, importPath})
	}
	stdlib := 0
	offset := 1
	other := len(imports) - 1

	inLines := bytes.Split(in, []byte{'\n'})
	for i, l := range inLines {
		if len(l) == 0 {
			continue
		}
		start := bytes.IndexByte(l, '"')
		if start == -1 {
			continue
		}
		if comment := bytes.Index(l, []byte("//")); comment > -1 && comment < start {
			continue
		}

		start++ // skip '"'
		end := bytes.IndexByte(l[start:], '"') + start
		s := string(l[start:end])

		found := false
		for j, sect := range sections {
			if strings.HasPrefix(s, sect) && (len(sect) == len(s) || s[len(sect)] == '/') {
				addImport(j+offset, i, s)
				found = true
				break
			}
		}

		if !found {
			if isStdLibPath(s) {
				addImport(stdlib, i, s)
			} else {
				addImport(other, i, s)
			}
		}
	}

	out := make([]byte, 0, len(in)+2)
	needSeperator := false
	for _, section := range imports {
		if len(section) == 0 {
			continue
		}
		if needSeperator {
			out = append(out, '\n')
		}
		sort.Slice(section, func(a, b int) bool {
			return section[a].path < section[b].path
		})
		for _, s := range section {
			out = append(out, inLines[s.index]...)
			out = append(out, '\n')
		}
		needSeperator = true
	}

	return out
}

func genFile(in []byte, sections []string) []byte {
	out := make([]byte, 0, len(in)+3) // Add some fudge to avoid re-allocation

	for {
		const importLine = "\nimport (\n"
		const importLineLen = len(importLine)
		importStart := bytes.Index(in, []byte(importLine))
		if importStart == -1 {
			break
		}
		// Save to `out` everything up to and including "import(\n"
		out = append(out, in[:importStart+importLineLen]...)
		in = in[importStart+importLineLen:]
		importLen := bytes.Index(in, []byte("\n)\n"))
		if importLen == -1 {
			panic("file missing close ')'")
		}
		// Sort body of "import" and write it to `out`
		out = append(out, sortImports(in[:importLen], sections)...)
		out = append(out, []byte(")")...)
		in = in[importLen+2:]
	}
	// Write everything leftover to out
	out = append(out, in...)
	return out
}

// returns true if the file changed
func processFile(filename string, writeFile, listDiffFiles bool, sections []string) bool {
	in, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	out := genFile(in, sections)

	equal := bytes.Equal(in, out)
	if listDiffFiles {
		return !equal
	}
	if !writeFile {
		os.Stdout.Write(out)
		return !equal
	}

	if equal {
		return false
	}
	temp, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename))
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(temp.Name())
	s, err := os.Stat(filename)
	if err != nil {
		panic(err)
	}
	if _, err = temp.Write(out); err != nil {
		panic(err)
	}
	if err := temp.Close(); err != nil {
		panic(err)
	}
	err = os.Chmod(temp.Name(), s.Mode())
	if err != nil {
		panic(err)
	}

	if err := os.Rename(temp.Name(), filename); err != nil {
		panic(err)
	}

	return true
}

// maps directory to vcsRoot
var vcsRootCache = make(map[string]string)

func vcsRootImportPath(f string) (string, error) {
	path, err := filepath.Abs(f)
	if err != nil {
		return "", err
	}
	dir := filepath.Dir(path)
	if root, ok := vcsRootCache[dir]; ok {
		return root, nil
	}
	gopath := build.Default.GOPATH
	var root string
	_, root, err = vcs.FromDir(dir, filepath.Join(gopath, "src"))
	if err != nil {
		return "", err
	}
	vcsRootCache[dir] = root
	return root, nil
}

func main() {
	writeFile := flag.Bool("w", false, "write result to file instead of stdout")
	listDiffFiles := flag.Bool("l", false, "list files whose formatting differs from importsort")
	var sections csv
	flag.Var(&sections, "s", "comma-seperated list of prefixes to define import sections,"+
		` ex: "cvshub.com/company". Default value is to use repository information.`)

	flag.Parse()

	checkVCSRoot := sections.vals == nil
	for _, f := range flag.Args() {
		if checkVCSRoot {
			root, err := vcsRootImportPath(f)
			if err != nil {
				fmt.Fprintf(os.Stderr, "error determining VCS root: %s", err)
			} else {
				sections.vals = []string{root}
			}
		}
		diff := processFile(f, *writeFile, *listDiffFiles, sections.vals)
		if *listDiffFiles && diff {
			fmt.Println(f)
		}
	}
}

// csv is a comma-separated flag.Value
type csv struct {
	vals []string
}

func (c *csv) String() string {
	return strings.Join(c.vals, ", ")
}
func (c *csv) Set(s string) error {
	c.vals = strings.Split(s, ",")
	return nil
}
<commit_msg>cmd/importsort: Replace csv flag with []string<commit_after>// Copyright (C) 2017  Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"golang.org/x/tools/go/vcs"
)

// Implementation taken from "isStandardImportPath" in go's source.
func isStdLibPath(path string) bool {
	i := strings.Index(path, "/")
	if i < 0 {
		i = len(path)
	}
	elem := path[:i]
	return !strings.Contains(elem, ".")
}

// sortImports takes in an "import" body and returns it sorted
func sortImports(in []byte, sections []string) []byte {
	type importLine struct {
		index int    // index into inLines
		path  string // import path used for sorting
	}
	// imports holds all the import lines, separated by section. The
	// first section is for stdlib imports, the following sections
	// hold the user specified sections, the final section is for
	// everything else.
	imports := make([][]importLine, len(sections)+2)
	addImport := func(section, index int, importPath string) {
		imports[section] = append(imports[section], importLine{index, importPath})
	}
	stdlib := 0
	offset := 1
	other := len(imports) - 1

	inLines := bytes.Split(in, []byte{'\n'})
	for i, l := range inLines {
		if len(l) == 0 {
			continue
		}
		start := bytes.IndexByte(l, '"')
		if start == -1 {
			continue
		}
		if comment := bytes.Index(l, []byte("//")); comment > -1 && comment < start {
			continue
		}

		start++ // skip '"'
		end := bytes.IndexByte(l[start:], '"') + start
		s := string(l[start:end])

		found := false
		for j, sect := range sections {
			if strings.HasPrefix(s, sect) && (len(sect) == len(s) || s[len(sect)] == '/') {
				addImport(j+offset, i, s)
				found = true
				break
			}
		}

		if !found {
			if isStdLibPath(s) {
				addImport(stdlib, i, s)
			} else {
				addImport(other, i, s)
			}
		}
	}

	out := make([]byte, 0, len(in)+2)
	needSeperator := false
	for _, section := range imports {
		if len(section) == 0 {
			continue
		}
		if needSeperator {
			out = append(out, '\n')
		}
		sort.Slice(section, func(a, b int) bool {
			return section[a].path < section[b].path
		})
		for _, s := range section {
			out = append(out, inLines[s.index]...)
			out = append(out, '\n')
		}
		needSeperator = true
	}

	return out
}

func genFile(in []byte, sections []string) []byte {
	out := make([]byte, 0, len(in)+3) // Add some fudge to avoid re-allocation

	for {
		const importLine = "\nimport (\n"
		const importLineLen = len(importLine)
		importStart := bytes.Index(in, []byte(importLine))
		if importStart == -1 {
			break
		}
		// Save to `out` everything up to and including "import(\n"
		out = append(out, in[:importStart+importLineLen]...)
		in = in[importStart+importLineLen:]
		importLen := bytes.Index(in, []byte("\n)\n"))
		if importLen == -1 {
			panic("file missing close ')'")
		}
		// Sort body of "import" and write it to `out`
		out = append(out, sortImports(in[:importLen], sections)...)
		out = append(out, []byte(")")...)
		in = in[importLen+2:]
	}
	// Write everything leftover to out
	out = append(out, in...)
	return out
}

// returns true if the file changed
func processFile(filename string, writeFile, listDiffFiles bool, sections []string) bool {
	in, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	out := genFile(in, sections)

	equal := bytes.Equal(in, out)
	if listDiffFiles {
		return !equal
	}
	if !writeFile {
		os.Stdout.Write(out)
		return !equal
	}

	if equal {
		return false
	}
	temp, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename))
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(temp.Name())
	s, err := os.Stat(filename)
	if err != nil {
		panic(err)
	}
	if _, err = temp.Write(out); err != nil {
		panic(err)
	}
	if err := temp.Close(); err != nil {
		panic(err)
	}
	err = os.Chmod(temp.Name(), s.Mode())
	if err != nil {
		panic(err)
	}

	if err := os.Rename(temp.Name(), filename); err != nil {
		panic(err)
	}

	return true
}

// maps directory to vcsRoot
var vcsRootCache = make(map[string]string)

func vcsRootImportPath(f string) (string, error) {
	path, err := filepath.Abs(f)
	if err != nil {
		return "", err
	}
	dir := filepath.Dir(path)
	if root, ok := vcsRootCache[dir]; ok {
		return root, nil
	}
	gopath := build.Default.GOPATH
	var root string
	_, root, err = vcs.FromDir(dir, filepath.Join(gopath, "src"))
	if err != nil {
		return "", err
	}
	vcsRootCache[dir] = root
	return root, nil
}

func main() {
	writeFile := flag.Bool("w", false, "write result to file instead of stdout")
	listDiffFiles := flag.Bool("l", false, "list files whose formatting differs from importsort")
	var sections multistring
	flag.Var(&sections, "s", "package `prefix` to define an import section,"+
		` ex: "cvshub.com/company". May be specified multiple times.`+
		" If not specified the repository root is used.")

	flag.Parse()

	checkVCSRoot := sections == nil
	for _, f := range flag.Args() {
		if checkVCSRoot {
			root, err := vcsRootImportPath(f)
			if err != nil {
				fmt.Fprintf(os.Stderr, "error determining VCS root: %s", err)
			} else {
				sections = multistring{root}
			}
		}
		diff := processFile(f, *writeFile, *listDiffFiles, sections)
		if *listDiffFiles && diff {
			fmt.Println(f)
		}
	}
}

type multistring []string

func (m *multistring) String() string {
	return strings.Join(*m, ", ")
}
func (m *multistring) Set(s string) error {
	*m = append(*m, s)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"regexp"
	"runtime/pprof"
	"strings"
	"syscall"

	"github.com/vektra/mockery/mockery"
)

const regexMetadataChars = "\\.+*?()|[]{}^$"

type Config struct {
	fName      string
	fPrint     bool
	fOutput    string
	fOutpkg    string
	fDir       string
	fRecursive bool
	fAll       bool
	fIP        bool
	fTO        bool
	fCase      string
	fNote      string
	fProfile   string
	fVersion   bool
	quiet      bool
}

func main() {
	config := parseConfigFromArgs(os.Args)

	var recursive bool
	var filter *regexp.Regexp
	var err error
	var limitOne bool

	if config.quiet {
		// if "quiet" flag is set, set os.Stdout to /dev/null to suppress all output to Stdout
		os.Stdout = os.NewFile(uintptr(syscall.Stdout), os.DevNull)
	}

	if config.fVersion {
		fmt.Println(mockery.SemVer)
		return
	} else if config.fName != "" && config.fAll {
		fmt.Fprintln(os.Stderr, "Specify -name or -all, but not both")
		os.Exit(1)
	} else if config.fName != "" {
		recursive = config.fRecursive
		if strings.ContainsAny(config.fName, regexMetadataChars) {
			if filter, err = regexp.Compile(config.fName); err != nil {
				fmt.Fprintln(os.Stderr, "Invalid regular expression provided to -name")
				os.Exit(1)
			}
		} else {
			filter = regexp.MustCompile(fmt.Sprintf("^%s$", config.fName))
			limitOne = true
		}
	} else if config.fAll {
		recursive = true
		filter = regexp.MustCompile(".*")
	} else {
		fmt.Fprintln(os.Stderr, "Use -name to specify the name of the interface or -all for all interfaces found")
		os.Exit(1)
	}

	if config.fProfile != "" {
		f, err := os.Create(config.fProfile)
		if err != nil {
			os.Exit(1)
			return
		}

		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	var osp mockery.OutputStreamProvider
	if config.fPrint {
		osp = &mockery.StdoutStreamProvider{}
	} else {
		osp = &mockery.FileOutputStreamProvider{
			BaseDir:   config.fOutput,
			InPackage: config.fIP,
			TestOnly:  config.fTO,
			Case:      config.fCase,
		}
	}

	visitor := &mockery.GeneratorVisitor{
		InPackage:   config.fIP,
		Note:        config.fNote,
		Osp:         osp,
		PackageName: config.fOutpkg,
	}

	walker := mockery.Walker{
		BaseDir:   config.fDir,
		Recursive: recursive,
		Filter:    filter,
		LimitOne:  limitOne,
	}

	generated := walker.Walk(visitor)

	if config.fName != "" && !generated {
		fmt.Printf("Unable to find %s in any go files under this path\n", config.fName)
		os.Exit(1)
	}
}

func parseConfigFromArgs(args []string) Config {
	config := Config{}

	flagSet := flag.NewFlagSet(args[0], flag.ExitOnError)

	flagSet.StringVar(&config.fName, "name", "", "name or matching regular expression of interface to generate mock for")
	flagSet.BoolVar(&config.fPrint, "print", false, "print the generated mock to stdout")
	flagSet.StringVar(&config.fOutput, "output", "./mocks", "directory to write mocks to")
	flagSet.StringVar(&config.fOutpkg, "outpkg", "mocks", "name of generated package")
	flagSet.StringVar(&config.fDir, "dir", ".", "directory to search for interfaces")
	flagSet.BoolVar(&config.fRecursive, "recursive", false, "recurse search into sub-directories")
	flagSet.BoolVar(&config.fAll, "all", false, "generates mocks for all found interfaces in all sub-directories")
	flagSet.BoolVar(&config.fIP, "inpkg", false, "generate a mock that goes inside the original package")
	flagSet.BoolVar(&config.fTO, "testonly", false, "generate a mock in a _test.go file")
	flagSet.StringVar(&config.fCase, "case", "camel", "name the mocked file using casing convention")
	flagSet.StringVar(&config.fNote, "note", "", "comment to insert into prologue of each generated file")
	flagSet.StringVar(&config.fProfile, "cpuprofile", "", "write cpu profile to file")
	flagSet.BoolVar(&config.fVersion, "version", false, "prints the installed version of mockery")
	flagSet.BoolVar(&config.quiet, "quiet", false, "suppress output to stdout")

	flagSet.Parse(args[1:])

	return config
}
<commit_msg>Mention possible values for -case argument<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"regexp"
	"runtime/pprof"
	"strings"
	"syscall"

	"github.com/vektra/mockery/mockery"
)

const regexMetadataChars = "\\.+*?()|[]{}^$"

type Config struct {
	fName      string
	fPrint     bool
	fOutput    string
	fOutpkg    string
	fDir       string
	fRecursive bool
	fAll       bool
	fIP        bool
	fTO        bool
	fCase      string
	fNote      string
	fProfile   string
	fVersion   bool
	quiet      bool
}

func main() {
	config := parseConfigFromArgs(os.Args)

	var recursive bool
	var filter *regexp.Regexp
	var err error
	var limitOne bool

	if config.quiet {
		// if "quiet" flag is set, set os.Stdout to /dev/null to suppress all output to Stdout
		os.Stdout = os.NewFile(uintptr(syscall.Stdout), os.DevNull)
	}

	if config.fVersion {
		fmt.Println(mockery.SemVer)
		return
	} else if config.fName != "" && config.fAll {
		fmt.Fprintln(os.Stderr, "Specify -name or -all, but not both")
		os.Exit(1)
	} else if config.fName != "" {
		recursive = config.fRecursive
		if strings.ContainsAny(config.fName, regexMetadataChars) {
			if filter, err = regexp.Compile(config.fName); err != nil {
				fmt.Fprintln(os.Stderr, "Invalid regular expression provided to -name")
				os.Exit(1)
			}
		} else {
			filter = regexp.MustCompile(fmt.Sprintf("^%s$", config.fName))
			limitOne = true
		}
	} else if config.fAll {
		recursive = true
		filter = regexp.MustCompile(".*")
	} else {
		fmt.Fprintln(os.Stderr, "Use -name to specify the name of the interface or -all for all interfaces found")
		os.Exit(1)
	}

	if config.fProfile != "" {
		f, err := os.Create(config.fProfile)
		if err != nil {
			os.Exit(1)
			return
		}

		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	var osp mockery.OutputStreamProvider
	if config.fPrint {
		osp = &mockery.StdoutStreamProvider{}
	} else {
		osp = &mockery.FileOutputStreamProvider{
			BaseDir:   config.fOutput,
			InPackage: config.fIP,
			TestOnly:  config.fTO,
			Case:      config.fCase,
		}
	}

	visitor := &mockery.GeneratorVisitor{
		InPackage:   config.fIP,
		Note:        config.fNote,
		Osp:         osp,
		PackageName: config.fOutpkg,
	}

	walker := mockery.Walker{
		BaseDir:   config.fDir,
		Recursive: recursive,
		Filter:    filter,
		LimitOne:  limitOne,
	}

	generated := walker.Walk(visitor)

	if config.fName != "" && !generated {
		fmt.Printf("Unable to find %s in any go files under this path\n", config.fName)
		os.Exit(1)
	}
}

func parseConfigFromArgs(args []string) Config {
	config := Config{}

	flagSet := flag.NewFlagSet(args[0], flag.ExitOnError)

	flagSet.StringVar(&config.fName, "name", "", "name or matching regular expression of interface to generate mock for")
	flagSet.BoolVar(&config.fPrint, "print", false, "print the generated mock to stdout")
	flagSet.StringVar(&config.fOutput, "output", "./mocks", "directory to write mocks to")
	flagSet.StringVar(&config.fOutpkg, "outpkg", "mocks", "name of generated package")
	flagSet.StringVar(&config.fDir, "dir", ".", "directory to search for interfaces")
	flagSet.BoolVar(&config.fRecursive, "recursive", false, "recurse search into sub-directories")
	flagSet.BoolVar(&config.fAll, "all", false, "generates mocks for all found interfaces in all sub-directories")
	flagSet.BoolVar(&config.fIP, "inpkg", false, "generate a mock that goes inside the original package")
	flagSet.BoolVar(&config.fTO, "testonly", false, "generate a mock in a _test.go file")
	flagSet.StringVar(&config.fCase, "case", "camel", "name the mocked file using casing convention [camel, snake, underscore]")
	flagSet.StringVar(&config.fNote, "note", "", "comment to insert into prologue of each generated file")
	flagSet.StringVar(&config.fProfile, "cpuprofile", "", "write cpu profile to file")
	flagSet.BoolVar(&config.fVersion, "version", false, "prints the installed version of mockery")
	flagSet.BoolVar(&config.quiet, "quiet", false, "suppress output to stdout")

	flagSet.Parse(args[1:])

	return config
}
<|endoftext|>


<commit_before>// Copyright 2016 IBM Corporation
//
//   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 nginx

import (
	"sync"
	"text/template"

	"encoding/json"

	"github.com/Sirupsen/logrus"
	"github.com/amalgam8/controller/resources"
	"github.com/amalgam8/sidecar/router/clients"
)

var log = logrus.WithFields(logrus.Fields{
	"module": "tenant.nginx",
})

// Nginx manages updates to NGINX configuration
type Nginx interface {
	// Update NGINX to run with the provided configuration
	Update(data []byte) error
}

type nginx struct {
	config      Config
	service     Service
	serviceName string
	template    *template.Template
	mutex       sync.Mutex
	nginxClient clients.NGINX
}

type NGINXConf struct {
	Config      Config
	Service     Service
	ServiceName string
	Path        string
	NGINXClient clients.NGINX
}

// NewNginx creates new Nginx instance
func NewNginx(conf NGINXConf) (Nginx, error) {
	t, err := template.ParseFiles(conf.Path)
	if err != nil {
		return nil, err
	}
	return &nginx{
		config:      conf.Config,      //NewConfig(),
		service:     conf.Service,     //NewService(),
		serviceName: conf.ServiceName, //serviceName,
		template:    t,
		nginxClient: conf.NGINXClient,
	}, nil
}

// Update NGINX to run with the provided configuration
func (n *nginx) Update(data []byte) error {
	n.mutex.Lock()
	defer n.mutex.Unlock()

	var err error
	templateConf := resources.NGINXJson{}
	if err = json.Unmarshal(data, &templateConf); err != nil {
		logrus.WithFields(logrus.Fields{
			"err":   err,
			"value": string(data),
		}).Error("Could not unmarshal object")
		return err
	}

	// Determine if NGINX is running
	running, err := n.service.Running()
	if err != nil {
		log.WithField("err", err).Error("Could not get status of NGINX service")
		return err
	}

	var nginxErr error
	if !running {
		// NGINX is not running; attempt to start NGINX
		nginxErr = n.startNginx()
	}

	// log the failed nginx config
	if nginxErr != nil {
		log.WithField("config", string(data)).Error("Failed NGINX config")
		return nginxErr
	}

	if err = n.nginxClient.UpdateHttpUpstreams(templateConf); err != nil {
		logrus.WithError(err).Error("Failed to update HTTP upstreams with NGINX")
		return err
	}

	//buf := bytes.NewBuffer([]byte{})
	//
	//if err = n.template.Execute(buf, &templateConf); err != nil {
	//	return err
	//}
	//
	//configBytes := buf.Bytes()
	//
	//// Replace service name
	//configStr := string(configBytes)
	//configStr = strings.Replace(configStr, "__SERVICE_NAME__", n.serviceName, -1)
	//
	//// Update the NGINX configuration file
	//if err = n.config.Update(configStr); err != nil {
	//	log.WithField("err", err).Error("Could not update NGINX configuration file")
	//	return err
	//}
	//
	//// Determine if NGINX is running
	//running, err := n.service.Running()
	//if err != nil {
	//	log.WithField("err", err).Error("Could not get status of NGINX service")
	//	return err
	//}
	//
	//var nginxErr error
	//if running {
	//	// NGINX is already running; attempt to reload NGINX
	//	nginxErr = n.reloadNginx()
	//} else {
	//	// NGINX is not running; attempt to start NGINX
	//	nginxErr = n.startNginx()
	//}
	//
	//// log the failed nginx config
	//if nginxErr != nil {
	//	log.WithField("config", string(data)).Error("Failed NGINX config")
	//	return nginxErr
	//}

	return nil
}

// startNginx attempts to start the NGINX service. On a failure attempt to start NGINX with the backup configuration.
func (n *nginx) startNginx() error {
	log.Debug("Starting NGINX with new configuration")
	if err := n.service.Start(); err != nil {
		log.WithField("err", err).Error("Could not start NGINX service with new configuration")
		if revertErr := n.config.Revert(); revertErr != nil {
			log.WithError(revertErr).Error("Reverting to backup NGINX configuration failed")
			return revertErr
		}

		if startErr := n.service.Start(); startErr != nil {
			log.WithField("err", startErr).Error("Could not start NGINX with backup configuration")
			return startErr
		}

		log.Warn("Reverted to old NGINX configuration")
		return err
	}

	return nil
}

// reloadNginx attempts to reload the NGINX service. On failure revert to the backup NGINX configuration.
func (n *nginx) reloadNginx() error {
	log.Debug("Reloading NGINX with new configuration")
	if err := n.service.Reload(); err != nil {
		log.WithField("err", err).Error("Could not reload NGINX with new configuration")
		if revertErr := n.config.Revert(); revertErr != nil {
			log.WithField("err", revertErr).Error("Failed to revert NGINX configuration to backup")
			return revertErr
		}

		log.Warn("Reverted to old NGINX configuration")
		return err
	}

	return nil
}
<commit_msg>removed unncessary commented code<commit_after>// Copyright 2016 IBM Corporation
//
//   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 nginx

import (
	"sync"
	"text/template"

	"encoding/json"

	"github.com/Sirupsen/logrus"
	"github.com/amalgam8/controller/resources"
	"github.com/amalgam8/sidecar/router/clients"
)

var log = logrus.WithFields(logrus.Fields{
	"module": "tenant.nginx",
})

// Nginx manages updates to NGINX configuration
type Nginx interface {
	// Update NGINX to run with the provided configuration
	Update(data []byte) error
}

type nginx struct {
	config      Config
	service     Service
	serviceName string
	template    *template.Template
	mutex       sync.Mutex
	nginxClient clients.NGINX
}

type NGINXConf struct {
	Config      Config
	Service     Service
	ServiceName string
	Path        string
	NGINXClient clients.NGINX
}

// NewNginx creates new Nginx instance
func NewNginx(conf NGINXConf) (Nginx, error) {
	t, err := template.ParseFiles(conf.Path)
	if err != nil {
		return nil, err
	}
	return &nginx{
		config:      conf.Config,      //NewConfig(),
		service:     conf.Service,     //NewService(),
		serviceName: conf.ServiceName, //serviceName,
		template:    t,
		nginxClient: conf.NGINXClient,
	}, nil
}

// Update NGINX to run with the provided configuration
func (n *nginx) Update(data []byte) error {
	n.mutex.Lock()
	defer n.mutex.Unlock()

	var err error
	templateConf := resources.NGINXJson{}
	if err = json.Unmarshal(data, &templateConf); err != nil {
		logrus.WithFields(logrus.Fields{
			"err":   err,
			"value": string(data),
		}).Error("Could not unmarshal object")
		return err
	}

	// Determine if NGINX is running
	running, err := n.service.Running()
	if err != nil {
		log.WithField("err", err).Error("Could not get status of NGINX service")
		return err
	}

	var nginxErr error
	if !running {
		// NGINX is not running; attempt to start NGINX
		nginxErr = n.startNginx()
	}

	// log the failed nginx config
	if nginxErr != nil {
		log.WithField("config", string(data)).Error("Failed NGINX config")
		return nginxErr
	}

	if err = n.nginxClient.UpdateHttpUpstreams(templateConf); err != nil {
		logrus.WithError(err).Error("Failed to update HTTP upstreams with NGINX")
		return err
	}

	return nil
}

// startNginx attempts to start the NGINX service. On a failure attempt to start NGINX with the backup configuration.
func (n *nginx) startNginx() error {
	log.Debug("Starting NGINX with new configuration")
	if err := n.service.Start(); err != nil {
		log.WithField("err", err).Error("Could not start NGINX service with new configuration")
		if revertErr := n.config.Revert(); revertErr != nil {
			log.WithError(revertErr).Error("Reverting to backup NGINX configuration failed")
			return revertErr
		}

		if startErr := n.service.Start(); startErr != nil {
			log.WithField("err", startErr).Error("Could not start NGINX with backup configuration")
			return startErr
		}

		log.Warn("Reverted to old NGINX configuration")
		return err
	}

	return nil
}

// reloadNginx attempts to reload the NGINX service. On failure revert to the backup NGINX configuration.
func (n *nginx) reloadNginx() error {
	log.Debug("Reloading NGINX with new configuration")
	if err := n.service.Reload(); err != nil {
		log.WithField("err", err).Error("Could not reload NGINX with new configuration")
		if revertErr := n.config.Revert(); revertErr != nil {
			log.WithField("err", revertErr).Error("Failed to revert NGINX configuration to backup")
			return revertErr
		}

		log.Warn("Reverted to old NGINX configuration")
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

// Installer is used to figure out what is being requested and how to install
type Installer struct {
	Request string
	Spec    *Specification
}

/* Requests can be a number of things:
a URL, e.g. https://example.com/stowage/
a reference to a repo, e.g. myrepo\some-binary
a local file, e.g. ./somedir/some-binary.json
a docker hub reference, e.g. ealexhudson/stowage

If it doesn't look like a URL or repo reference, we try it as a local
file. If that doesn't work, we assume it's a Docker hub reference.

*/
func (i *Installer) setup() bool {
	name := i.Request

	if strings.Index(name, "://") > -1 {
		// this is a URL
		return i.loadSpecFromURL(name)
	}

	repoSep := strings.Index(name, ":")
	if repoSep > -1 {
		// this could be a repo reference
		repo := name[0:repoSep]
		name = name[repoSep+1:]

		if strings.Index(repo, "/") == -1 {
			// repo names cannot have slashes in them; this must be a
			// docker hub reference!
			return i.loadSpecFromRepo(repo, name)
		}
	}

	_ = i.loadSpecFromFie(name)

	if i.Spec == nil {
		spec := Specification{
			Name:    name,
			Image:   name,
			Command: "",
		}
		i.Spec = &spec
	}

	return true
}

func (i *Installer) loadSpecFromFie(path string) bool {
	store := createStorage()
	spec, err := store.loadSpecification(path)
	if err != nil {
		return false
	}

	i.Spec = &spec
	return true
}

func (i *Installer) loadSpecFromURL(url string) bool {
	var spec Specification

	response, err := http.Get(url)
	if err != nil {
		fmt.Println("Specfile missing from repository!")
		return false
	}
	fmt.Println(response.Body)
	buf, _ := ioutil.ReadAll(response.Body)
	json.Unmarshal(buf, &spec)

	i.Spec = &spec
	return true
}

func (i *Installer) loadSpecFromRepo(repoName string, name string) bool {
	store := createStorage()

	repo, err := store.loadRepositoryByName(repoName)
	if err != nil {
		fmt.Println("No such repository.")
		return false
	}

	urlForSpec := repo.getURLForSpec(name)

	return i.loadSpecFromURL(urlForSpec)
}

func (i *Installer) run() bool {
	store := createStorage()
	store.saveSpecification(i.Spec)

	binary := Binary{name: i.Spec.Name, spec: i.Spec}
	binary.install()

	fmt.Printf("%s installed\n", i.Spec.Name)
	return true
}
<commit_msg>GH-9: Allow specfile to be included as a custom label.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os/exec"
	"strings"
)

// Installer is used to figure out what is being requested and how to install
type Installer struct {
	Request string
	Spec    *Specification
}

/* Requests can be a number of things:
a URL, e.g. https://example.com/stowage/
a reference to a repo, e.g. myrepo\some-binary
a local file, e.g. ./somedir/some-binary.json
a docker hub reference, e.g. ealexhudson/stowage

If it doesn't look like a URL or repo reference, we try it as a local
file. If that doesn't work, we assume it's a Docker hub reference.

*/
func (i *Installer) setup() bool {
	name := i.Request

	if strings.Index(name, "://") > -1 {
		// this is a URL
		return i.loadSpecFromURL(name)
	}

	repoSep := strings.Index(name, ":")
	if repoSep > -1 {
		// this could be a repo reference
		repo := name[0:repoSep]
		name = name[repoSep+1:]

		if strings.Index(repo, "/") == -1 {
			// repo names cannot have slashes in them; this must be a
			// docker hub reference!
			return i.loadSpecFromRepo(repo, name)
		}
	}

	_ = i.loadSpecFromFile(name)

	if i.Spec == nil {
		// we assume this is an image reference somehow

		// try fetching it; if this fails later things may not work but
		// that's not necessarily fatal
		fetchCmd := exec.Command("docker", "image", "pull", name)
		fetchCmd.Run()

		// check if we have a custom label with our specfile.
		specCmd := exec.Command("docker", "inspect", "--format",
			"{{ index .Config.Labels \"org.stowage.spec\" }}",
			name,
		)
		imgSpec, err := specCmd.Output()
		if err != nil {
			panic(err)
		}

		spec := Specification{
			Name:    name,
			Image:   name,
			Command: "",
		}

		if len(imgSpec) > 0 {
			// if a spec file was provided via the image, let's load that up
			spec.fromJSON(imgSpec)
		}

		i.Spec = &spec
	}

	cliName := strings.LastIndex(name, "/")
	if (i.Spec != nil) && (cliName > -1) && (i.Spec.Name == name) {
		// this is a reference to an image in a registry - we want to use
		// the project name as cli name. Only do this if we haven't already
		// learned a better name somewhere (e.g. Docker label)
		i.Spec.Name = name[cliName+1:]
	}

	return true
}

func (i *Installer) loadSpecFromFile(path string) bool {
	store := createStorage()
	spec, err := store.loadSpecification(path)
	if err != nil {
		return false
	}

	i.Spec = &spec
	return true
}

func (i *Installer) loadSpecFromURL(url string) bool {
	var spec Specification

	response, err := http.Get(url)
	if err != nil {
		fmt.Println("Specfile missing from repository!")
		return false
	}
	buf, _ := ioutil.ReadAll(response.Body)
	json.Unmarshal(buf, &spec)

	i.Spec = &spec
	return true
}

func (i *Installer) loadSpecFromRepo(repoName string, name string) bool {
	store := createStorage()

	repo, err := store.loadRepositoryByName(repoName)
	if err != nil {
		fmt.Println("No such repository.")
		return false
	}

	urlForSpec := repo.getURLForSpec(name)

	return i.loadSpecFromURL(urlForSpec)
}

func (i *Installer) run() bool {
	store := createStorage()
	store.saveSpecification(i.Spec)

	binary := Binary{name: i.Spec.Name, spec: i.Spec}
	binary.install()

	fmt.Printf("%s installed\n", i.Spec.Name)
	return true
}
<|endoftext|>


<commit_before>// Copyright 2015 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
// Use of this source code is governed by the MIT-license that can be
// found in the LICENSE file.

package cfg

type Config struct {
	// This station's callsign.
	MyCall string `json:"mycall"`

	// Secure login password used when a secure login challenge is received.
	//
	// The user is prompted if this is undefined.
	SecureLoginPassword string `json:"secure_login_password"`

	// Auxiliary callsigns to fetch email on behalf of.
	AuxAddrs []string `json:"auxiliary_addresses"`

	// Maidenhead grid square (e.g. JP20qe)
	Locator string `json:"locator"`

	// Handshake comment lines sent to remote node on incoming connections.
	MOTD []string `json:"motd"`

	// Default connect METHOD:[URI] (e.g. "telnet", "ax25:LA1B-10").
	ConnectDefaults []string `json:"connect_defaults"`

	// Methods to listen for incoming P2P connections by default
	// (e.g. "ax25", "winmor", "telnet").
	Listen []string `json:"listen"`

	// Hamlib rigs available (with reference name) for ptt and frequency
	// control.
	HamlibRigs map[string]HamlibConfig `json:"hamlib_rigs"`

	AX25      AX25Config      `json:"ax25"`       // See AX25Config.
	SerialTNC SerialTNCConfig `json:"serial-tnc"` // See SerialTNCConfig.
	Winmor    WinmorConfig    `json:"winmor"`     // See WinmorConfig.
	Telnet    TelnetConfig    `json:"telnet"`     // See TelnetConfig.

	// Command schedule (cron-like syntax).
	// Examples:
	//   # Connect to telnet once every hour
	//   "@hourly": "connect telnet"
	//
	//   # Change winmor listen frequency based on hour of day
	//   "00 10 * * *": "freq winmor:7350.000", # 40m from 10:00
	//   "00 18 * * *": "freq winmor:5347.000", # 60m from 18:00
	//   "00 22 * * *": "freq winmor:3602.000"  # 80m from 22:00
	Schedule map[string]string `json:"schedule"`
}

type HamlibConfig struct {
	// The rig model number as reported by --rig-list.
	RigModel int `json:"rig_model"`

	// Serial port (e.g. /dev/ttyUSB0 or COM1).
	TTYPath string `json:"tty_path"`

	// Baudrate for the serial port (e.g. 4800).
	Baudrate int `json:"baudrate"`
}

type WinmorConfig struct {
	// Network address of the Winmor TNC (e.g. localhost:8500).
	Addr string `json:"addr"`

	// Bandwidth to use when getting an inbound connection (500/1600).
	InboundBandwidth int `json:"inbound_bandwidth"`

	// (optional) Reference name to the Hamlib rig to control frequency and ptt.
	Rig string `json:"rig"` //TODO: Maybe custom unmarshal to ensure the rig is registered?

	// Set to true if hamlib should control PTT (SignaLink=false, most rigexpert=true).
	PTTControl bool `json:"ptt_ctrl"`
}

type TelnetConfig struct {
	// Network address (and port) to listen for telnet-p2p connections (e.g. :8774).
	ListenAddr string `json:"listen_addr"`

	// Telnet-p2p password.
	Password string `json:"password"`
}

type SerialTNCConfig struct {
	// Serial port (e.g. /dev/ttyUSB0 or COM1).
	Path string `json:"path"`

	// Baudrate for the serial port (e.g. 57600).
	Baudrate int `json:"baudrate"`

	// Type of TNC (currently only 'kenwood').
	Type string `json:"type"`
}

type AX25Config struct {
	// axport to use (as defined in /etc/ax25/axports).
	Port string `json:"port"`

	// Optional beacon when listening for incoming packet-p2p connections.
	Beacon BeaconConfig `json:"beacon"`
}

type BeaconConfig struct {
	// Beacon interval in seconds (e.g. 3600 for once every 1 hour)
	Every int `json:"every"` // (seconds)

	// Beacon data/message
	Message string `json:"message"`

	// Beacon destination (e.g. IDENT)
	Destination string `json:"destination"`
}

var DefaultConfig Config = Config{
	MyCall:          "",
	AuxAddrs:        []string{},
	ConnectDefaults: []string{"telnet"},
	AX25: AX25Config{
		Beacon: BeaconConfig{
			Every:       3600,
			Message:     "Winlink P2P",
			Destination: "IDENT",
		},
	},
	SerialTNC: SerialTNCConfig{
		Path:     "/dev/ttyUSB0",
		Baudrate: 9600,
		Type:     "Kenwood",
	},
	Winmor: WinmorConfig{
		Addr:             "localhost:8500",
		InboundBandwidth: 1600,
	},
	Telnet: TelnetConfig{
		ListenAddr: ":8774",
		Password:   "",
	},
}
<commit_msg>cmd/wl2k: Config code cleanup. Add default MOTD.<commit_after>// Copyright 2015 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
// Use of this source code is governed by the MIT-license that can be
// found in the LICENSE file.

package cfg

type Config struct {
	// This station's callsign.
	MyCall string `json:"mycall"`

	// Secure login password used when a secure login challenge is received.
	//
	// The user is prompted if this is undefined.
	SecureLoginPassword string `json:"secure_login_password"`

	// Auxiliary callsigns to fetch email on behalf of.
	AuxAddrs []string `json:"auxiliary_addresses"`

	// Maidenhead grid square (e.g. JP20qe).
	Locator string `json:"locator"`

	// Handshake comment lines sent to remote node on incoming connections.
	//
	// Example: ["QTH: Hagavik, Norway. Operator: Martin", "Rig: FT-897 with Signalink USB"]
	MOTD []string `json:"motd"`

	// Default connect METHOD:[URI].
	//
	// Example: ["telnet", "ax25:LA1B-10"]
	ConnectDefaults []string `json:"connect_defaults"`

	// Methods to listen for incoming P2P connections by default.
	//
	// Example: ["ax25", "winmor", "telnet"]
	Listen []string `json:"listen"`

	// Hamlib rigs available (with reference name) for ptt and frequency control.
	HamlibRigs map[string]HamlibConfig `json:"hamlib_rigs"`

	AX25      AX25Config      `json:"ax25"`       // See AX25Config.
	SerialTNC SerialTNCConfig `json:"serial-tnc"` // See SerialTNCConfig.
	Winmor    WinmorConfig    `json:"winmor"`     // See WinmorConfig.
	Telnet    TelnetConfig    `json:"telnet"`     // See TelnetConfig.

	// Command schedule (cron-like syntax).
	//
	// Examples:
	//   # Connect to telnet once every hour
	//   "@hourly": "connect telnet"
	//
	//   # Change winmor listen frequency based on hour of day
	//   "00 10 * * *": "freq winmor:7350.000", # 40m from 10:00
	//   "00 18 * * *": "freq winmor:5347.000", # 60m from 18:00
	//   "00 22 * * *": "freq winmor:3602.000"  # 80m from 22:00
	Schedule map[string]string `json:"schedule"`
}

type HamlibConfig struct {
	// The rig model number as reported by --rig-list.
	RigModel int `json:"rig_model"`

	// Serial port (e.g. /dev/ttyUSB0 or COM1).
	TTYPath string `json:"tty_path"`

	// Baudrate for the serial port (e.g. 4800).
	Baudrate int `json:"baudrate"`
}

type WinmorConfig struct {
	// Network address of the Winmor TNC (e.g. localhost:8500).
	Addr string `json:"addr"`

	// Bandwidth to use when getting an inbound connection (500/1600).
	InboundBandwidth int `json:"inbound_bandwidth"`

	// (optional) Reference name to the Hamlib rig to control frequency and ptt.
	Rig string `json:"rig"` //TODO: Maybe custom unmarshal to ensure the rig is registered?

	// Set to true if hamlib should control PTT (SignaLink=false, most rigexpert=true).
	PTTControl bool `json:"ptt_ctrl"`
}

type TelnetConfig struct {
	// Network address (and port) to listen for telnet-p2p connections (e.g. :8774).
	ListenAddr string `json:"listen_addr"`

	// Telnet-p2p password.
	Password string `json:"password"`
}

type SerialTNCConfig struct {
	// Serial port (e.g. /dev/ttyUSB0 or COM1).
	Path string `json:"path"`

	// Baudrate for the serial port (e.g. 57600).
	Baudrate int `json:"baudrate"`

	// Type of TNC (currently only 'kenwood').
	Type string `json:"type"`
}

type AX25Config struct {
	// axport to use (as defined in /etc/ax25/axports).
	Port string `json:"port"`

	// Optional beacon when listening for incoming packet-p2p connections.
	Beacon BeaconConfig `json:"beacon"`
}

type BeaconConfig struct {
	// Beacon interval in seconds (e.g. 3600 for once every 1 hour)
	Every int `json:"every"` // (seconds)

	// Beacon data/message
	Message string `json:"message"`

	// Beacon destination (e.g. IDENT)
	Destination string `json:"destination"`
}

var DefaultConfig Config = Config{
	MyCall:          "",
	MOTD:            []string{"Open source WL2K client - github.com/LA5NTA/wl2k-go"},
	AuxAddrs:        []string{},
	ConnectDefaults: []string{"telnet"},
	AX25: AX25Config{
		Beacon: BeaconConfig{
			Every:       3600,
			Message:     "Winlink P2P",
			Destination: "IDENT",
		},
	},
	SerialTNC: SerialTNCConfig{
		Path:     "/dev/ttyUSB0",
		Baudrate: 9600,
		Type:     "Kenwood",
	},
	Winmor: WinmorConfig{
		Addr:             "localhost:8500",
		InboundBandwidth: 1600,
	},
	Telnet: TelnetConfig{
		ListenAddr: ":8774",
		Password:   "",
	},
}
<|endoftext|>


<commit_before>// Copyright 2012 tsuru 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 main

import (
	"github.com/globocom/tsuru/app"
	"github.com/globocom/tsuru/db"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/provision"
	"labix.org/v2/mgo/bson"
	"sort"
)

type AppList []*app.App

func (l AppList) Search(name string) (*app.App, int) {
	index := sort.Search(len(l), func(i int) bool {
		return l[i].Name >= name
	})
	if index < len(l) && l[index].Name == name {
		return l[index], -1
	} else if index < len(l) {
		return &app.App{Name: name}, index
	}
	return &app.App{Name: name}, len(l)
}

func (l *AppList) Add(a *app.App, index int) {
	length := len(*l)
	*l = append(*l, a)
	if index < length {
		for i := length; i > index; i-- {
			(*l)[i] = (*l)[i-1]
		}
		(*l)[index] = a
	}
}

func update(units []provision.Unit) {
	log.Print("updating status from provisioner")
	var l AppList
	for _, unit := range units {
		a, index := l.Search(unit.AppName)
		if index > -1 {
			err := a.Get()
			if err != nil {
				log.Printf("collector: app %q not found. Skipping.\n", unit.AppName)
				continue
			}
		}
		u := app.Unit{}
		u.Name = unit.Name
		u.Type = unit.Type
		u.Machine = unit.Machine
		u.Ip = unit.Ip
		u.State = string(unit.Status)
		a.State = string(unit.Status)
		a.Ip = unit.Ip
		a.AddUnit(&u)
		if index > -1 {
			l.Add(a, index)
		}
	}
	for _, a := range l {
		db.Session.Apps().Update(bson.M{"name": a.Name}, a)
	}
}
<commit_msg>collector: added note on AppList regarding thread safety<commit_after>// Copyright 2012 tsuru 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 main

import (
	"github.com/globocom/tsuru/app"
	"github.com/globocom/tsuru/db"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/provision"
	"labix.org/v2/mgo/bson"
	"sort"
)

// AppList is a list of apps. It's not thread safe.
type AppList []*app.App

func (l AppList) Search(name string) (*app.App, int) {
	index := sort.Search(len(l), func(i int) bool {
		return l[i].Name >= name
	})
	if index < len(l) && l[index].Name == name {
		return l[index], -1
	} else if index < len(l) {
		return &app.App{Name: name}, index
	}
	return &app.App{Name: name}, len(l)
}

func (l *AppList) Add(a *app.App, index int) {
	length := len(*l)
	*l = append(*l, a)
	if index < length {
		for i := length; i > index; i-- {
			(*l)[i] = (*l)[i-1]
		}
		(*l)[index] = a
	}
}

func update(units []provision.Unit) {
	log.Print("updating status from provisioner")
	var l AppList
	for _, unit := range units {
		a, index := l.Search(unit.AppName)
		if index > -1 {
			err := a.Get()
			if err != nil {
				log.Printf("collector: app %q not found. Skipping.\n", unit.AppName)
				continue
			}
		}
		u := app.Unit{}
		u.Name = unit.Name
		u.Type = unit.Type
		u.Machine = unit.Machine
		u.Ip = unit.Ip
		u.State = string(unit.Status)
		a.State = string(unit.Status)
		a.Ip = unit.Ip
		a.AddUnit(&u)
		if index > -1 {
			l.Add(a, index)
		}
	}
	for _, a := range l {
		db.Session.Apps().Update(bson.M{"name": a.Name}, a)
	}
}
<|endoftext|>


<commit_before>package commands

import (
	"errors"
	"io/ioutil"

	"github.com/pivotal-cf-experimental/bosh-bootloader/aws"
	"github.com/pivotal-cf-experimental/bosh-bootloader/aws/iam"
	"github.com/pivotal-cf-experimental/bosh-bootloader/flags"
	"github.com/pivotal-cf-experimental/bosh-bootloader/storage"
)

type updateLBConfig struct {
	certPath string
	keyPath  string
}

type UpdateLBs struct {
	certificateManager        certificateManager
	clientProvider            awsClientProvider
	availabilityZoneRetriever availabilityZoneRetriever
	infrastructureManager     infrastructureManager
}

func NewUpdateLBs(certificateManager certificateManager, clientProvider awsClientProvider,
	availabilityZoneRetriever availabilityZoneRetriever, infrastructureManager infrastructureManager) UpdateLBs {

	return UpdateLBs{
		certificateManager:        certificateManager,
		clientProvider:            clientProvider,
		availabilityZoneRetriever: availabilityZoneRetriever,
		infrastructureManager:     infrastructureManager,
	}
}

func (c UpdateLBs) Execute(globalFlags GlobalFlags, subcommandFlags []string, state storage.State) (storage.State, error) {
	config, err := c.parseFlags(subcommandFlags)
	if err != nil {
		return state, err
	}

	awsConfig := aws.Config{
		AccessKeyID:      state.AWS.AccessKeyID,
		SecretAccessKey:  state.AWS.SecretAccessKey,
		Region:           state.AWS.Region,
		EndpointOverride: globalFlags.EndpointOverride,
	}

	cloudFormationClient, err := c.clientProvider.CloudFormationClient(awsConfig)
	if err != nil {
		return state, err
	}

	stackExists, err := c.infrastructureManager.Exists(state.Stack.Name, cloudFormationClient)
	if err != nil {
		return state, err
	}

	if !stackExists {
		return state, errors.New("a bbl environment could not be found, please create a new environment before running this command again")
	}

	if state.Stack.LBType != "concourse" && state.Stack.LBType != "cf" {
		return state, errors.New("no load balancer has been found for this bbl environment")
	}

	iamClient, err := c.clientProvider.IAMClient(awsConfig)
	if err != nil {
		return state, err
	}

	if match, err := c.certificatesMatch(config.certPath, state.CertificateName, iamClient); err != nil {
		return state, err
	} else if match {
		return state, nil
	}

	certificateName, err := c.certificateManager.Create(config.certPath, config.keyPath, iamClient)
	if err != nil {
		return state, err
	}

	ec2Client, err := c.clientProvider.EC2Client(awsConfig)
	if err != nil {
		return state, err
	}

	availabilityZones, err := c.availabilityZoneRetriever.Retrieve(state.AWS.Region, ec2Client)
	if err != nil {
		return state, err
	}

	certificate, err := c.certificateManager.Describe(certificateName, iamClient)
	if err != nil {
		return state, err
	}

	_, err = c.infrastructureManager.Update(state.KeyPair.Name, len(availabilityZones), state.Stack.Name, state.Stack.LBType, certificate.ARN, cloudFormationClient)
	if err != nil {
		return state, err
	}

	err = c.certificateManager.Delete(state.CertificateName, iamClient)
	if err != nil {
		return state, err
	}

	state.CertificateName = certificateName

	return state, nil
}

func (c UpdateLBs) certificatesMatch(certPath string, oldCertName string, client iam.Client) (bool, error) {
	localCertificate, err := ioutil.ReadFile(certPath)
	if err != nil {
		return false, err
	}

	remoteCertificate, err := c.certificateManager.Describe(oldCertName, client)
	if err != nil {
		return false, err
	}

	return string(localCertificate) == remoteCertificate.Body, nil
}

func (UpdateLBs) parseFlags(subcommandFlags []string) (updateLBConfig, error) {
	lbFlags := flags.New("update-lbs")

	config := updateLBConfig{}
	lbFlags.String(&config.certPath, "cert", "")
	lbFlags.String(&config.keyPath, "key", "")

	err := lbFlags.Parse(subcommandFlags)
	if err != nil {
		return config, err
	}

	return config, nil
}
<commit_msg>Refactor update-lbs to use private methods<commit_after>package commands

import (
	"errors"
	"io/ioutil"

	"github.com/pivotal-cf-experimental/bosh-bootloader/aws"
	"github.com/pivotal-cf-experimental/bosh-bootloader/aws/cloudformation"
	"github.com/pivotal-cf-experimental/bosh-bootloader/aws/ec2"
	"github.com/pivotal-cf-experimental/bosh-bootloader/aws/iam"
	"github.com/pivotal-cf-experimental/bosh-bootloader/flags"
	"github.com/pivotal-cf-experimental/bosh-bootloader/storage"
)

type updateLBConfig struct {
	certPath string
	keyPath  string
}

type UpdateLBs struct {
	certificateManager        certificateManager
	clientProvider            awsClientProvider
	availabilityZoneRetriever availabilityZoneRetriever
	infrastructureManager     infrastructureManager
}

func NewUpdateLBs(certificateManager certificateManager, clientProvider awsClientProvider,
	availabilityZoneRetriever availabilityZoneRetriever, infrastructureManager infrastructureManager) UpdateLBs {

	return UpdateLBs{
		certificateManager:        certificateManager,
		clientProvider:            clientProvider,
		availabilityZoneRetriever: availabilityZoneRetriever,
		infrastructureManager:     infrastructureManager,
	}
}

func (c UpdateLBs) Execute(globalFlags GlobalFlags, subcommandFlags []string, state storage.State) (storage.State, error) {
	config, err := c.parseFlags(subcommandFlags)
	if err != nil {
		return state, err
	}

	awsConfig := aws.Config{
		AccessKeyID:      state.AWS.AccessKeyID,
		SecretAccessKey:  state.AWS.SecretAccessKey,
		Region:           state.AWS.Region,
		EndpointOverride: globalFlags.EndpointOverride,
	}

	cloudFormationClient, err := c.clientProvider.CloudFormationClient(awsConfig)
	if err != nil {
		return state, err
	}

	iamClient, err := c.clientProvider.IAMClient(awsConfig)
	if err != nil {
		return state, err
	}

	ec2Client, err := c.clientProvider.EC2Client(awsConfig)
	if err != nil {
		return state, err
	}

	if err := c.checkFastFails(state.Stack.Name, state.Stack.LBType, cloudFormationClient); err != nil {
		return state, err
	}

	if match, err := c.certificatesMatch(config.certPath, state.CertificateName, iamClient); err != nil {
		return state, err
	} else if match {
		return state, nil
	}

	certificateName, err := c.certificateManager.Create(config.certPath, config.keyPath, iamClient)
	if err != nil {
		return state, err
	}

	if err := c.updateStack(certificateName, state.KeyPair.Name, state.Stack.Name, state.Stack.LBType, state.AWS.Region, iamClient, cloudFormationClient, ec2Client); err != nil {
		return state, err
	}

	err = c.certificateManager.Delete(state.CertificateName, iamClient)
	if err != nil {
		return state, err
	}

	state.CertificateName = certificateName

	return state, nil
}

func (c UpdateLBs) certificatesMatch(certPath string, oldCertName string, client iam.Client) (bool, error) {
	localCertificate, err := ioutil.ReadFile(certPath)
	if err != nil {
		return false, err
	}

	remoteCertificate, err := c.certificateManager.Describe(oldCertName, client)
	if err != nil {
		return false, err
	}

	return string(localCertificate) == remoteCertificate.Body, nil
}

func (UpdateLBs) parseFlags(subcommandFlags []string) (updateLBConfig, error) {
	lbFlags := flags.New("update-lbs")

	config := updateLBConfig{}
	lbFlags.String(&config.certPath, "cert", "")
	lbFlags.String(&config.keyPath, "key", "")

	err := lbFlags.Parse(subcommandFlags)
	if err != nil {
		return config, err
	}

	return config, nil
}

func (c UpdateLBs) updateStack(certificateName string, keyPairName string, stackName string, lbType string, awsRegion string, iamClient iam.Client, cloudFormationClient cloudformation.Client, ec2Client ec2.Client) error {
	availabilityZones, err := c.availabilityZoneRetriever.Retrieve(awsRegion, ec2Client)
	if err != nil {
		return err
	}

	certificate, err := c.certificateManager.Describe(certificateName, iamClient)
	if err != nil {
		return err
	}

	_, err = c.infrastructureManager.Update(keyPairName, len(availabilityZones), stackName, lbType, certificate.ARN, cloudFormationClient)
	if err != nil {
		return err
	}

	return nil
}

func (c UpdateLBs) checkFastFails(stackName string, lbType string, cloudFormationClient cloudformation.Client) error {
	stackExists, err := c.infrastructureManager.Exists(stackName, cloudFormationClient)
	if err != nil {
		return err
	}

	if !stackExists {
		return errors.New("a bbl environment could not be found, please create a new environment before running this command again")
	}

	if lbType != "concourse" && lbType != "cf" {
		return errors.New("no load balancer has been found for this bbl environment")
	}

	return nil
}
<|endoftext|>


<commit_before>package main // import "github.com/ONSdigital/go-launch-a-survey"

import (
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/gorilla/mux"

	"github.com/ONSdigital/go-launch-a-survey/authentication"
	"github.com/ONSdigital/go-launch-a-survey/settings"
)

func serveTemplate(templateName string, data interface{}, w http.ResponseWriter, r *http.Request) {
	lp := filepath.Join("templates", "layout.html")
	fp := filepath.Join("templates", filepath.Clean(templateName))

	// Return a 404 if the template doesn't exist or is directory
	info, err := os.Stat(fp)
	if err != nil && (os.IsNotExist(err) || info.IsDir()) {
		fmt.Println("Cannot find: " + fp)
		http.NotFound(w, r)
		return
	}

	tmpl, err := template.ParseFiles(lp, fp)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
		return
	}

	if err := tmpl.ExecuteTemplate(w, "layout", data); err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
	}
}

type page struct {
	Schemas []string
}

func getAvailableSchemas() []string {
	// TODO: Replace with something dynamic
	return []string{
		"0_rogue_one.json",
		"0_star_wars.json",
		"1_0001.json",
		"1_0005.json",
		"1_0102.json",
		"1_0112.json",
		"1_0203.json",
		"1_0205.json",
		"1_0213.json",
		"1_0215.json",
		"2_0001.json",
		"census_communal.json",
		"census_household.json",
		"census_individual.json",
		"mci_0203.json",
		"mci_0213.json",
		"mci_0205.json",
		"multiple_answers.json",
		"rsi_0102.json",
		"rsi_0112.json",
		"test_big_list_naughty_strings.json",
		"test_checkbox.json",
		"test_conditional_routing.json",
		"test_currency.json",
		"test_dates.json",
		"test_final_confirmation.json",
		"test_household_question.json",
		"test_interstitial_page.json",
		"test_language.json",
		"test_language_cy.json",
		"test_markup.json",
		"test_metadata_routing.json",
		"test_navigation.json",
		"test_navigation_confirmation.json",
		"test_percentage.json",
		"test_question_guidance.json",
		"test_radio.json",
		"test_radio_checkbox_descriptions.json",
		"test_relationship_household.json",
		"test_repeating_and_conditional_routing.json",
		"test_repeating_household.json",
		"test_routing_on_multiple_select.json",
		"test_skip_condition.json",
		"test_skip_condition_block.json",
		"test_skip_condition_group.json",
		"test_textarea.json",
		"test_textfield.json",
		"test_timeout.json",
		"test_total_breakdown.json",
	}
}

func getLaunchHandler(w http.ResponseWriter, r *http.Request) {
	p := page{Schemas: getAvailableSchemas()}
	serveTemplate("launch.html", p, w, r)
}

func postLaunchHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, fmt.Sprintf("POST. r.ParseForm() err: %v", err), 500)
		return
	}

	token, tokenErr := authentication.ConvertPostToToken(r.PostForm)
	if tokenErr != nil {
		http.Error(w, fmt.Sprintf("ConvertPostToToken failed err: %v", tokenErr), 500)
		return
	}

	launchAction := r.PostForm.Get("action_launch")
	flushAction := r.PostForm.Get("action_flush")
	log.Println("Request: " + r.PostForm.Encode())

	hostURL := settings.Get("SURVEY_RUNNER_URL")

	if flushAction != "" {
		http.Redirect(w, r, hostURL+"/flush?token="+token, 307)
	} else if launchAction != "" {
		http.Redirect(w, r, hostURL+"/session?token="+token, 301)
	} else {
		http.Error(w, fmt.Sprintf("Invalid Action"), 500)
	}
}

func main() {
	r := mux.NewRouter()

	// Launch handlers
	r.HandleFunc("/", getLaunchHandler).Methods("GET")
	r.HandleFunc("/", postLaunchHandler).Methods("POST")

	// Serve static assets
	staticFs := http.FileServer(http.Dir("static"))
	r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", staticFs))

	// Bind to a port and pass our router in
	hostname := settings.Get("GO_LAUNCH_A_SURVEY_LISTEN_HOST") + ":" + settings.Get("GO_LAUNCH_A_SURVEY_LISTEN_PORT")

	log.Println("Listening on " + hostname)
	log.Fatal(http.ListenAndServe(hostname, r))
}
<commit_msg>EQ 1221 Numeric Range Validation<commit_after>package main // import "github.com/ONSdigital/go-launch-a-survey"

import (
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/gorilla/mux"

	"github.com/ONSdigital/go-launch-a-survey/authentication"
	"github.com/ONSdigital/go-launch-a-survey/settings"
)

func serveTemplate(templateName string, data interface{}, w http.ResponseWriter, r *http.Request) {
	lp := filepath.Join("templates", "layout.html")
	fp := filepath.Join("templates", filepath.Clean(templateName))

	// Return a 404 if the template doesn't exist or is directory
	info, err := os.Stat(fp)
	if err != nil && (os.IsNotExist(err) || info.IsDir()) {
		fmt.Println("Cannot find: " + fp)
		http.NotFound(w, r)
		return
	}

	tmpl, err := template.ParseFiles(lp, fp)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
		return
	}

	if err := tmpl.ExecuteTemplate(w, "layout", data); err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
	}
}

type page struct {
	Schemas []string
}

func getAvailableSchemas() []string {
	// TODO: Replace with something dynamic
	return []string{
		"0_rogue_one.json",
		"0_star_wars.json",
		"1_0001.json",
		"1_0005.json",
		"1_0102.json",
		"1_0112.json",
		"1_0203.json",
		"1_0205.json",
		"1_0213.json",
		"1_0215.json",
		"2_0001.json",
		"census_communal.json",
		"census_household.json",
		"census_individual.json",
		"mci_0203.json",
		"mci_0213.json",
		"mci_0205.json",
		"multiple_answers.json",
		"rsi_0102.json",
		"rsi_0112.json",
		"test_big_list_naughty_strings.json",
		"test_checkbox.json",
		"test_conditional_routing.json",
		"test_currency.json",
		"test_dates.json",
		"test_final_confirmation.json",
		"test_household_question.json",
		"test_interstitial_page.json",
		"test_language.json",
		"test_language_cy.json",
		"test_markup.json",
		"test_metadata_routing.json",
		"test_navigation.json",
		"test_navigation_confirmation.json",
		"test_numbers.json",
		"test_percentage.json",
		"test_question_guidance.json",
		"test_radio.json",
		"test_radio_checkbox_descriptions.json",
		"test_relationship_household.json",
		"test_repeating_and_conditional_routing.json",
		"test_repeating_household.json",
		"test_routing_on_multiple_select.json",
		"test_skip_condition.json",
		"test_skip_condition_block.json",
		"test_skip_condition_group.json",
		"test_textarea.json",
		"test_textfield.json",
		"test_timeout.json",
		"test_total_breakdown.json",
	}
}

func getLaunchHandler(w http.ResponseWriter, r *http.Request) {
	p := page{Schemas: getAvailableSchemas()}
	serveTemplate("launch.html", p, w, r)
}

func postLaunchHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, fmt.Sprintf("POST. r.ParseForm() err: %v", err), 500)
		return
	}

	token, tokenErr := authentication.ConvertPostToToken(r.PostForm)
	if tokenErr != nil {
		http.Error(w, fmt.Sprintf("ConvertPostToToken failed err: %v", tokenErr), 500)
		return
	}

	launchAction := r.PostForm.Get("action_launch")
	flushAction := r.PostForm.Get("action_flush")
	log.Println("Request: " + r.PostForm.Encode())

	hostURL := settings.Get("SURVEY_RUNNER_URL")

	if flushAction != "" {
		http.Redirect(w, r, hostURL+"/flush?token="+token, 307)
	} else if launchAction != "" {
		http.Redirect(w, r, hostURL+"/session?token="+token, 301)
	} else {
		http.Error(w, fmt.Sprintf("Invalid Action"), 500)
	}
}

func main() {
	r := mux.NewRouter()

	// Launch handlers
	r.HandleFunc("/", getLaunchHandler).Methods("GET")
	r.HandleFunc("/", postLaunchHandler).Methods("POST")

	// Serve static assets
	staticFs := http.FileServer(http.Dir("static"))
	r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", staticFs))

	// Bind to a port and pass our router in
	hostname := settings.Get("GO_LAUNCH_A_SURVEY_LISTEN_HOST") + ":" + settings.Get("GO_LAUNCH_A_SURVEY_LISTEN_PORT")

	log.Println("Listening on " + hostname)
	log.Fatal(http.ListenAndServe(hostname, r))
}
<|endoftext|>


<commit_before>package twilio

import (
	"encoding/json"
	"fmt"

	twilio "github.com/twilio/twilio-go/internal"
)

// IncomingPhoneNumber represents a Twilio phone number provisioned from Twilio, ported or hosted to Twilio.
// See: https://www.twilio.com/docs/phone-numbers/api/incomingphonenumber-resource
type IncomingPhoneNumber struct {
	Beta                 bool            `json:"beta"`
	VoiceCallerIDLookup  bool            `json:"voice_caller_id_lookup"`
	AccountSid           string          `json:"account_sid"`
	AddressSid           string          `json:"address_sid"`
	AddressRequirements  string          `json:"address_requirements"`
	APIVersion           string          `json:"api_version"`
	Capabilities         map[string]bool `json:"capabilities"`
	DateCreated          string          `json:"date_created"`
	DateUpdated          string          `json:"date_updated"`
	FriendlyName         string          `json:"friendly_name"`
	IdentitySid          string          `json:"identity_sid"`
	PhoneNumber          string          `json:"phone_number"`
	Origin               string          `json:"origin"`
	Sid                  string          `json:"sid"`
	SMSApplicationSid    string          `json:"sms_application_sid"`
	SMSFallbackMethod    string          `json:"sms_fallback_method"`
	SMSFallbackURL       string          `json:"sms_fallback_url"`
	SMSMethod            string          `json:"sms_method"`
	SMSURL               string          `json:"sms_url"`
	StatusCallback       string          `json:"status_callback"`
	StatusCallbackMethod string          `json:"status_callback_method"`
	TrunkSid             string          `json:"trunk_sid"`
	URI                  string          `json:"uri"`
	VoiceApplicationSid  string          `json:"voice_application_sid"`
	VoiceFallbackMethod  string          `json:"voice_fallback_method"`
	VoiceFallbackURL     string          `json:"voice_fallback_url"`
	VoiceMethod          string          `json:"voice_method"`
	VoiceURL             string          `json:"voice_url"`
	EmergencyStatus      string          `json:"emergency_status"`
	EmergencyAddressSid  string          `json:"emergency_address_sid"`
	BundleSid            string          `json:"bundle_sid"`
}

// IncomingPhoneNumberParams is the set of parameters that can
// be used when creating or updating an Incoming Phone Number.
type IncomingPhoneNumberParams struct {
	AccountSid           string `url:"AccountSid,omitempty"`
	APIVersion           string `url:"ApiVersion,omitempty"`
	FriendlyName         string `url:"FriendlyName,omitempty"`
	SMSApplicationSid    string `url:"SmsApplicationSid,omitempty"`
	SMSFallbackMethod    string `url:"SmsApplicationMethod,omitempty"`
	PhoneNumber          string `url:"PhoneNumber,omitempty"`
	AreaCode             string `url:"AreaCode,omitempty"`
	SMSFallbackURL       string `url:"SmsFallbackUrl,omitempty"`
	SMSMethod            string `url:"SmsMethod,omitempty"`
	SMSURL               string `url:"SmsUrl,omitempty"`
	StatusCallback       string `url:"StatusCallback,omitempty"`
	AddressSid           string `url:"AddressSid,omitempty"`
	StatusCallbackMethod string `url:"StatusCallbackMethod,omitempty"`
	VoiceApplicationSid  string `url:"VoiceApplicationSid,omitempty"`
	VoiceCallerIDLookup  bool   `url:"VoiceCallerIdLookup,omitempty"`
	VoiceFallbackMethod  string `url:"VoiceFallbackMethod,omitempty"`
	VoiceFallbackURL     string `url:"VoiceFallbackUrl,omitempty"`
	VoiceMethod          string `url:"VoiceMethod,omitempty"`
	VoiceURL             string `url:"VoiceUrl,omitempty"`
	VoiceReceiveMode     string `url:"VoiceReceiveMode,omitempty"`
	EmergencyStatus      string `url:"EmergencyStatus,omitempty"`
	EmergencyAddressSid  string `url:"EmergencyAddressSid,omitempty"`
	BundleSid            string `url:"BundleSid,omitempty"`
	IdentitySid          string `url:"IdentitySid,omitempty"`
	TrunkSid             string `url:"TrunkSid,omitempty"`
}

// IncomingPhoneNumberClient is the entrypoint for the Incoming Phone Number resource.
// See: https://www.twilio.com/docs/phone-numbers/api/incomingphonenumber-resource
type IncomingPhoneNumberClient struct {
	client     *twilio.Client
	serviceURL string
}

// NewIncomingPhoneNumberClient constructs a new IncomingPhoneNumber client.
func NewIncomingPhoneNumberClient(client *twilio.Client) *IncomingPhoneNumberClient {
	pn := new(IncomingPhoneNumberClient)
	pn.client = client
	pn.serviceURL = fmt.Sprintf("https://api.%s/2010-04-01", pn.client.BaseURL)

	return pn
}

// Create creates a new IncomingPhoneNumber.
func (c IncomingPhoneNumberClient) Create(params *IncomingPhoneNumberParams) (*IncomingPhoneNumber, error) {
	uri := fmt.Sprintf("%s/Accounts/%s/IncomingPhoneNumbers.json", c.serviceURL, c.client.AccountSid)

	resp, err := c.client.Post(uri, params)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	ipn := &IncomingPhoneNumber{}
	if decodeErr := json.NewDecoder(resp.Body).Decode(ipn); decodeErr != nil {
		return nil, decodeErr
	}

	return ipn, err
}

// Read returns the details of an IncomingPhoneNumber.
func (c IncomingPhoneNumberClient) Read(sid string) (*IncomingPhoneNumber, error) {
	uri := fmt.Sprintf("%s/Accounts/%s/IncomingPhoneNumbers/%s.json", c.serviceURL, c.client.AccountSid, sid)

	resp, err := c.client.Get(uri, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	ipn := &IncomingPhoneNumber{}
	if decodeErr := json.NewDecoder(resp.Body).Decode(ipn); decodeErr != nil {
		return nil, decodeErr
	}

	return ipn, err
}

// Update updates an IncomingPhoneNumber.
func (c IncomingPhoneNumberClient) Update(sid string, data IncomingPhoneNumberParams) (*IncomingPhoneNumber, error) {
	uri := fmt.Sprintf("%s/Accounts/%s/IncomingPhoneNumbers/%s.json", c.serviceURL, c.client.AccountSid, sid)

	resp, err := c.client.Post(uri, data)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	ipn := &IncomingPhoneNumber{}
	if decodeErr := json.NewDecoder(resp.Body).Decode(ipn); decodeErr != nil {
		return nil, decodeErr
	}

	return ipn, err
}

// Delete releases an existing IncomingPhoneNumber.
func (c IncomingPhoneNumberClient) Delete(sid string) error {
	uri := fmt.Sprintf("%s/Accounts/%s/IncomingPhoneNumbers/%s.json", c.serviceURL, c.client.AccountSid, sid)

	resp, err := c.client.Delete(uri)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return err
}
<commit_msg>chore: modify Incoming Phone Number CRUD ops to take parameters as pointers (#19)<commit_after>package twilio

import (
	"encoding/json"
	"fmt"

	twilio "github.com/twilio/twilio-go/internal"
)

// IncomingPhoneNumber represents a Twilio phone number provisioned from Twilio, ported or hosted to Twilio.
// See: https://www.twilio.com/docs/phone-numbers/api/incomingphonenumber-resource
type IncomingPhoneNumber struct {
	Beta                 bool            `json:"beta"`
	VoiceCallerIDLookup  bool            `json:"voice_caller_id_lookup"`
	AccountSid           string          `json:"account_sid"`
	AddressSid           string          `json:"address_sid"`
	AddressRequirements  string          `json:"address_requirements"`
	APIVersion           string          `json:"api_version"`
	Capabilities         map[string]bool `json:"capabilities"`
	DateCreated          string          `json:"date_created"`
	DateUpdated          string          `json:"date_updated"`
	FriendlyName         string          `json:"friendly_name"`
	IdentitySid          string          `json:"identity_sid"`
	PhoneNumber          string          `json:"phone_number"`
	Origin               string          `json:"origin"`
	Sid                  string          `json:"sid"`
	SMSApplicationSid    string          `json:"sms_application_sid"`
	SMSFallbackMethod    string          `json:"sms_fallback_method"`
	SMSFallbackURL       string          `json:"sms_fallback_url"`
	SMSMethod            string          `json:"sms_method"`
	SMSURL               string          `json:"sms_url"`
	StatusCallback       string          `json:"status_callback"`
	StatusCallbackMethod string          `json:"status_callback_method"`
	TrunkSid             string          `json:"trunk_sid"`
	URI                  string          `json:"uri"`
	VoiceApplicationSid  string          `json:"voice_application_sid"`
	VoiceFallbackMethod  string          `json:"voice_fallback_method"`
	VoiceFallbackURL     string          `json:"voice_fallback_url"`
	VoiceMethod          string          `json:"voice_method"`
	VoiceURL             string          `json:"voice_url"`
	EmergencyStatus      string          `json:"emergency_status"`
	EmergencyAddressSid  string          `json:"emergency_address_sid"`
	BundleSid            string          `json:"bundle_sid"`
}

// IncomingPhoneNumberParams is the set of parameters that can
// be used when creating or updating an Incoming Phone Number.
type IncomingPhoneNumberParams struct {
	AccountSid           string `url:"AccountSid,omitempty"`
	APIVersion           string `url:"ApiVersion,omitempty"`
	FriendlyName         string `url:"FriendlyName,omitempty"`
	SMSApplicationSid    string `url:"SmsApplicationSid,omitempty"`
	SMSFallbackMethod    string `url:"SmsApplicationMethod,omitempty"`
	PhoneNumber          string `url:"PhoneNumber,omitempty"`
	AreaCode             string `url:"AreaCode,omitempty"`
	SMSFallbackURL       string `url:"SmsFallbackUrl,omitempty"`
	SMSMethod            string `url:"SmsMethod,omitempty"`
	SMSURL               string `url:"SmsUrl,omitempty"`
	StatusCallback       string `url:"StatusCallback,omitempty"`
	AddressSid           string `url:"AddressSid,omitempty"`
	StatusCallbackMethod string `url:"StatusCallbackMethod,omitempty"`
	VoiceApplicationSid  string `url:"VoiceApplicationSid,omitempty"`
	VoiceCallerIDLookup  bool   `url:"VoiceCallerIdLookup,omitempty"`
	VoiceFallbackMethod  string `url:"VoiceFallbackMethod,omitempty"`
	VoiceFallbackURL     string `url:"VoiceFallbackUrl,omitempty"`
	VoiceMethod          string `url:"VoiceMethod,omitempty"`
	VoiceURL             string `url:"VoiceUrl,omitempty"`
	VoiceReceiveMode     string `url:"VoiceReceiveMode,omitempty"`
	EmergencyStatus      string `url:"EmergencyStatus,omitempty"`
	EmergencyAddressSid  string `url:"EmergencyAddressSid,omitempty"`
	BundleSid            string `url:"BundleSid,omitempty"`
	IdentitySid          string `url:"IdentitySid,omitempty"`
	TrunkSid             string `url:"TrunkSid,omitempty"`
}

// IncomingPhoneNumberClient is the entrypoint for the Incoming Phone Number resource.
// See: https://www.twilio.com/docs/phone-numbers/api/incomingphonenumber-resource
type IncomingPhoneNumberClient struct {
	client     *twilio.Client
	serviceURL string
}

// NewIncomingPhoneNumberClient constructs a new IncomingPhoneNumber client.
func NewIncomingPhoneNumberClient(client *twilio.Client) *IncomingPhoneNumberClient {
	pn := new(IncomingPhoneNumberClient)
	pn.client = client
	pn.serviceURL = fmt.Sprintf("https://api.%s/2010-04-01", pn.client.BaseURL)

	return pn
}

// Create creates a new IncomingPhoneNumber.
func (c IncomingPhoneNumberClient) Create(params *IncomingPhoneNumberParams) (*IncomingPhoneNumber, error) {
	uri := fmt.Sprintf("%s/Accounts/%s/IncomingPhoneNumbers.json", c.serviceURL, c.client.AccountSid)

	resp, err := c.client.Post(uri, params)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	ipn := &IncomingPhoneNumber{}
	if decodeErr := json.NewDecoder(resp.Body).Decode(ipn); decodeErr != nil {
		return nil, decodeErr
	}

	return ipn, err
}

// Read returns the details of an IncomingPhoneNumber.
func (c IncomingPhoneNumberClient) Read(sid string) (*IncomingPhoneNumber, error) {
	uri := fmt.Sprintf("%s/Accounts/%s/IncomingPhoneNumbers/%s.json", c.serviceURL, c.client.AccountSid, sid)

	resp, err := c.client.Get(uri, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	ipn := &IncomingPhoneNumber{}
	if decodeErr := json.NewDecoder(resp.Body).Decode(ipn); decodeErr != nil {
		return nil, decodeErr
	}

	return ipn, err
}

// Update updates an IncomingPhoneNumber.
func (c IncomingPhoneNumberClient) Update(sid string, params *IncomingPhoneNumberParams) (*IncomingPhoneNumber, error) {
	uri := fmt.Sprintf("%s/Accounts/%s/IncomingPhoneNumbers/%s.json", c.serviceURL, c.client.AccountSid, sid)

	resp, err := c.client.Post(uri, params)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	ipn := &IncomingPhoneNumber{}
	if decodeErr := json.NewDecoder(resp.Body).Decode(ipn); decodeErr != nil {
		return nil, decodeErr
	}

	return ipn, err
}

// Delete releases an existing IncomingPhoneNumber.
func (c IncomingPhoneNumberClient) Delete(sid string) error {
	uri := fmt.Sprintf("%s/Accounts/%s/IncomingPhoneNumbers/%s.json", c.serviceURL, c.client.AccountSid, sid)

	resp, err := c.client.Delete(uri)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return err
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/tueftler/doget/dockerfile"
	"io"
	"os"
)

var (
	input  string
	output string

	parser   *dockerfile.Parser
	commands = map[string]func(out io.Writer, file *dockerfile.Dockerfile) error{
		"transform": transform,
		"dump":      dump,
	}
)

func init() {
	flag.StringVar(&input, "in", "Dockerfile.in", "Use given dockerfile")
	flag.StringVar(&output, "out", "-", "Output. Use - for standard output")

	parser = dockerfile.NewParser().Extend("INCLUDE", include)
}

func parse(input string, file *dockerfile.Dockerfile) error {
	return parser.ParseFile(input, file)
}

func open(output string) (io.Writer, error) {
	if output == "-" {
		return os.Stdout, nil
	} else {
		return os.Create(output)
	}
}

func main() {
	flag.Parse()

	out, err := open(output)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(2)
	}

	command := "transform"
	if len(flag.Args()) > 0 {
		command = flag.Args()[0]
	}

	var file dockerfile.Dockerfile
	if err := parse(input, &file); err != nil {
		fmt.Println(err.Error())
		os.Exit(2)
	}

	if delegate, ok := commands[command]; ok {
		if err := delegate(out, &file); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	} else {
		fmt.Printf("Cannot handle command `%s`\n", command)
		os.Exit(2)
	}
}
<commit_msg>Support "-" for -in, too<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/tueftler/doget/dockerfile"
	"io"
	"os"
)

var (
	input  string
	output string

	parser   *dockerfile.Parser
	commands = map[string]func(out io.Writer, file *dockerfile.Dockerfile) error{
		"transform": transform,
		"dump":      dump,
	}
)

func init() {
	flag.StringVar(&input, "in", "Dockerfile.in", "Input. Use - for standard input")
	flag.StringVar(&output, "out", "-", "Output. Use - for standard output")

	parser = dockerfile.NewParser().Extend("INCLUDE", include)
}

func parse(input string, file *dockerfile.Dockerfile) error {
	if input == "-" {
		return parser.Parse(os.Stdin, file, "<stdin>")
	} else {
		return parser.ParseFile(input, file)
	}
}

func open(output string) (io.Writer, error) {
	if output == "-" {
		return os.Stdout, nil
	} else {
		return os.Create(output)
	}
}

func main() {
	flag.Parse()

	out, err := open(output)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(2)
	}

	command := "transform"
	if len(flag.Args()) > 0 {
		command = flag.Args()[0]
	}

	var file dockerfile.Dockerfile
	if err := parse(input, &file); err != nil {
		fmt.Println(err.Error())
		os.Exit(2)
	}

	if delegate, ok := commands[command]; ok {
		if err := delegate(out, &file); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	} else {
		fmt.Printf("Cannot handle command `%s`\n", command)
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>package mangadownloader

import (
	"code.google.com/p/go-html-transform/css/selector"
	"errors"
	"fmt"
	"net/url"
	"regexp"
	"strconv"
)

const (
	serviceMangaReaderDomain            = "www.mangareader.net"
	serviceMangaReaderPathMangas        = "/alphabetical"
	serviceMangaReaderChapterDigitCount = 4
)

var (
	serviceMangaReaderUrlBase   *url.URL
	serviceMangaReaderUrlMangas *url.URL

	serviceMangaReaderHtmlSelectorIdentifyManga, _   = selector.Selector("#chapterlist")
	serviceMangaReaderHtmlSelectorIdentifyChapter, _ = selector.Selector("#pageMenu")
	serviceMangaReaderHtmlSelectorMangas, _          = selector.Selector("ul.series_alpha a")
	serviceMangaReaderHtmlSelectorMangaName, _       = selector.Selector("h2.aname")
	serviceMangaReaderHtmlSelectorMangaChapters, _   = selector.Selector("#chapterlist a")
	serviceMangaReaderHtmlSelectorChapterName, _     = selector.Selector("#mangainfo h1")
	serviceMangaReaderHtmlSelectorChapterPages, _    = selector.Selector("#pageMenu option")
	serviceMangaReaderHtmlSelectorPageImage, _       = selector.Selector("#img")

	serviceMangaReaderRegexpChapterName, _ = regexp.Compile("^.* (\\d*)$")

	serviceMangaReaderFormatChapter = "%0" + strconv.Itoa(serviceMangaReaderChapterDigitCount) + "d"
)

func init() {
	serviceMangaReaderUrlBase = new(url.URL)
	serviceMangaReaderUrlBase.Scheme = "http"
	serviceMangaReaderUrlBase.Host = serviceMangaReaderDomain

	serviceMangaReaderUrlMangas = urlCopy(serviceMangaReaderUrlBase)
	serviceMangaReaderUrlMangas.Path = serviceMangaReaderPathMangas
}

type MangaReaderService struct {
	Md *MangaDownloader
}

func (service *MangaReaderService) Supports(u *url.URL) bool {
	return u.Host == serviceMangaReaderDomain
}

func (service *MangaReaderService) Identify(u *url.URL) (interface{}, error) {
	if !service.Supports(u) {
		return nil, errors.New("Not supported")
	}

	rootNode, err := service.Md.HttpGetHtml(u)
	if err != nil {
		return nil, err
	}

	identifyMangaNodes := serviceMangaReaderHtmlSelectorIdentifyManga.Find(rootNode)
	if len(identifyMangaNodes) == 1 {
		manga := &Manga{
			Url:     u,
			Service: service,
		}
		return manga, nil
	}

	identifyChapterNodes := serviceMangaReaderHtmlSelectorIdentifyChapter.Find(rootNode)
	if len(identifyChapterNodes) == 1 {
		chapter := &Chapter{
			Url:     u,
			Service: service,
		}
		return chapter, nil
	}

	return nil, errors.New("Unknown url")
}

func (service *MangaReaderService) Mangas() ([]*Manga, error) {
	rootNode, err := service.Md.HttpGetHtml(serviceMangaReaderUrlMangas)
	if err != nil {
		return nil, err
	}

	linkNodes := serviceMangaReaderHtmlSelectorMangas.Find(rootNode)

	mangas := make([]*Manga, 0, len(linkNodes))
	for _, linkNode := range linkNodes {
		mangaUrl := urlCopy(serviceMangaReaderUrlBase)
		mangaUrl.Path = htmlGetNodeAttribute(linkNode, "href")
		manga := &Manga{
			Url:     mangaUrl,
			Service: service,
		}
		mangas = append(mangas, manga)
	}

	return mangas, nil
}

func (service *MangaReaderService) MangaName(manga *Manga) (string, error) {
	rootNode, err := service.Md.HttpGetHtml(manga.Url)
	if err != nil {
		return "", err
	}

	nameNodes := serviceMangaReaderHtmlSelectorMangaName.Find(rootNode)
	if len(nameNodes) != 1 {
		return "", errors.New("Name node not found")
	}
	nameNode := nameNodes[0]
	if nameNode.FirstChild == nil {
		return "", errors.New("Name text node not found")
	}
	nameTextNode := nameNode.FirstChild
	name := nameTextNode.Data

	return name, nil
}

func (service *MangaReaderService) MangaChapters(manga *Manga) ([]*Chapter, error) {
	rootNode, err := service.Md.HttpGetHtml(manga.Url)
	if err != nil {
		return nil, err
	}

	linkNodes := serviceMangaReaderHtmlSelectorMangaChapters.Find(rootNode)

	chapters := make([]*Chapter, 0, len(linkNodes))
	for _, linkNode := range linkNodes {
		chapterUrl := urlCopy(serviceMangaReaderUrlBase)
		chapterUrl.Path = htmlGetNodeAttribute(linkNode, "href")
		chapter := &Chapter{
			Url:     chapterUrl,
			Service: service,
		}
		chapters = append(chapters, chapter)
	}

	return chapters, nil
}

func (service *MangaReaderService) ChapterName(chapter *Chapter) (string, error) {
	rootNode, err := service.Md.HttpGetHtml(chapter.Url)
	if err != nil {
		return "", err
	}

	nameNodes := serviceMangaReaderHtmlSelectorChapterName.Find(rootNode)
	if len(nameNodes) != 1 {
		return "", errors.New("Name node not found")
	}
	nameNode := nameNodes[0]
	if nameNode.FirstChild == nil {
		return "", errors.New("Name text node not found")
	}
	nameTextNode := nameNode.FirstChild
	name := nameTextNode.Data
	matches := serviceMangaReaderRegexpChapterName.FindStringSubmatch(name)
	if matches == nil || len(matches) != 2 {
		return "", errors.New("Invalid name format")
	}
	name = matches[1]
	nameInt, err := strconv.Atoi(name)
	if err != nil {
		return "", err
	}
	name = fmt.Sprintf(serviceMangaReaderFormatChapter, nameInt)

	return name, nil
}

func (service *MangaReaderService) ChapterPages(chapter *Chapter) ([]*Page, error) {
	rootNode, err := service.Md.HttpGetHtml(chapter.Url)
	if err != nil {
		return nil, err
	}

	optionNodes := serviceMangaReaderHtmlSelectorChapterPages.Find(rootNode)

	pages := make([]*Page, 0, len(optionNodes))
	for _, optionNode := range optionNodes {
		pageUrl := urlCopy(serviceMangaReaderUrlBase)
		pageUrl.Path = htmlGetNodeAttribute(optionNode, "value")
		page := &Page{
			Url:     pageUrl,
			Service: service,
		}
		pages = append(pages, page)
	}

	return pages, nil
}

func (service *MangaReaderService) PageImageUrl(page *Page) (*url.URL, error) {
	rootNode, err := service.Md.HttpGetHtml(page.Url)
	if err != nil {
		return nil, err
	}

	imgNodes := serviceMangaReaderHtmlSelectorPageImage.Find(rootNode)
	if len(imgNodes) < 1 {
		return nil, errors.New("Image node not found")
	}
	imgNode := imgNodes[0]

	imageUrl, err := url.Parse(htmlGetNodeAttribute(imgNode, "src"))
	if err != nil {
		return nil, err
	}

	return imageUrl, nil
}

func (service *MangaReaderService) String() string {
	return "MangaReaderService"
}
<commit_msg>Small fix<commit_after>package mangadownloader

import (
	"code.google.com/p/go-html-transform/css/selector"
	"errors"
	"fmt"
	"net/url"
	"regexp"
	"strconv"
)

const (
	serviceMangaReaderDomain            = "www.mangareader.net"
	serviceMangaReaderPathMangas        = "/alphabetical"
	serviceMangaReaderChapterDigitCount = 4
)

var (
	serviceMangaReaderUrlBase   *url.URL
	serviceMangaReaderUrlMangas *url.URL

	serviceMangaReaderHtmlSelectorIdentifyManga, _   = selector.Selector("#chapterlist")
	serviceMangaReaderHtmlSelectorIdentifyChapter, _ = selector.Selector("#pageMenu")
	serviceMangaReaderHtmlSelectorMangas, _          = selector.Selector("ul.series_alpha a")
	serviceMangaReaderHtmlSelectorMangaName, _       = selector.Selector("h2.aname")
	serviceMangaReaderHtmlSelectorMangaChapters, _   = selector.Selector("#chapterlist a")
	serviceMangaReaderHtmlSelectorChapterName, _     = selector.Selector("#mangainfo h1")
	serviceMangaReaderHtmlSelectorChapterPages, _    = selector.Selector("#pageMenu option")
	serviceMangaReaderHtmlSelectorPageImage, _       = selector.Selector("#img")

	serviceMangaReaderRegexpChapterName, _ = regexp.Compile("^.* (\\d*)$")

	serviceMangaReaderFormatChapter = "%0" + strconv.Itoa(serviceMangaReaderChapterDigitCount) + "d"
)

func init() {
	serviceMangaReaderUrlBase = new(url.URL)
	serviceMangaReaderUrlBase.Scheme = "http"
	serviceMangaReaderUrlBase.Host = serviceMangaReaderDomain

	serviceMangaReaderUrlMangas = urlCopy(serviceMangaReaderUrlBase)
	serviceMangaReaderUrlMangas.Path = serviceMangaReaderPathMangas
}

type MangaReaderService struct {
	Md *MangaDownloader
}

func (service *MangaReaderService) Supports(u *url.URL) bool {
	return u.Host == serviceMangaReaderDomain
}

func (service *MangaReaderService) Identify(u *url.URL) (interface{}, error) {
	if !service.Supports(u) {
		return nil, errors.New("Not supported")
	}

	rootNode, err := service.Md.HttpGetHtml(u)
	if err != nil {
		return nil, err
	}

	identifyMangaNodes := serviceMangaReaderHtmlSelectorIdentifyManga.Find(rootNode)
	if len(identifyMangaNodes) == 1 {
		manga := &Manga{
			Url:     u,
			Service: service,
		}
		return manga, nil
	}

	identifyChapterNodes := serviceMangaReaderHtmlSelectorIdentifyChapter.Find(rootNode)
	if len(identifyChapterNodes) == 1 {
		chapter := &Chapter{
			Url:     u,
			Service: service,
		}
		return chapter, nil
	}

	return nil, errors.New("Unknown url")
}

func (service *MangaReaderService) Mangas() ([]*Manga, error) {
	rootNode, err := service.Md.HttpGetHtml(serviceMangaReaderUrlMangas)
	if err != nil {
		return nil, err
	}

	linkNodes := serviceMangaReaderHtmlSelectorMangas.Find(rootNode)

	mangas := make([]*Manga, 0, len(linkNodes))
	for _, linkNode := range linkNodes {
		mangaUrl := urlCopy(serviceMangaReaderUrlBase)
		mangaUrl.Path = htmlGetNodeAttribute(linkNode, "href")
		manga := &Manga{
			Url:     mangaUrl,
			Service: service,
		}
		mangas = append(mangas, manga)
	}

	return mangas, nil
}

func (service *MangaReaderService) MangaName(manga *Manga) (string, error) {
	rootNode, err := service.Md.HttpGetHtml(manga.Url)
	if err != nil {
		return "", err
	}

	nameNodes := serviceMangaReaderHtmlSelectorMangaName.Find(rootNode)
	if len(nameNodes) != 1 {
		return "", errors.New("Name node not found")
	}
	nameNode := nameNodes[0]
	if nameNode.FirstChild == nil {
		return "", errors.New("Name text node not found")
	}
	nameTextNode := nameNode.FirstChild
	name := nameTextNode.Data

	return name, nil
}

func (service *MangaReaderService) MangaChapters(manga *Manga) ([]*Chapter, error) {
	rootNode, err := service.Md.HttpGetHtml(manga.Url)
	if err != nil {
		return nil, err
	}

	linkNodes := serviceMangaReaderHtmlSelectorMangaChapters.Find(rootNode)

	chapters := make([]*Chapter, 0, len(linkNodes))
	for _, linkNode := range linkNodes {
		chapterUrl := urlCopy(serviceMangaReaderUrlBase)
		chapterUrl.Path = htmlGetNodeAttribute(linkNode, "href")
		chapter := &Chapter{
			Url:     chapterUrl,
			Service: service,
		}
		chapters = append(chapters, chapter)
	}

	return chapters, nil
}

func (service *MangaReaderService) ChapterName(chapter *Chapter) (string, error) {
	rootNode, err := service.Md.HttpGetHtml(chapter.Url)
	if err != nil {
		return "", err
	}

	nameNodes := serviceMangaReaderHtmlSelectorChapterName.Find(rootNode)
	if len(nameNodes) != 1 {
		return "", errors.New("Name node not found")
	}
	nameNode := nameNodes[0]
	if nameNode.FirstChild == nil {
		return "", errors.New("Name text node not found")
	}
	nameTextNode := nameNode.FirstChild
	name := nameTextNode.Data
	matches := serviceMangaReaderRegexpChapterName.FindStringSubmatch(name)
	if matches == nil || len(matches) != 2 {
		return "", errors.New("Invalid name format")
	}
	name = matches[1]
	nameInt, err := strconv.Atoi(name)
	if err != nil {
		return "", err
	}
	name = fmt.Sprintf(serviceMangaReaderFormatChapter, nameInt)

	return name, nil
}

func (service *MangaReaderService) ChapterPages(chapter *Chapter) ([]*Page, error) {
	rootNode, err := service.Md.HttpGetHtml(chapter.Url)
	if err != nil {
		return nil, err
	}

	optionNodes := serviceMangaReaderHtmlSelectorChapterPages.Find(rootNode)

	pages := make([]*Page, 0, len(optionNodes))
	for _, optionNode := range optionNodes {
		pageUrl := urlCopy(serviceMangaReaderUrlBase)
		pageUrl.Path = htmlGetNodeAttribute(optionNode, "value")
		page := &Page{
			Url:     pageUrl,
			Service: service,
		}
		pages = append(pages, page)
	}

	return pages, nil
}

func (service *MangaReaderService) PageImageUrl(page *Page) (*url.URL, error) {
	rootNode, err := service.Md.HttpGetHtml(page.Url)
	if err != nil {
		return nil, err
	}

	imgNodes := serviceMangaReaderHtmlSelectorPageImage.Find(rootNode)
	if len(imgNodes) != 1 {
		return nil, errors.New("Image node not found")
	}
	imgNode := imgNodes[0]

	imageUrl, err := url.Parse(htmlGetNodeAttribute(imgNode, "src"))
	if err != nil {
		return nil, err
	}

	return imageUrl, nil
}

func (service *MangaReaderService) String() string {
	return "MangaReaderService"
}
<|endoftext|>