<commit_before>package deps

import (
	"encoding/json"
	"errors"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"runtime"
	"sort"

	"github.com/fatih/set"
)

const (
	DepsGoPath    = "gopackage"
	gopackageFile = "gopackage.json"
)

type Deps struct {
	// Packages is written as the importPath of a given package(s).
	Packages []string `json:"packages"`

	// GoVersion defines the Go version needed at least as a minumum.
	GoVersion string `json:"goVersion"`

	// Dependencies defines the dependency of the given Packages. If multiple
	// packages are defined, each dependency will point to the HEAD unless
	// changed manually.
	Dependencies []string `json:"dependencies"`

	// BuildGoPath is used to fetch dependencies of the given Packages
	BuildGoPath string

	// currentGoPath, is taken from current GOPATH environment variable
	currentGoPath string
}

// LoadDeps returns a new Deps struct with the given packages. It founds the
// dependencies and populates the fields in Deps. After LoadDeps one can use
// InstallDeps() to install/build the binary for the given pkg or use
// GetDeps() to download and vendorize the dependencies of the given pkg.
func LoadDeps(pkgs ...string) (*Deps, error) {
	packages, err := listPackages(pkgs...)
	if err != nil {
		fmt.Println(err)
	}

	// get all dependencies for applications defined above
	dependencies := set.New()
	for _, pkg := range packages {
		for _, imp := range pkg.Deps {
			dependencies.Add(imp)
		}
	}

	// clean up deps
	// 1. remove std lib paths
	context := build.Default
	thirdPartyDeps := make([]string, 0)

	for _, importPath := range dependencies.StringSlice() {
		p, err := context.Import(importPath, ".", build.AllowBinary)
		if err != nil {
			log.Println(err)
		}

		// do not include std lib
		if p.Goroot {
			continue
		}

		thirdPartyDeps = append(thirdPartyDeps, importPath)
	}

	sort.Strings(thirdPartyDeps)

	deps := &Deps{
		Packages:     pkgs,
		Dependencies: thirdPartyDeps,
		GoVersion:    runtime.Version(),
	}

	err = deps.populateGoPaths()
	if err != nil {
		return nil, err
	}

	return deps, nil
}

func (d *Deps) populateGoPaths() error {
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return errors.New("GOPATH is not set")
	}

	pwd, err := os.Getwd()
	if err != nil {
		return err
	}

	d.currentGoPath = gopath
	d.BuildGoPath = path.Join(pwd, DepsGoPath)
	return nil
}

// InstallDeps calls "go install" on the given packages and installs them
// to deps.BuildGoPath/pkgname
func (d *Deps) InstallDeps() error {
	if !compareGoVersions(d.GoVersion, runtime.Version()) {
		return fmt.Errorf("Go Version is not satisfied\nSystem Go Version: '%s' Expected: '%s'",
			runtime.Version(), d.GoVersion)
	}

	// expand current path
	if d.BuildGoPath != d.currentGoPath {
		os.Setenv("GOPATH", fmt.Sprintf("%s:%s", d.BuildGoPath, d.currentGoPath))
	}

	// another approach is let them building with a single gobin and then move
	// the final binaries into new directories based on the binary filename.
	for _, pkg := range d.Packages {
		pkgname := path.Base(pkg)
		binpath := fmt.Sprintf("%s/%s/", d.BuildGoPath, pkgname)

		os.MkdirAll(binpath, 0755)
		os.Setenv("GOBIN", binpath)

		args := []string{"install", pkg}
		cmd := exec.Command("go", args...)
		cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr

		err := cmd.Run()
		if err != nil {
			log.Println(err)
		}
	}

	return nil
}

func goPackagePath() string {
	pwd, err := os.Getwd()
	if err != nil {
		return ""
	}

	return path.Join(pwd, gopackageFile)
}

// WriteJSON writes the state of d into a json file, useful to restore again
// with ReadJSON().
func (d *Deps) WriteJSON() error {
	data, err := json.MarshalIndent(d, "", "  ")
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(goPackagePath(), data, 0755)
	if err != nil {
		return err
	}

	return nil
}

// ReadJSON() restores a given gopackage.json to create a new deps struct.
func ReadJson() (*Deps, error) {
	data, err := ioutil.ReadFile(goPackagePath())
	if err != nil {
		return nil, err
	}

	d := new(Deps)
	err = json.Unmarshal(data, d)
	if err != nil {
		return nil, err
	}

	err = d.populateGoPaths()
	if err != nil {
		return nil, err
	}

	return d, nil
}

// GetDeps calls "go get -d" to download all dependencies for the packages
// defined in d.
func (d *Deps) GetDeps() error {
	os.MkdirAll(d.BuildGoPath, 0755)
	os.Setenv("GOPATH", d.BuildGoPath)

	for _, pkg := range d.Dependencies {
		fmt.Println("go get", pkg)
		cmd := exec.Command("go", []string{"get", "-d", pkg}...)
		cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr

		err := cmd.Run()
		if err != nil {
			log.Println(err)
		}
	}

	return nil
}
<commit_msg>util/deps: revert gopath back to original state<commit_after>package deps

import (
	"encoding/json"
	"errors"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"runtime"
	"sort"

	"github.com/fatih/set"
)

const (
	DepsGoPath    = "gopackage"
	gopackageFile = "gopackage.json"
)

type Deps struct {
	// Packages is written as the importPath of a given package(s).
	Packages []string `json:"packages"`

	// GoVersion defines the Go version needed at least as a minumum.
	GoVersion string `json:"goVersion"`

	// Dependencies defines the dependency of the given Packages. If multiple
	// packages are defined, each dependency will point to the HEAD unless
	// changed manually.
	Dependencies []string `json:"dependencies"`

	// BuildGoPath is used to fetch dependencies of the given Packages
	BuildGoPath string

	// currentGoPath, is taken from current GOPATH environment variable
	currentGoPath string
}

// LoadDeps returns a new Deps struct with the given packages. It founds the
// dependencies and populates the fields in Deps. After LoadDeps one can use
// InstallDeps() to install/build the binary for the given pkg or use
// GetDeps() to download and vendorize the dependencies of the given pkg.
func LoadDeps(pkgs ...string) (*Deps, error) {
	packages, err := listPackages(pkgs...)
	if err != nil {
		fmt.Println(err)
	}

	// get all dependencies for applications defined above
	dependencies := set.New()
	for _, pkg := range packages {
		for _, imp := range pkg.Deps {
			dependencies.Add(imp)
		}
	}

	// clean up deps
	// 1. remove std lib paths
	context := build.Default
	thirdPartyDeps := make([]string, 0)

	for _, importPath := range dependencies.StringSlice() {
		p, err := context.Import(importPath, ".", build.AllowBinary)
		if err != nil {
			log.Println(err)
		}

		// do not include std lib
		if p.Goroot {
			continue
		}

		thirdPartyDeps = append(thirdPartyDeps, importPath)
	}

	sort.Strings(thirdPartyDeps)

	deps := &Deps{
		Packages:     pkgs,
		Dependencies: thirdPartyDeps,
		GoVersion:    runtime.Version(),
	}

	err = deps.populateGoPaths()
	if err != nil {
		return nil, err
	}

	return deps, nil
}

func (d *Deps) populateGoPaths() error {
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return errors.New("GOPATH is not set")
	}

	pwd, err := os.Getwd()
	if err != nil {
		return err
	}

	d.currentGoPath = gopath
	d.BuildGoPath = path.Join(pwd, DepsGoPath)
	return nil
}

// InstallDeps calls "go install" on the given packages and installs them
// to deps.BuildGoPath/pkgname
func (d *Deps) InstallDeps() error {
	if !compareGoVersions(d.GoVersion, runtime.Version()) {
		return fmt.Errorf("Go Version is not satisfied\nSystem Go Version: '%s' Expected: '%s'",
			runtime.Version(), d.GoVersion)
	}

	// expand current path
	if d.BuildGoPath != d.currentGoPath {
		os.Setenv("GOPATH", fmt.Sprintf("%s:%s", d.BuildGoPath, d.currentGoPath))
	}

	// revert gopath back, so that we don't mess up gopath for other applications
	defer os.Setenv("GOPATH", d.currentGoPath)

	// another approach is let them building with a single gobin and then move
	// the final binaries into new directories based on the binary filename.
	for _, pkg := range d.Packages {
		pkgname := path.Base(pkg)
		binpath := fmt.Sprintf("%s/%s/", d.BuildGoPath, pkgname)

		os.MkdirAll(binpath, 0755)
		os.Setenv("GOBIN", binpath)

		args := []string{"install", pkg}
		cmd := exec.Command("go", args...)
		cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr

		err := cmd.Run()
		if err != nil {
			log.Println(err)
		}
	}

	return nil
}

func goPackagePath() string {
	pwd, err := os.Getwd()
	if err != nil {
		return ""
	}

	return path.Join(pwd, gopackageFile)
}

// WriteJSON writes the state of d into a json file, useful to restore again
// with ReadJSON().
func (d *Deps) WriteJSON() error {
	data, err := json.MarshalIndent(d, "", "  ")
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(goPackagePath(), data, 0755)
	if err != nil {
		return err
	}

	return nil
}

// ReadJSON() restores a given gopackage.json to create a new deps struct.
func ReadJson() (*Deps, error) {
	data, err := ioutil.ReadFile(goPackagePath())
	if err != nil {
		return nil, err
	}

	d := new(Deps)
	err = json.Unmarshal(data, d)
	if err != nil {
		return nil, err
	}

	err = d.populateGoPaths()
	if err != nil {
		return nil, err
	}

	return d, nil
}

// GetDeps calls "go get -d" to download all dependencies for the packages
// defined in d.
func (d *Deps) GetDeps() error {
	os.MkdirAll(d.BuildGoPath, 0755)
	os.Setenv("GOPATH", d.BuildGoPath)

	for _, pkg := range d.Dependencies {
		fmt.Println("go get", pkg)
		cmd := exec.Command("go", []string{"get", "-d", pkg}...)
		cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr

		err := cmd.Run()
		if err != nil {
			log.Println(err)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Keithley RF Switch driver

package keithley

import (
	"fmt"

	vi "github.com/jpoirier/visa"
)

type Keithley struct {
	vi.Visa
	instr vi.Object
}

// RF_Switch Class - works with Keithley S46 RF Switch.
// Eight SPDT unterminated coaxial relays (2-pole) and four multi-pole unterminated coaxial relays.
// Relay A, multipole = Chan  1...6
// Relay B, multipole = Chan  7..12
// Relay C, multipole = Chan 13..18
// Relay D, multipole = Chan 19..24
// Relay 1..8, 2-pole = Chan 25..32
// Caution: Do not close more than one RF path per multiport switch.

// OpenGpib Opens a session to the specified resource.
func (k *Keithley) OpenGpib(rm vi.Session, ctrl, addr, mode, timeout uint32) (status vi.Status) {
	name := fmt.Sprintf("GPIB%d::%d", ctrl, ddr)
	k.instr, status = rm.Open(name, mode, timeout)
	return
}

// OpenTcp Opens a session to the specified resource.
func (k *Keithley) OpenTcp(rm vi.Session, ip string, mode, timeout uint32) (status vi.Status) {
	name := fmt.Sprintf("TCPIP::%s::INSTR", addr)
	k.instr, status = rm.Open(name, mode, timeout)
	return
}

// Reset Resets the switch unit.
func (k *Keithley) Reset() (status vi.Status) {
	b := []byte("*RST")
	_, status = k.instr.Write(b, uint32(len(b)))
	return
}

// OpenChan Opens the specified channel. Where an open channel does not
// allow a signal to pass through.
func (k *Keithley) OpenChan(ch uint32) (status vi.Status) {
	b := fmt.Sprintf("OPEN (@%d)", ch)
	_, status = k.instr.Write([]byte(b), uint32(len(b)))
	return
}

// OpenAllChans Opens all channels.
func (k *Keithley) OpenAllChans() (status vi.Status) {
	b := []byte("OPEN:ALL")
	_, status = k.instr.Write(b, uint32(len(b)))
	return
}

// CloseChan Closes the specified channel. Note, All other channels on relay
// are opened first to prevent multiple closed relays leading to damage.
func (k *Keithley) CloseChan(ch uint32) (status vi.Status) {
	// Determine if ch is part of 2-port relay or multi-port relay (A..D)
	// Multi-Port Relay, A..D
	if ch > 0 && ch < 25 {
		// Open all ports on this relay
		for i := 1; i < 7; i++ {
			c := int((ch-1)/6)*6 + i
			k.OpenChan(uint32(c))
		}
	}
	b := fmt.Sprintf("CLOSE (@%d)", ch)
	_, status = k.instr.Write([]byte(b), uint32(len(b)))
	return
}

// ClosedChanList Returns a list of closed channels.
func (k *Keithley) ClosedChanList(ch uint32) (list string, status vi.Status) {
	// Returns list of closed channels.
	// RF Switch returns format '(@1,2,3)'.
	// If no channels closed, switch returns '(@)'.

	b := []byte("CLOSE?")
	_, status = k.instr.Write(b, uint32(len(b)))
	if status < vi.SUCCESS {
		return
	}
	buffer, _, status := k.instr.Read(100)
	if status < vi.SUCCESS {
		return
	} else {
		return string(buffer), status
	}
}

// if len(strClosedChans) > 3:  # Len always greater than 3 if channel in the list.
//     if strClosedChans.find(",") >= 0:
//         closedChans = strClosedChans[2:len(strClosedChans)-1].split(",")
//     else:
//         closedChans.append(strClosedChans[2:len(strClosedChans)-1])
<commit_msg>keithley driver fixes<commit_after>// Keithley RF Switch driver

package keithley

import (
	"fmt"

	vi "github.com/jpoirier/visa"
)

type Keithley struct {
	vi.Visa
	instr vi.Object
}

// RF_Switch Class - works with Keithley S46 RF Switch.
// Eight SPDT unterminated coaxial relays (2-pole) and four multi-pole unterminated coaxial relays.
// Relay A, multipole = Chan  1...6
// Relay B, multipole = Chan  7..12
// Relay C, multipole = Chan 13..18
// Relay D, multipole = Chan 19..24
// Relay 1..8, 2-pole = Chan 25..32
// Caution: Do not close more than one RF path per multiport switch.

// OpenGpib Opens a session to the specified resource.
func (k *Keithley) OpenGpib(rm vi.Session, ctrl, addr, mode, timeout uint32) (status vi.Status) {
	name := fmt.Sprintf("GPIB%d::%d", ctrl, addr)
	k.instr, status = rm.Open(name, mode, timeout)
	return
}

// OpenTcp Opens a session to the specified resource.
func (k *Keithley) OpenTcp(rm vi.Session, ip string, mode, timeout uint32) (status vi.Status) {
	name := fmt.Sprintf("TCPIP::%s::INSTR", ip)
	k.instr, status = rm.Open(name, mode, timeout)
	return
}

// Reset Resets the switch unit.
func (k *Keithley) Reset() (status vi.Status) {
	b := []byte("*RST")
	_, status = k.instr.Write(b, uint32(len(b)))
	return
}

// OpenChan Opens the specified channel. Where an open channel does not
// allow a signal to pass through.
func (k *Keithley) OpenChan(ch uint32) (status vi.Status) {
	b := fmt.Sprintf("OPEN (@%d)", ch)
	_, status = k.instr.Write([]byte(b), uint32(len(b)))
	return
}

// OpenAllChans Opens all channels.
func (k *Keithley) OpenAllChans() (status vi.Status) {
	b := []byte("OPEN:ALL")
	_, status = k.instr.Write(b, uint32(len(b)))
	return
}

// CloseChan Closes the specified channel. Note, All other channels on relay
// are opened first to prevent multiple closed relays leading to damage.
func (k *Keithley) CloseChan(ch uint32) (status vi.Status) {
	// Determine if ch is part of 2-port relay or multi-port relay (A..D)
	// Multi-Port Relay, A..D
	if ch > 0 && ch < 25 {
		// Open all ports on this relay
		for i := 1; i < 7; i++ {
			c := int((ch-1)/6)*6 + i
			k.OpenChan(uint32(c))
		}
	}
	b := fmt.Sprintf("CLOSE (@%d)", ch)
	_, status = k.instr.Write([]byte(b), uint32(len(b)))
	return
}

// ClosedChanList Returns a list of closed channels.
func (k *Keithley) ClosedChanList(ch uint32) (list string, status vi.Status) {
	// Returns list of closed channels.
	// RF Switch returns format '(@1,2,3)'.
	// If no channels closed, switch returns '(@)'.

	b := []byte("CLOSE?")
	_, status = k.instr.Write(b, uint32(len(b)))
	if status < vi.SUCCESS {
		return
	}
	buffer, _, status := k.instr.Read(100)
	if status < vi.SUCCESS {
		return
	} else {
		return string(buffer), status
	}
}

// if len(strClosedChans) > 3:  # Len always greater than 3 if channel in the list.
//     if strClosedChans.find(",") >= 0:
//         closedChans = strClosedChans[2:len(strClosedChans)-1].split(",")
//     else:
//         closedChans.append(strClosedChans[2:len(strClosedChans)-1])
<|endoftext|>


<commit_before>package runc

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/inconshreveable/log15"
	"github.com/polydawn/gosh"
	"github.com/spacemonkeygo/errors"
	"github.com/spacemonkeygo/errors/try"

	"polydawn.net/repeatr/def"
	"polydawn.net/repeatr/executor"
	"polydawn.net/repeatr/executor/basicjob"
	"polydawn.net/repeatr/executor/util"
	"polydawn.net/repeatr/io"
	"polydawn.net/repeatr/io/assets"
	"polydawn.net/repeatr/lib/flak"
	"polydawn.net/repeatr/lib/streamer"
)

// interface assertion
var _ executor.Executor = &Executor{}

type Executor struct {
	workspacePath string
}

func (e *Executor) Configure(workspacePath string) {
	e.workspacePath = workspacePath
}

func (e *Executor) Start(f def.Formula, id def.JobID, stdin io.Reader, journal io.Writer) def.Job {
	// TODO this function sig and its interface are long overdue for an aggressive refactor.
	// - `journal` is Rong.  The streams mux should be accessible after this function's scope!
	//   - either that or it's time to get cracking on saving the stream mux as an output
	// - `journal` should still be a thing, but it should be a logger.
	// - All these other values should move along in a `Job` struct
	//   - `BasicJob` sorta started, but is drunk:
	//      - if we're gonna have that, it's incomplete on the inputs
	//      - for some reason it mixes in responsibility for waiting for some of the ouputs
	//      - that use of channels and public fields is stupidly indefensive
	//   - The current `Job` interface is in the wrong package
	// - almost all of the scopes in these functions is wrong
	//   - they should be realigned until they actually assist the defers and cleanups
	//     - e.g. withErrorCapture, withJobWorkPath, withFilesystems, etc

	// Prepare the forumla for execution on this host
	def.ValidateAll(&f)

	job := basicjob.New(id)
	jobReady := make(chan struct{})

	go func() {
		// Run the formula in a temporary directory
		flak.WithDir(func(dir string) {

			// spool our output to a muxed stream
			var strm streamer.Mux
			strm = streamer.CborFileMux(filepath.Join(dir, "log"))
			outS := strm.Appender(1)
			errS := strm.Appender(2)
			job.Streams = strm
			defer func() {
				// Regardless of how the job ends (or even if it fails the remaining setup), output streams must be terminated.
				outS.Close()
				errS.Close()
			}()

			// Job is ready to stream process output
			close(jobReady)

			// Set up a logger.  Tag all messages with this jobid.
			logger := log15.New(log15.Ctx{"JobID": id})
			logger.SetHandler(log15.StreamHandler(journal, log15.TerminalFormat()))

			job.Result = e.Run(f, job, dir, stdin, outS, errS, logger)
		}, e.workspacePath, "job", string(job.Id()))

		// Directory is clean; job complete
		close(job.WaitChan)
	}()

	<-jobReady
	return job
}

// Executes a job, catching any panics.
func (e *Executor) Run(f def.Formula, j def.Job, d string, stdin io.Reader, outS, errS io.WriteCloser, journal log15.Logger) def.JobResult {
	r := def.JobResult{
		ID:       j.Id(),
		ExitCode: -1,
	}

	try.Do(func() {
		e.Execute(f, j, d, &r, outS, errS, journal)
	}).Catch(executor.Error, func(err *errors.Error) {
		r.Error = err
	}).Catch(integrity.Error, func(err *errors.Error) {
		r.Error = err
	}).CatchAll(func(err error) {
		r.Error = executor.UnknownError.Wrap(err).(*errors.Error)
	}).Done()

	return r
}

// Execute a formula in a specified directory. MAY PANIC.
func (e *Executor) Execute(formula def.Formula, job def.Job, jobPath string, result *def.JobResult, stdout, stderr io.WriteCloser, journal log15.Logger) {
	rootfsPath := filepath.Join(jobPath, "rootfs")

	// Prepare inputs
	transmat := util.DefaultTransmat()
	inputArenas := util.ProvisionInputs(transmat, formula.Inputs, journal)
	util.ProvisionOutputs(formula.Outputs, rootfsPath, journal)

	// Assemble filesystem
	assembly := util.AssembleFilesystem(
		util.BestAssembler(),
		rootfsPath,
		formula.Inputs,
		inputArenas,
		formula.Action.Escapes.Mounts,
		journal,
	)
	defer assembly.Teardown()

	// Emit configs for runc.
	runcConfigJsonPath := filepath.Join(jobPath, "config.json")
	cfg := EmitRuncConfigStruct(formula, rootfsPath)
	buf, err := json.Marshal(cfg)
	if err != nil {
		panic(executor.UnknownError.Wrap(err))
	}
	ioutil.WriteFile(runcConfigJsonPath, buf, 0600)
	runcRuntimeJsonPath := filepath.Join(jobPath, "runtime.json")
	cfg = EmitRuncRuntimeStruct(formula)
	buf, err = json.Marshal(cfg)
	if err != nil {
		panic(executor.UnknownError.Wrap(err))
	}
	ioutil.WriteFile(runcRuntimeJsonPath, buf, 0600)

	// Routing logs through a fifo appears to work, but we're going to use a file as a buffer anyway:
	//  in the event of nasty breakdowns, it's preferable that the runc log remain readable even if repeatr was the process to end first.
	logPath := filepath.Join(jobPath, "runc-debug.log")

	// Get handle to invokable runc plugin.
	runcPath := filepath.Join(assets.Get("runc"), "bin/runc")

	// Prepare command to exec
	args := []string{
		"--root", filepath.Join(e.workspacePath, "shared"), // a tmpfs would be appropriate
		"--log", logPath,
		"--log-format", "json",
		"start",
		"--config-file", runcConfigJsonPath,
		"--runtime-file", runcRuntimeJsonPath,
	}
	cmd := exec.Command(runcPath, args...)
	cmd.Stdin = nil
	cmd.Stdout = stdout
	cmd.Stderr = stderr

	// launch execution.
	// transform gosh's typed errors to repeatr's hierarchical errors.
	// this is... not untroubled code: since we're invoking a helper that's then
	//  proxying the exec even further, most errors are fatal (the mapping here is
	//   very different than in e.g. chroot executor, and provides much less meaning).
	var proc gosh.Proc
	try.Do(func() {
		proc = gosh.ExecProcCmd(cmd)
	}).CatchAll(func(err error) {
		switch err.(type) {
		case gosh.NoSuchCommandError:
			panic(executor.ConfigError.New("runc binary is missing"))
		case gosh.NoArgumentsError:
			panic(executor.UnknownError.Wrap(err))
		case gosh.NoSuchCwdError:
			panic(executor.UnknownError.Wrap(err))
		case gosh.ProcMonitorError:
			panic(executor.TaskExecError.Wrap(err))
		default:
			panic(executor.UnknownError.Wrap(err))
		}
	}).Done()

	var runcLog io.ReadCloser
	runcLog, err = os.OpenFile(logPath, os.O_CREATE|os.O_RDONLY, 0644)
	// note this open races child; doesn't matter.
	if err != nil {
		panic(executor.TaskExecError.New("failed to tail runc log: %s", err))
	}
	// swaddle the file in userland-interruptable reader;
	//  obviously we don't want to stop watching the logs when we hit the end of the still-growing file.
	runcLog = streamer.NewTailReader(runcLog)
	// close the reader when we return (which means after waiting for the exit code, which overall DTRT).
	defer runcLog.Close()

	// Proxy runc's logs out in realtime; also, detect errors and exit statuses from the stream.
	var realError error
	go func() {
		dec := json.NewDecoder(runcLog)
		for {
			var logMsg map[string]string
			err := dec.Decode(&logMsg)
			if err != nil {
				if err == io.EOF {
					return
				}
				panic(executor.TaskExecError.New("unparsable log from runc: %s", err))
			}
			// remap
			if _, ok := logMsg["msg"]; !ok {
				logMsg["msg"] = ""
			}
			ctx := log15.Ctx{}
			for k, v := range logMsg {
				if k == "msg" {
					continue
				}
				ctx["runc-"+k] = v
			}
			// with runc, everything we hear is at least a warning.
			journal.Warn(logMsg["msg"], ctx)
			// actually filtering the interesting structures and raising issues
			// note that we don't need to capture the "exit status" message, because that
			//  code *does* come out correctly... but we do need to sometimes override it again.
			switch logMsg["msg"] {
			case "Container start failed: [8] System error: no such file or directory":
				realError = executor.NoSuchCwdError.New("cannot set cwd to %q: no such file or directory", formula.Action.Cwd)
			case "Container start failed: [8] System error: not a directory":
				realError = executor.NoSuchCwdError.New("cannot set cwd to %q: not a directory", formula.Action.Cwd)
			}
		}
	}()

	// Wait for the job to complete
	result.ExitCode = proc.GetExitCode()
	//runcLog.Close() // this could/should happen before PreserveOutputs.  see todo about fixing scopes.

	// If we had a CnC error (rather than the real subprocess exit code), reset code, and raise
	if realError != nil {
		result.ExitCode = -1
		panic(realError)
	}

	// Save outputs
	result.Outputs = util.PreserveOutputs(transmat, formula.Outputs, rootfsPath, journal)
}
<commit_msg>Handle command-not-found in runc.  With todos.<commit_after>package runc

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/inconshreveable/log15"
	"github.com/polydawn/gosh"
	"github.com/spacemonkeygo/errors"
	"github.com/spacemonkeygo/errors/try"

	"polydawn.net/repeatr/def"
	"polydawn.net/repeatr/executor"
	"polydawn.net/repeatr/executor/basicjob"
	"polydawn.net/repeatr/executor/util"
	"polydawn.net/repeatr/io"
	"polydawn.net/repeatr/io/assets"
	"polydawn.net/repeatr/lib/flak"
	"polydawn.net/repeatr/lib/streamer"
)

// interface assertion
var _ executor.Executor = &Executor{}

type Executor struct {
	workspacePath string
}

func (e *Executor) Configure(workspacePath string) {
	e.workspacePath = workspacePath
}

func (e *Executor) Start(f def.Formula, id def.JobID, stdin io.Reader, journal io.Writer) def.Job {
	// TODO this function sig and its interface are long overdue for an aggressive refactor.
	// - `journal` is Rong.  The streams mux should be accessible after this function's scope!
	//   - either that or it's time to get cracking on saving the stream mux as an output
	// - `journal` should still be a thing, but it should be a logger.
	// - All these other values should move along in a `Job` struct
	//   - `BasicJob` sorta started, but is drunk:
	//      - if we're gonna have that, it's incomplete on the inputs
	//      - for some reason it mixes in responsibility for waiting for some of the ouputs
	//      - that use of channels and public fields is stupidly indefensive
	//   - The current `Job` interface is in the wrong package
	// - almost all of the scopes in these functions is wrong
	//   - they should be realigned until they actually assist the defers and cleanups
	//     - e.g. withErrorCapture, withJobWorkPath, withFilesystems, etc

	// Prepare the forumla for execution on this host
	def.ValidateAll(&f)

	job := basicjob.New(id)
	jobReady := make(chan struct{})

	go func() {
		// Run the formula in a temporary directory
		flak.WithDir(func(dir string) {

			// spool our output to a muxed stream
			var strm streamer.Mux
			strm = streamer.CborFileMux(filepath.Join(dir, "log"))
			outS := strm.Appender(1)
			errS := strm.Appender(2)
			job.Streams = strm
			defer func() {
				// Regardless of how the job ends (or even if it fails the remaining setup), output streams must be terminated.
				outS.Close()
				errS.Close()
			}()

			// Job is ready to stream process output
			close(jobReady)

			// Set up a logger.  Tag all messages with this jobid.
			logger := log15.New(log15.Ctx{"JobID": id})
			logger.SetHandler(log15.StreamHandler(journal, log15.TerminalFormat()))

			job.Result = e.Run(f, job, dir, stdin, outS, errS, logger)
		}, e.workspacePath, "job", string(job.Id()))

		// Directory is clean; job complete
		close(job.WaitChan)
	}()

	<-jobReady
	return job
}

// Executes a job, catching any panics.
func (e *Executor) Run(f def.Formula, j def.Job, d string, stdin io.Reader, outS, errS io.WriteCloser, journal log15.Logger) def.JobResult {
	r := def.JobResult{
		ID:       j.Id(),
		ExitCode: -1,
	}

	try.Do(func() {
		e.Execute(f, j, d, &r, outS, errS, journal)
	}).Catch(executor.Error, func(err *errors.Error) {
		r.Error = err
	}).Catch(integrity.Error, func(err *errors.Error) {
		r.Error = err
	}).CatchAll(func(err error) {
		r.Error = executor.UnknownError.Wrap(err).(*errors.Error)
	}).Done()

	return r
}

// Execute a formula in a specified directory. MAY PANIC.
func (e *Executor) Execute(formula def.Formula, job def.Job, jobPath string, result *def.JobResult, stdout, stderr io.WriteCloser, journal log15.Logger) {
	rootfsPath := filepath.Join(jobPath, "rootfs")

	// Prepare inputs
	transmat := util.DefaultTransmat()
	inputArenas := util.ProvisionInputs(transmat, formula.Inputs, journal)
	util.ProvisionOutputs(formula.Outputs, rootfsPath, journal)

	// Assemble filesystem
	assembly := util.AssembleFilesystem(
		util.BestAssembler(),
		rootfsPath,
		formula.Inputs,
		inputArenas,
		formula.Action.Escapes.Mounts,
		journal,
	)
	defer assembly.Teardown()

	// Emit configs for runc.
	runcConfigJsonPath := filepath.Join(jobPath, "config.json")
	cfg := EmitRuncConfigStruct(formula, rootfsPath)
	buf, err := json.Marshal(cfg)
	if err != nil {
		panic(executor.UnknownError.Wrap(err))
	}
	ioutil.WriteFile(runcConfigJsonPath, buf, 0600)
	runcRuntimeJsonPath := filepath.Join(jobPath, "runtime.json")
	cfg = EmitRuncRuntimeStruct(formula)
	buf, err = json.Marshal(cfg)
	if err != nil {
		panic(executor.UnknownError.Wrap(err))
	}
	ioutil.WriteFile(runcRuntimeJsonPath, buf, 0600)

	// Routing logs through a fifo appears to work, but we're going to use a file as a buffer anyway:
	//  in the event of nasty breakdowns, it's preferable that the runc log remain readable even if repeatr was the process to end first.
	logPath := filepath.Join(jobPath, "runc-debug.log")

	// Get handle to invokable runc plugin.
	runcPath := filepath.Join(assets.Get("runc"), "bin/runc")

	// Prepare command to exec
	args := []string{
		"--root", filepath.Join(e.workspacePath, "shared"), // a tmpfs would be appropriate
		"--log", logPath,
		"--log-format", "json",
		"start",
		"--config-file", runcConfigJsonPath,
		"--runtime-file", runcRuntimeJsonPath,
	}
	cmd := exec.Command(runcPath, args...)
	cmd.Stdin = nil
	cmd.Stdout = stdout
	cmd.Stderr = stderr

	// launch execution.
	// transform gosh's typed errors to repeatr's hierarchical errors.
	// this is... not untroubled code: since we're invoking a helper that's then
	//  proxying the exec even further, most errors are fatal (the mapping here is
	//   very different than in e.g. chroot executor, and provides much less meaning).
	var proc gosh.Proc
	try.Do(func() {
		proc = gosh.ExecProcCmd(cmd)
	}).CatchAll(func(err error) {
		switch err.(type) {
		case gosh.NoSuchCommandError:
			panic(executor.ConfigError.New("runc binary is missing"))
		case gosh.NoArgumentsError:
			panic(executor.UnknownError.Wrap(err))
		case gosh.NoSuchCwdError:
			panic(executor.UnknownError.Wrap(err))
		case gosh.ProcMonitorError:
			panic(executor.TaskExecError.Wrap(err))
		default:
			panic(executor.UnknownError.Wrap(err))
		}
	}).Done()

	var runcLog io.ReadCloser
	runcLog, err = os.OpenFile(logPath, os.O_CREATE|os.O_RDONLY, 0644)
	// note this open races child; doesn't matter.
	if err != nil {
		panic(executor.TaskExecError.New("failed to tail runc log: %s", err))
	}
	// swaddle the file in userland-interruptable reader;
	//  obviously we don't want to stop watching the logs when we hit the end of the still-growing file.
	runcLog = streamer.NewTailReader(runcLog)
	// close the reader when we return (which means after waiting for the exit code, which overall DTRT).
	defer runcLog.Close()

	// Proxy runc's logs out in realtime; also, detect errors and exit statuses from the stream.
	var realError error
	//var unknownError bool // see the "NOTE WELL" section below -.-
	go func() {
		dec := json.NewDecoder(runcLog)
		for {
			var logMsg map[string]string
			err := dec.Decode(&logMsg)
			if err != nil {
				if err == io.EOF {
					return
				}
				panic(executor.TaskExecError.New("unparsable log from runc: %s", err))
			}
			// remap
			if _, ok := logMsg["msg"]; !ok {
				logMsg["msg"] = ""
			}
			ctx := log15.Ctx{}
			for k, v := range logMsg {
				if k == "msg" {
					continue
				}
				ctx["runc-"+k] = v
			}
			// with runc, everything we hear is at least a warning.
			journal.Warn(logMsg["msg"], ctx)
			// actually filtering the interesting structures and raising issues
			// note that we don't need to capture the "exit status" message, because that
			//  code *does* come out correctly... but we do need to sometimes override it again.
			// NOTE WELL: we cannot guarantee to capture all semantic runc failure modes.
			//  Errors may slip through with exit status 1: there are still many fail states
			//  which runc does not log with sufficient consistency or a sufficiently separate
			//  control channel for us to be able to reliably disambiguate them from stderr
			//  output of a successfully executing job!
			// We have whitelisted what we can; the following oddities remain:
			//   - runc will log an "exit status ${n}" message for other failures of its internal forking
			//     - this one is at least on a clear control channel, so we can raise it as a panic, even if we don't know what it is
			//       - TODO do so
			//   - lots of system initialization paths in runc will error directly stderr with no clear sigils or separation from usermode stderr.
			//     - and these mean we're just screwed, and require additional upstream patches to address.
			switch logMsg["msg"] {
			case "Container start failed: [8] System error: no such file or directory":
				realError = executor.NoSuchCwdError.New("cannot set cwd to %q: no such file or directory", formula.Action.Cwd)
			case "Container start failed: [8] System error: not a directory":
				realError = executor.NoSuchCwdError.New("cannot set cwd to %q: not a directory", formula.Action.Cwd)
			default:
				// broader patterns required for some of these so we can ignore the vagaries of how the command name was quoted
				if strings.HasPrefix(logMsg["msg"], "Container start failed: [8] System error: exec: ") {
					if strings.HasSuffix(logMsg["msg"], ": executable file not found in $PATH") {
						realError = executor.NoSuchCommandError.New("command %q not found", formula.Action.Entrypoint[0])
					} else if strings.HasSuffix(logMsg["msg"], ": no such file or directory") {
						realError = executor.NoSuchCommandError.New("command %q not found", formula.Action.Entrypoint[0])
					}
				}
			}
		}
	}()

	// Wait for the job to complete
	result.ExitCode = proc.GetExitCode()
	//runcLog.Close() // this could/should happen before PreserveOutputs.  see todo about fixing scopes.

	// If we had a CnC error (rather than the real subprocess exit code):
	//  - reset code to -1 because the runc exit code wasn't really from the job command
	//  - zero the output buffers because runc (again) doesn't understand what control channels are
	//  - finally, raise the error
	if realError != nil {
		result.ExitCode = -1
		// TODO just overwrite the streamer, i guess?
		panic(realError)
	}

	// Save outputs
	result.Outputs = util.PreserveOutputs(transmat, formula.Outputs, rootfsPath, journal)
}
<|endoftext|>


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

// The uuid package can be used to generate and parse universally unique
// identifiers, a standardized format in the form of a 128 bit number.
//
// http://tools.ietf.org/html/rfc4122
package uuid

import (
    "crypto/rand"
    "fmt"
    "io"
    "net"
    "time"

    "github.com/0x6e6562/gocql"
)

type UUID [16]byte

var hardwareAddr []byte

const (
    VariantNCSCompat = 0
    VariantIETF      = 2
    VariantMicrosoft = 6
    VariantFuture    = 7
)

func init() {
    if interfaces, err := net.Interfaces(); err == nil {
        for _, i := range interfaces {
            if i.Flags&net.FlagLoopback == 0 && len(i.HardwareAddr) > 0 {
                hardwareAddr = i.HardwareAddr
                break
            }
        }
    }
    if hardwareAddr == nil {
        // If we failed to obtain the MAC address of the current computer,
        // we will use a randomly generated 6 byte sequence instead and set
        // the multicast bit as recommended in RFC 4122.
        hardwareAddr = make([]byte, 6)
        _, err := io.ReadFull(rand.Reader, hardwareAddr)
        if err != nil {
            panic(err)
        }
        hardwareAddr[0] = hardwareAddr[0] | 0x01
    }
}

// ParseUUID parses a 32 digit hexadecimal number (that might contain hypens)
// represanting an UUID.
func ParseUUID(input string) (UUID, error) {
    var u UUID
    j := 0
    for _, r := range input {
        switch {
        case r == '-' && j&1 == 0:
            continue
        case r >= '0' && r <= '9' && j < 32:
            u[j/2] |= byte(r-'0') << uint(4-j&1*4)
        case r >= 'a' && r <= 'f' && j < 32:
            u[j/2] |= byte(r-'a'+10) << uint(4-j&1*4)
        case r >= 'A' && r <= 'F' && j < 32:
            u[j/2] |= byte(r-'A'+10) << uint(4-j&1*4)
        default:
            return UUID{}, fmt.Errorf("invalid UUID %q", input)
        }
        j += 1
    }
    if j != 32 {
        return UUID{}, fmt.Errorf("invalid UUID %q", input)
    }
    return u, nil
}

// FromBytes converts a raw byte slice to an UUID. It will panic if the slice
// isn't exactly 16 bytes long.
func FromBytes(input []byte) UUID {
    var u UUID
    if len(input) != 16 {
        panic("UUIDs must be exactly 16 bytes long")
    }
    copy(u[:], input)
    return u
}

// RandomUUID generates a totally random UUID (version 4) as described in
// RFC 4122.
func RandomUUID() UUID {
    var u UUID
    io.ReadFull(rand.Reader, u[:])
    u[6] &= 0x0F // clear version
    u[6] |= 0x40 // set version to 4 (random uuid)
    u[8] &= 0x3F // clear variant
    u[8] |= 0x80 // set to IETF variant
    return u
}

var timeBase = time.Date(1582, time.October, 15, 0, 0, 0, 0, time.UTC).Unix()

// TimeUUID generates a new time based UUID (version 1) as described in RFC
// 4122. This UUID contains the MAC address of the node that generated the
// UUID, a timestamp and a sequence number.
func TimeUUID() UUID {
    var u UUID

    now := time.Now().In(time.UTC)
    t := uint64(now.Unix()-timeBase)*10000000 + uint64(now.Nanosecond()/100)
    u[0], u[1], u[2], u[3] = byte(t>>24), byte(t>>16), byte(t>>8), byte(t)
    u[4], u[5] = byte(t>>40), byte(t>>32)
    u[6], u[7] = byte(t>>56)&0x0F, byte(t>>48)

    var clockSeq [2]byte
    io.ReadFull(rand.Reader, clockSeq[:])
    u[8] = clockSeq[1]
    u[9] = clockSeq[0]

    copy(u[10:], hardwareAddr)

    u[6] |= 0x10 // set version to 1 (time based uuid)
    u[8] &= 0x3F // clear variant
    u[8] |= 0x80 // set to IETF variant

    return u
}

// String returns the UUID in it's canonical form, a 32 digit hexadecimal
// number in the form of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
func (u UUID) String() string {
    return fmt.Sprintf("%x-%x-%x-%x-%x",
        u[0:4], u[4:6], u[6:8], u[8:10], u[10:16])
}

// Bytes returns the raw byte slice for this UUID. A UUID is always 128 bits
// (16 bytes) long.
func (u UUID) Bytes() []byte {
    return u[:]
}

// Variant returns the variant of this UUID. This package will only generate
// UUIDs in the IETF variant.
func (u UUID) Variant() int {
    x := u[8]
    if x&0x80 == 0 {
        return VariantNCSCompat
    }
    if x&0x40 == 0 {
        return VariantIETF
    }
    if x&0x20 == 0 {
        return VariantMicrosoft
    }
    return VariantFuture
}

// Version extracts the version of this UUID variant. The RFC 4122 describes
// five kinds of UUIDs.
func (u UUID) Version() int {
    return int(u[6] & 0xF0 >> 4)
}

// Node extracts the MAC address of the node who generated this UUID. It will
// return nil if the UUID is not a time based UUID (version 1).
func (u UUID) Node() []byte {
    if u.Version() != 1 {
        return nil
    }
    return u[10:]
}

// Timestamp extracts the timestamp information from a time based UUID
// (version 1).
func (u UUID) Timestamp() uint64 {
    if u.Version() != 1 {
        return 0
    }
    return uint64(u[0])<<24 + uint64(u[1])<<16 + uint64(u[2])<<8 +
        uint64(u[3]) + uint64(u[4])<<40 + uint64(u[5])<<32 +
        uint64(u[7])<<48 + uint64(u[6]&0x0F)<<56
}

// Time is like Timestamp, except that it returns a time.Time.
func (u UUID) Time() time.Time {
    t := u.Timestamp()
    if t == 0 {
        return time.Time{}
    }
    sec := t / 10000000
    nsec := t - sec
    return time.Unix(int64(sec)+timeBase, int64(nsec))
}

func (u UUID) MarshalCQL(info *gocql.TypeInfo) ([]byte, error) {
    switch info.Type {
    case gocql.TypeUUID, gocql.TypeTimeUUID:
        return u[:], nil
    }
    return gocql.Marshal(info, u[:])
}

func (u *UUID) UnmarshalCQL(info *gocql.TypeInfo, data []byte) error {
    switch info.Type {
    case gocql.TypeUUID, gocql.TypeTimeUUID:
        *u = FromBytes(data)
        return nil
    }
    var val []byte
    if err := gocql.Unmarshal(info, data, &val); err != nil {
        return err
    }
    *u = FromBytes(val)
    return nil
}
<commit_msg>rebase old namespace<commit_after>// Copyright (c) 2012 The gocql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The uuid package can be used to generate and parse universally unique
// identifiers, a standardized format in the form of a 128 bit number.
//
// http://tools.ietf.org/html/rfc4122
package uuid

import (
    "crypto/rand"
    "fmt"
    "io"
    "net"
    "time"

    "github.com/QTAB/gocql"
)

type UUID [16]byte

var hardwareAddr []byte

const (
    VariantNCSCompat = 0
    VariantIETF      = 2
    VariantMicrosoft = 6
    VariantFuture    = 7
)

func init() {
    if interfaces, err := net.Interfaces(); err == nil {
        for _, i := range interfaces {
            if i.Flags&net.FlagLoopback == 0 && len(i.HardwareAddr) > 0 {
                hardwareAddr = i.HardwareAddr
                break
            }
        }
    }
    if hardwareAddr == nil {
        // If we failed to obtain the MAC address of the current computer,
        // we will use a randomly generated 6 byte sequence instead and set
        // the multicast bit as recommended in RFC 4122.
        hardwareAddr = make([]byte, 6)
        _, err := io.ReadFull(rand.Reader, hardwareAddr)
        if err != nil {
            panic(err)
        }
        hardwareAddr[0] = hardwareAddr[0] | 0x01
    }
}

// ParseUUID parses a 32 digit hexadecimal number (that might contain hypens)
// represanting an UUID.
func ParseUUID(input string) (UUID, error) {
    var u UUID
    j := 0
    for _, r := range input {
        switch {
        case r == '-' && j&1 == 0:
            continue
        case r >= '0' && r <= '9' && j < 32:
            u[j/2] |= byte(r-'0') << uint(4-j&1*4)
        case r >= 'a' && r <= 'f' && j < 32:
            u[j/2] |= byte(r-'a'+10) << uint(4-j&1*4)
        case r >= 'A' && r <= 'F' && j < 32:
            u[j/2] |= byte(r-'A'+10) << uint(4-j&1*4)
        default:
            return UUID{}, fmt.Errorf("invalid UUID %q", input)
        }
        j += 1
    }
    if j != 32 {
        return UUID{}, fmt.Errorf("invalid UUID %q", input)
    }
    return u, nil
}

// FromBytes converts a raw byte slice to an UUID. It will panic if the slice
// isn't exactly 16 bytes long.
func FromBytes(input []byte) UUID {
    var u UUID
    if len(input) != 16 {
        panic("UUIDs must be exactly 16 bytes long")
    }
    copy(u[:], input)
    return u
}

// RandomUUID generates a totally random UUID (version 4) as described in
// RFC 4122.
func RandomUUID() UUID {
    var u UUID
    io.ReadFull(rand.Reader, u[:])
    u[6] &= 0x0F // clear version
    u[6] |= 0x40 // set version to 4 (random uuid)
    u[8] &= 0x3F // clear variant
    u[8] |= 0x80 // set to IETF variant
    return u
}

var timeBase = time.Date(1582, time.October, 15, 0, 0, 0, 0, time.UTC).Unix()

// TimeUUID generates a new time based UUID (version 1) as described in RFC
// 4122. This UUID contains the MAC address of the node that generated the
// UUID, a timestamp and a sequence number.
func TimeUUID() UUID {
    var u UUID

    now := time.Now().In(time.UTC)
    t := uint64(now.Unix()-timeBase)*10000000 + uint64(now.Nanosecond()/100)
    u[0], u[1], u[2], u[3] = byte(t>>24), byte(t>>16), byte(t>>8), byte(t)
    u[4], u[5] = byte(t>>40), byte(t>>32)
    u[6], u[7] = byte(t>>56)&0x0F, byte(t>>48)

    var clockSeq [2]byte
    io.ReadFull(rand.Reader, clockSeq[:])
    u[8] = clockSeq[1]
    u[9] = clockSeq[0]

    copy(u[10:], hardwareAddr)

    u[6] |= 0x10 // set version to 1 (time based uuid)
    u[8] &= 0x3F // clear variant
    u[8] |= 0x80 // set to IETF variant

    return u
}

// String returns the UUID in it's canonical form, a 32 digit hexadecimal
// number in the form of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
func (u UUID) String() string {
    return fmt.Sprintf("%x-%x-%x-%x-%x",
        u[0:4], u[4:6], u[6:8], u[8:10], u[10:16])
}

// Bytes returns the raw byte slice for this UUID. A UUID is always 128 bits
// (16 bytes) long.
func (u UUID) Bytes() []byte {
    return u[:]
}

// Variant returns the variant of this UUID. This package will only generate
// UUIDs in the IETF variant.
func (u UUID) Variant() int {
    x := u[8]
    if x&0x80 == 0 {
        return VariantNCSCompat
    }
    if x&0x40 == 0 {
        return VariantIETF
    }
    if x&0x20 == 0 {
        return VariantMicrosoft
    }
    return VariantFuture
}

// Version extracts the version of this UUID variant. The RFC 4122 describes
// five kinds of UUIDs.
func (u UUID) Version() int {
    return int(u[6] & 0xF0 >> 4)
}

// Node extracts the MAC address of the node who generated this UUID. It will
// return nil if the UUID is not a time based UUID (version 1).
func (u UUID) Node() []byte {
    if u.Version() != 1 {
        return nil
    }
    return u[10:]
}

// Timestamp extracts the timestamp information from a time based UUID
// (version 1).
func (u UUID) Timestamp() uint64 {
    if u.Version() != 1 {
        return 0
    }
    return uint64(u[0])<<24 + uint64(u[1])<<16 + uint64(u[2])<<8 +
        uint64(u[3]) + uint64(u[4])<<40 + uint64(u[5])<<32 +
        uint64(u[7])<<48 + uint64(u[6]&0x0F)<<56
}

// Time is like Timestamp, except that it returns a time.Time.
func (u UUID) Time() time.Time {
    t := u.Timestamp()
    if t == 0 {
        return time.Time{}
    }
    sec := t / 10000000
    nsec := t - sec
    return time.Unix(int64(sec)+timeBase, int64(nsec))
}

func (u UUID) MarshalCQL(info *gocql.TypeInfo) ([]byte, error) {
    switch info.Type {
    case gocql.TypeUUID, gocql.TypeTimeUUID:
        return u[:], nil
    }
    return gocql.Marshal(info, u[:])
}

func (u *UUID) UnmarshalCQL(info *gocql.TypeInfo, data []byte) error {
    switch info.Type {
    case gocql.TypeUUID, gocql.TypeTimeUUID:
        *u = FromBytes(data)
        return nil
    }
    var val []byte
    if err := gocql.Unmarshal(info, data, &val); err != nil {
        return err
    }
    *u = FromBytes(val)
    return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package service

import (
	"github.com/kubernetes/deployment-manager/expandybird/expander"
	"github.com/kubernetes/deployment-manager/common"
	"github.com/kubernetes/deployment-manager/util"

	"errors"
	"fmt"
	"net/http"

	restful "github.com/emicklei/go-restful"
)

// A Service wraps a web service that performs template expansion.
type Service struct {
	*restful.WebService
}

// NewService creates and returns a new Service, initialized with a new
// restful.WebService configured with a route that dispatches to the supplied
// handler. The new Service must be registered before accepting traffic by
// calling Register.
func NewService(handler restful.RouteFunction) *Service {
	restful.EnableTracing(true)
	webService := new(restful.WebService)
	webService.Consumes(restful.MIME_JSON, restful.MIME_XML)
	webService.Produces(restful.MIME_JSON, restful.MIME_XML)
	webService.Route(webService.POST("/expand").To(handler).
		Doc("Expand a template.").
		Reads(&common.Template{}))
	return &Service{webService}
}

// Register adds the web service wrapped by the Service to the supplied
// container. If the supplied container is nil, then the default container is
// used, instead.
func (s *Service) Register(container *restful.Container) {
	if container == nil {
		container = restful.DefaultContainer
	}

	container.Add(s.WebService)
}

// NewExpansionHandler returns a route function that handles an incoming
// template expansion request, bound to the supplied expander.
func NewExpansionHandler(backend expander.Expander) restful.RouteFunction {
	return func(req *restful.Request, resp *restful.Response) {
		util.LogHandlerEntry("expandybird: expand", req.Request)
		template := &common.Template{}
		if err := req.ReadEntity(&template); err != nil {
			logAndReturnErrorFromHandler(http.StatusBadRequest, err.Error(), resp)
			return
		}

		output, err := backend.ExpandTemplate(template)
		if err != nil {
			message := fmt.Sprintf("error expanding template: %s", err)
			logAndReturnErrorFromHandler(http.StatusBadRequest, message, resp)
			return
		}

		response, err := expander.NewExpansionResponse(output)
		if err != nil {
			message := fmt.Sprintf("error marshaling output: %s", err)
			logAndReturnErrorFromHandler(http.StatusBadRequest, message, resp)
			return
		}

		util.LogHandlerExit("expandybird", http.StatusOK, "OK", resp.ResponseWriter)
		resp.WriteEntity(response)
	}
}

func logAndReturnErrorFromHandler(statusCode int, message string, resp *restful.Response) {
	util.LogHandlerExit("expandybird: expand", statusCode, message, resp.ResponseWriter)
	resp.WriteError(statusCode, errors.New(message))
}
<commit_msg>Fix test failure in expandybird.<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package service

import (
	"github.com/kubernetes/deployment-manager/common"
	"github.com/kubernetes/deployment-manager/expandybird/expander"
	"github.com/kubernetes/deployment-manager/util"

	"errors"
	"fmt"
	"net/http"

	restful "github.com/emicklei/go-restful"
)

// A Service wraps a web service that performs template expansion.
type Service struct {
	*restful.WebService
}

// NewService creates and returns a new Service, initialized with a new
// restful.WebService configured with a route that dispatches to the supplied
// handler. The new Service must be registered before accepting traffic by
// calling Register.
func NewService(handler restful.RouteFunction) *Service {
	restful.EnableTracing(true)
	webService := new(restful.WebService)
	webService.Consumes(restful.MIME_JSON, restful.MIME_XML)
	webService.Produces(restful.MIME_JSON, restful.MIME_XML)
	webService.Route(webService.POST("/expand").To(handler).
		Doc("Expand a template.").
		Reads(&common.Template{}).
		Writes(&expander.ExpansionResponse{}))
	return &Service{webService}
}

// Register adds the web service wrapped by the Service to the supplied
// container. If the supplied container is nil, then the default container is
// used, instead.
func (s *Service) Register(container *restful.Container) {
	if container == nil {
		container = restful.DefaultContainer
	}

	container.Add(s.WebService)
}

// NewExpansionHandler returns a route function that handles an incoming
// template expansion request, bound to the supplied expander.
func NewExpansionHandler(backend expander.Expander) restful.RouteFunction {
	return func(req *restful.Request, resp *restful.Response) {
		util.LogHandlerEntry("expandybird: expand", req.Request)
		template := &common.Template{}
		if err := req.ReadEntity(&template); err != nil {
			logAndReturnErrorFromHandler(http.StatusBadRequest, err.Error(), resp)
			return
		}

		output, err := backend.ExpandTemplate(template)
		if err != nil {
			message := fmt.Sprintf("error expanding template: %s", err)
			logAndReturnErrorFromHandler(http.StatusBadRequest, message, resp)
			return
		}

		response, err := expander.NewExpansionResponse(output)
		if err != nil {
			message := fmt.Sprintf("error marshaling output: %s", err)
			logAndReturnErrorFromHandler(http.StatusBadRequest, message, resp)
			return
		}

		util.LogHandlerExit("expandybird", http.StatusOK, "OK", resp.ResponseWriter)
		resp.WriteEntity(response)
	}
}

func logAndReturnErrorFromHandler(statusCode int, message string, resp *restful.Response) {
	util.LogHandlerExit("expandybird: expand", statusCode, message, resp.ResponseWriter)
	resp.WriteError(statusCode, errors.New(message))
}
<|endoftext|>


<commit_before>package solidserver

import (
	"encoding/json"
	"fmt"
	"github.com/hashicorp/terraform/helper/schema"
	"log"
	"net/url"
	"strconv"
	"strings"
)

func resourcednsrr() *schema.Resource {
	return &schema.Resource{
		Create: resourcednsrrCreate,
		Read:   resourcednsrrRead,
		Update: resourcednsrrUpdate,
		Delete: resourcednsrrDelete,
		Exists: resourcednsrrExists,
		Importer: &schema.ResourceImporter{
			State: resourcednsrrImportState,
		},

		Schema: map[string]*schema.Schema{
			"dnsserver": {
				Type:        schema.TypeString,
				Description: "The managed SMART DNS server name, or DNS server name hosting the RR's zone.",
				Required:    true,
				ForceNew:    true,
			},
			"dnsview": {
				Type:        schema.TypeString,
				Description: "The View name of the RR to create.",
				Optional:    true,
				Default:     "",
			},
			"name": {
				Type:        schema.TypeString,
				Description: "The Fully Qualified Domain Name of the RR to create.",
				Required:    true,
				ForceNew:    true,
			},
			"type": {
				Type:         schema.TypeString,
				Description:  "The type of the RR to create (Supported: A, AAAA, CNAME, TXT).",
				ValidateFunc: resourcednsrrvalidatetype,
				Required:     true,
				ForceNew:     true,
			},
			"value": {
				Type:        schema.TypeString,
				Description: "The value od the RR to create.",
				Computed:    false,
				Required:    true,
				ForceNew:    true,
			},
			"ttl": {
				Type:        schema.TypeInt,
				Description: "The DNS Time To Live of the RR to create.",
				Optional:    true,
				Default:     3600,
			},
		},
	}
}

func resourcednsrrvalidatetype(v interface{}, _ string) ([]string, []error) {
	switch strings.ToUpper(v.(string)) {
	case "A":
		return nil, nil
	case "AAAA":
		return nil, nil
	case "PTR":
		return nil, nil
	case "CNAME":
		return nil, nil
	case "DNAME":
		return nil, nil
	case "TXT":
		return nil, nil
	case "NS":
		return nil, nil
	default:
		return nil, []error{fmt.Errorf("Unsupported RR type.")}
	}
}

func resourcednsrrExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	s := meta.(*SOLIDserver)

	// Building parameters
	parameters := url.Values{}
	parameters.Add("rr_id", d.Id())

	log.Printf("[DEBUG] Checking existence of RR (oid): %s\n", d.Id())

	// Sending the read request
	resp, body, err := s.Request("get", "rest/dns_rr_info", &parameters)

	if err == nil {
		var buf [](map[string]interface{})
		json.Unmarshal([]byte(body), &buf)

		// Checking the answer
		if (resp.StatusCode == 200 || resp.StatusCode == 201) && len(buf) > 0 {
			return true, nil
		}

		if len(buf) > 0 {
			if errMsg, errExist := buf[0]["errmsg"].(string); errExist {
				// Log the error
				log.Printf("[DEBUG] SOLIDServer - Unable to find RR (oid): %s (%s)\n", d.Id(), errMsg)
			}
		} else {
			// Log the error
			log.Printf("[DEBUG] SOLIDServer - Unable to find RR (oid): %s\n", d.Id())
		}

		// Unset local ID
		d.SetId("")
	}

	// Reporting a failure
	return false, err
}

func resourcednsrrCreate(d *schema.ResourceData, meta interface{}) error {
	s := meta.(*SOLIDserver)

	// Building parameters
	parameters := url.Values{}
	parameters.Add("add_flag", "new_only")
	parameters.Add("dns_name", d.Get("dnsserver").(string))
	parameters.Add("rr_name", d.Get("name").(string))
	parameters.Add("rr_type", strings.ToUpper(d.Get("type").(string)))
	parameters.Add("value1", d.Get("value").(string))
	parameters.Add("rr_ttl", strconv.Itoa(d.Get("ttl").(int)))

	// Add dnsview parameter if it is supplied
	if len(d.Get("dnsview").(string)) != 0 {
		parameters.Add("dnsview_name", strings.ToLower(d.Get("dnsview").(string)))
	}

	// Sending the creation request
	resp, body, err := s.Request("post", "rest/dns_rr_add", &parameters)

	if err == nil {
		var buf [](map[string]interface{})
		json.Unmarshal([]byte(body), &buf)

		// Checking the answer
		if (resp.StatusCode == 200 || resp.StatusCode == 201) && len(buf) > 0 {
			if oid, oidExist := buf[0]["ret_oid"].(string); oidExist {
				log.Printf("[DEBUG] SOLIDServer - Created RR (oid): %s\n", oid)
				d.SetId(oid)
				return nil
			}
		}

		// Reporting a failure
		if len(buf) > 0 {
			if errMsg, errExist := buf[0]["errmsg"].(string); errExist {
				return fmt.Errorf("SOLIDServer - Unable to create RR: %s (%s)", d.Get("name").(string), errMsg)
			}
		}

		return fmt.Errorf("SOLIDServer - Unable to create RR: %s\n", d.Get("name").(string))
	}

	// Reporting a failure
	return err
}

func resourcednsrrUpdate(d *schema.ResourceData, meta interface{}) error {
	s := meta.(*SOLIDserver)

	// Building parameters
	parameters := url.Values{}
	parameters.Add("rr_id", d.Id())
	parameters.Add("add_flag", "edit_only")
	parameters.Add("dns_name", d.Get("dnsserver").(string))
	parameters.Add("rr_name", d.Get("name").(string))
	parameters.Add("rr_type", strings.ToUpper(d.Get("type").(string)))
	parameters.Add("value1", d.Get("value").(string))
	parameters.Add("rr_ttl", strconv.Itoa(d.Get("ttl").(int)))

	// Add dnsview parameter if it is supplied
	if len(d.Get("dnsview").(string)) != 0 {
		parameters.Add("dnsview_name", strings.ToLower(d.Get("dnsview").(string)))
	}

	// Sending the update request
	resp, body, err := s.Request("put", "rest/dns_rr_add", &parameters)

	if err == nil {
		var buf [](map[string]interface{})
		json.Unmarshal([]byte(body), &buf)

		// Checking the answer
		if (resp.StatusCode == 200 || resp.StatusCode == 201) && len(buf) > 0 {
			if oid, oidExist := buf[0]["ret_oid"].(string); oidExist {
				log.Printf("[DEBUG] SOLIDServer - Updated RR (oid): %s\n", oid)
				d.SetId(oid)
				return nil
			}
		}

		// Reporting a failure
		if len(buf) > 0 {
			if errMsg, errExist := buf[0]["errmsg"].(string); errExist {
				return fmt.Errorf("SOLIDServer - Unable to update RR: %s (%s)", d.Get("name").(string), errMsg)
			}
		}

		return fmt.Errorf("SOLIDServer - Unable to update RR: %s\n", d.Get("name").(string))
	}

	// Reporting a failure
	return err
}

func resourcednsrrDelete(d *schema.ResourceData, meta interface{}) error {
	s := meta.(*SOLIDserver)

	// Building parameters
	parameters := url.Values{}
	parameters.Add("rr_id", d.Id())

	// Add dnsview parameter if it is supplied
	if len(d.Get("dnsview").(string)) != 0 {
		parameters.Add("dnsview_name", strings.ToLower(d.Get("dnsview").(string)))
	}

	// Sending the deletion request
	resp, body, err := s.Request("delete", "rest/dns_rr_delete", &parameters)

	if err == nil {
		var buf [](map[string]interface{})
		json.Unmarshal([]byte(body), &buf)

		// Checking the answer
		if resp.StatusCode != 200 && resp.StatusCode != 204 {
			// Reporting a failure
			if len(buf) > 0 {
				if errMsg, errExist := buf[0]["errmsg"].(string); errExist {
					return fmt.Errorf("SOLIDServer - Unable to delete RR: %s (%s)", d.Get("name").(string), errMsg)
				}
			}

			return fmt.Errorf("SOLIDServer - Unable to delete RR: %s", d.Get("name").(string))
		}

		// Log deletion
		log.Printf("[DEBUG] SOLIDServer - Deleted RR (oid): %s\n", d.Id())

		// Unset local ID
		d.SetId("")

		// Reporting a success
		return nil
	}

	// Reporting a failure
	return err
}

func resourcednsrrRead(d *schema.ResourceData, meta interface{}) error {
	s := meta.(*SOLIDserver)

	// Building parameters
	parameters := url.Values{}
	parameters.Add("rr_id", d.Id())

	// Sending the read request
	resp, body, err := s.Request("get", "rest/dns_rr_info", &parameters)

	if err == nil {
		var buf [](map[string]interface{})
		json.Unmarshal([]byte(body), &buf)

		// Checking the answer
		if resp.StatusCode == 200 && len(buf) > 0 {
			ttl, _ := strconv.Atoi(buf[0]["ttl"].(string))

			d.Set("dnsserver", buf[0]["dns_name"].(string))
			d.Set("name", buf[0]["rr_full_name"].(string))
			d.Set("type", buf[0]["rr_type"].(string))
			d.Set("value", buf[0]["value1"].(string))
			d.Set("ttl", ttl)

			if buf[0]["dnsview_name"].(string) != "#" {
				d.Set("dnsview", buf[0]["dnsview_name"].(string))
			}

			return nil
		}

		if len(buf) > 0 {
			if errMsg, errExist := buf[0]["errmsg"].(string); errExist {
				// Log the error
				log.Printf("[DEBUG] SOLIDServer - Unable to find RR: %s (%s)\n", d.Get("name"), errMsg)
			}
		} else {
			// Log the error
			log.Printf("[DEBUG] SOLIDServer - Unable to find RR (oid): %s\n", d.Id())
		}

		// Do not unset the local ID to avoid inconsistency

		// Reporting a failure
		return fmt.Errorf("SOLIDServer - Unable to find RR: %s\n", d.Get("name").(string))
	}

	// Reporting a failure
	return err
}

func resourcednsrrImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
	s := meta.(*SOLIDserver)

	// Building parameters
	parameters := url.Values{}
	parameters.Add("rr_id", d.Id())

	// Sending the read request
	resp, body, err := s.Request("get", "rest/dns_rr_info", &parameters)

	if err == nil {
		var buf [](map[string]interface{})
		json.Unmarshal([]byte(body), &buf)

		// Checking the answer
		if resp.StatusCode == 200 && len(buf) > 0 {
			ttl, _ := strconv.Atoi(buf[0]["ttl"].(string))

			d.Set("dnsserver", buf[0]["dns_name"].(string))
			d.Set("name", buf[0]["rr_full_name"].(string))
			d.Set("type", buf[0]["rr_type"].(string))
			d.Set("value", buf[0]["value1"].(string))
			d.Set("ttl", ttl)

			if buf[0]["dnsview_name"].(string) != "#" {
				d.Set("dnsview", buf[0]["dnsview_name"].(string))
			}

			return []*schema.ResourceData{d}, nil
		}

		if len(buf) > 0 {
			if errMsg, errExist := buf[0]["errmsg"].(string); errExist {
				// Log the error
				log.Printf("[DEBUG] SOLIDServer - Unable to import RR (oid): %s (%s)\n", d.Id(), errMsg)
			}
		} else {
			// Log the error
			log.Printf("[DEBUG] SOLIDServer - Unable to find and import RR (oid): %s\n", d.Id())
		}

		// Reporting a failure
		return nil, fmt.Errorf("SOLIDServer - Unable to find and import RR (oid): %s\n", d.Id())
	}

	// Reporting a failure
	return nil, err
}
<commit_msg>DNS RR's property dnsview should require delete/create operation on change<commit_after>package solidserver

import (
	"encoding/json"
	"fmt"
	"github.com/hashicorp/terraform/helper/schema"
	"log"
	"net/url"
	"strconv"
	"strings"
)

func resourcednsrr() *schema.Resource {
	return &schema.Resource{
		Create: resourcednsrrCreate,
		Read:   resourcednsrrRead,
		Update: resourcednsrrUpdate,
		Delete: resourcednsrrDelete,
		Exists: resourcednsrrExists,
		Importer: &schema.ResourceImporter{
			State: resourcednsrrImportState,
		},

		Schema: map[string]*schema.Schema{
			"dnsserver": {
				Type:        schema.TypeString,
				Description: "The managed SMART DNS server name, or DNS server name hosting the RR's zone.",
				Required:    true,
				ForceNew:    true,
			},
			"dnsview": {
				Type:        schema.TypeString,
				Description: "The View name of the RR to create.",
				Optional:    true,
				ForceNew:    true,
				Default:     "",
			},
			"name": {
				Type:        schema.TypeString,
				Description: "The Fully Qualified Domain Name of the RR to create.",
				Required:    true,
				ForceNew:    true,
			},
			"type": {
				Type:         schema.TypeString,
				Description:  "The type of the RR to create (Supported: A, AAAA, CNAME, TXT).",
				ValidateFunc: resourcednsrrvalidatetype,
				Required:     true,
				ForceNew:     true,
			},
			"value": {
				Type:        schema.TypeString,
				Description: "The value od the RR to create.",
				Computed:    false,
				Required:    true,
				ForceNew:    true,
			},
			"ttl": {
				Type:        schema.TypeInt,
				Description: "The DNS Time To Live of the RR to create.",
				Optional:    true,
				Default:     3600,
			},
		},
	}
}

func resourcednsrrvalidatetype(v interface{}, _ string) ([]string, []error) {
	switch strings.ToUpper(v.(string)) {
	case "A":
		return nil, nil
	case "AAAA":
		return nil, nil
	case "PTR":
		return nil, nil
	case "CNAME":
		return nil, nil
	case "DNAME":
		return nil, nil
	case "TXT":
		return nil, nil
	case "NS":
		return nil, nil
	default:
		return nil, []error{fmt.Errorf("Unsupported RR type.")}
	}
}

func resourcednsrrExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	s := meta.(*SOLIDserver)

	// Building parameters
	parameters := url.Values{}
	parameters.Add("rr_id", d.Id())

	log.Printf("[DEBUG] Checking existence of RR (oid): %s\n", d.Id())

	// Sending the read request
	resp, body, err := s.Request("get", "rest/dns_rr_info", &parameters)

	if err == nil {
		var buf [](map[string]interface{})
		json.Unmarshal([]byte(body), &buf)

		// Checking the answer
		if (resp.StatusCode == 200 || resp.StatusCode == 201) && len(buf) > 0 {
			return true, nil
		}

		if len(buf) > 0 {
			if errMsg, errExist := buf[0]["errmsg"].(string); errExist {
				// Log the error
				log.Printf("[DEBUG] SOLIDServer - Unable to find RR (oid): %s (%s)\n", d.Id(), errMsg)
			}
		} else {
			// Log the error
			log.Printf("[DEBUG] SOLIDServer - Unable to find RR (oid): %s\n", d.Id())
		}

		// Unset local ID
		d.SetId("")
	}

	// Reporting a failure
	return false, err
}

func resourcednsrrCreate(d *schema.ResourceData, meta interface{}) error {
	s := meta.(*SOLIDserver)

	// Building parameters
	parameters := url.Values{}
	parameters.Add("add_flag", "new_only")
	parameters.Add("dns_name", d.Get("dnsserver").(string))
	parameters.Add("rr_name", d.Get("name").(string))
	parameters.Add("rr_type", strings.ToUpper(d.Get("type").(string)))
	parameters.Add("value1", d.Get("value").(string))
	parameters.Add("rr_ttl", strconv.Itoa(d.Get("ttl").(int)))

	// Add dnsview parameter if it is supplied
	if len(d.Get("dnsview").(string)) != 0 {
		parameters.Add("dnsview_name", strings.ToLower(d.Get("dnsview").(string)))
	}

	// Sending the creation request
	resp, body, err := s.Request("post", "rest/dns_rr_add", &parameters)

	if err == nil {
		var buf [](map[string]interface{})
		json.Unmarshal([]byte(body), &buf)

		// Checking the answer
		if (resp.StatusCode == 200 || resp.StatusCode == 201) && len(buf) > 0 {
			if oid, oidExist := buf[0]["ret_oid"].(string); oidExist {
				log.Printf("[DEBUG] SOLIDServer - Created RR (oid): %s\n", oid)
				d.SetId(oid)
				return nil
			}
		}

		// Reporting a failure
		if len(buf) > 0 {
			if errMsg, errExist := buf[0]["errmsg"].(string); errExist {
				return fmt.Errorf("SOLIDServer - Unable to create RR: %s (%s)", d.Get("name").(string), errMsg)
			}
		}

		return fmt.Errorf("SOLIDServer - Unable to create RR: %s\n", d.Get("name").(string))
	}

	// Reporting a failure
	return err
}

func resourcednsrrUpdate(d *schema.ResourceData, meta interface{}) error {
	s := meta.(*SOLIDserver)

	// Building parameters
	parameters := url.Values{}
	parameters.Add("rr_id", d.Id())
	parameters.Add("add_flag", "edit_only")
	parameters.Add("dns_name", d.Get("dnsserver").(string))
	parameters.Add("rr_name", d.Get("name").(string))
	parameters.Add("rr_type", strings.ToUpper(d.Get("type").(string)))
	parameters.Add("value1", d.Get("value").(string))
	parameters.Add("rr_ttl", strconv.Itoa(d.Get("ttl").(int)))

	// Add dnsview parameter if it is supplied
	if len(d.Get("dnsview").(string)) != 0 {
		parameters.Add("dnsview_name", strings.ToLower(d.Get("dnsview").(string)))
	}

	// Sending the update request
	resp, body, err := s.Request("put", "rest/dns_rr_add", &parameters)

	if err == nil {
		var buf [](map[string]interface{})
		json.Unmarshal([]byte(body), &buf)

		// Checking the answer
		if (resp.StatusCode == 200 || resp.StatusCode == 201) && len(buf) > 0 {
			if oid, oidExist := buf[0]["ret_oid"].(string); oidExist {
				log.Printf("[DEBUG] SOLIDServer - Updated RR (oid): %s\n", oid)
				d.SetId(oid)
				return nil
			}
		}

		// Reporting a failure
		if len(buf) > 0 {
			if errMsg, errExist := buf[0]["errmsg"].(string); errExist {
				return fmt.Errorf("SOLIDServer - Unable to update RR: %s (%s)", d.Get("name").(string), errMsg)
			}
		}

		return fmt.Errorf("SOLIDServer - Unable to update RR: %s\n", d.Get("name").(string))
	}

	// Reporting a failure
	return err
}

func resourcednsrrDelete(d *schema.ResourceData, meta interface{}) error {
	s := meta.(*SOLIDserver)

	// Building parameters
	parameters := url.Values{}
	parameters.Add("rr_id", d.Id())

	// Add dnsview parameter if it is supplied
	if len(d.Get("dnsview").(string)) != 0 {
		parameters.Add("dnsview_name", strings.ToLower(d.Get("dnsview").(string)))
	}

	// Sending the deletion request
	resp, body, err := s.Request("delete", "rest/dns_rr_delete", &parameters)

	if err == nil {
		var buf [](map[string]interface{})
		json.Unmarshal([]byte(body), &buf)

		// Checking the answer
		if resp.StatusCode != 200 && resp.StatusCode != 204 {
			// Reporting a failure
			if len(buf) > 0 {
				if errMsg, errExist := buf[0]["errmsg"].(string); errExist {
					return fmt.Errorf("SOLIDServer - Unable to delete RR: %s (%s)", d.Get("name").(string), errMsg)
				}
			}

			return fmt.Errorf("SOLIDServer - Unable to delete RR: %s", d.Get("name").(string))
		}

		// Log deletion
		log.Printf("[DEBUG] SOLIDServer - Deleted RR (oid): %s\n", d.Id())

		// Unset local ID
		d.SetId("")

		// Reporting a success
		return nil
	}

	// Reporting a failure
	return err
}

func resourcednsrrRead(d *schema.ResourceData, meta interface{}) error {
	s := meta.(*SOLIDserver)

	// Building parameters
	parameters := url.Values{}
	parameters.Add("rr_id", d.Id())

	// Sending the read request
	resp, body, err := s.Request("get", "rest/dns_rr_info", &parameters)

	if err == nil {
		var buf [](map[string]interface{})
		json.Unmarshal([]byte(body), &buf)

		// Checking the answer
		if resp.StatusCode == 200 && len(buf) > 0 {
			ttl, _ := strconv.Atoi(buf[0]["ttl"].(string))

			d.Set("dnsserver", buf[0]["dns_name"].(string))
			d.Set("name", buf[0]["rr_full_name"].(string))
			d.Set("type", buf[0]["rr_type"].(string))
			d.Set("value", buf[0]["value1"].(string))
			d.Set("ttl", ttl)

			if buf[0]["dnsview_name"].(string) != "#" {
				d.Set("dnsview", buf[0]["dnsview_name"].(string))
			}

			return nil
		}

		if len(buf) > 0 {
			if errMsg, errExist := buf[0]["errmsg"].(string); errExist {
				// Log the error
				log.Printf("[DEBUG] SOLIDServer - Unable to find RR: %s (%s)\n", d.Get("name"), errMsg)
			}
		} else {
			// Log the error
			log.Printf("[DEBUG] SOLIDServer - Unable to find RR (oid): %s\n", d.Id())
		}

		// Do not unset the local ID to avoid inconsistency

		// Reporting a failure
		return fmt.Errorf("SOLIDServer - Unable to find RR: %s\n", d.Get("name").(string))
	}

	// Reporting a failure
	return err
}

func resourcednsrrImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
	s := meta.(*SOLIDserver)

	// Building parameters
	parameters := url.Values{}
	parameters.Add("rr_id", d.Id())

	// Sending the read request
	resp, body, err := s.Request("get", "rest/dns_rr_info", &parameters)

	if err == nil {
		var buf [](map[string]interface{})
		json.Unmarshal([]byte(body), &buf)

		// Checking the answer
		if resp.StatusCode == 200 && len(buf) > 0 {
			ttl, _ := strconv.Atoi(buf[0]["ttl"].(string))

			d.Set("dnsserver", buf[0]["dns_name"].(string))
			d.Set("name", buf[0]["rr_full_name"].(string))
			d.Set("type", buf[0]["rr_type"].(string))
			d.Set("value", buf[0]["value1"].(string))
			d.Set("ttl", ttl)

			if buf[0]["dnsview_name"].(string) != "#" {
				d.Set("dnsview", buf[0]["dnsview_name"].(string))
			}

			return []*schema.ResourceData{d}, nil
		}

		if len(buf) > 0 {
			if errMsg, errExist := buf[0]["errmsg"].(string); errExist {
				// Log the error
				log.Printf("[DEBUG] SOLIDServer - Unable to import RR (oid): %s (%s)\n", d.Id(), errMsg)
			}
		} else {
			// Log the error
			log.Printf("[DEBUG] SOLIDServer - Unable to find and import RR (oid): %s\n", d.Id())
		}

		// Reporting a failure
		return nil, fmt.Errorf("SOLIDServer - Unable to find and import RR (oid): %s\n", d.Id())
	}

	// Reporting a failure
	return nil, err
}
<|endoftext|>


<commit_before>package keystone

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

type ServiceCatalog struct {
	Endpoints []map[string]string
	Type      string
	Name      string
}

type Client struct {
	Token    string
	Catalogs []ServiceCatalog
	authUrl  string
}

type Tenant struct {
	Id          string
	Name        string
	Description string
}

type User struct {
	Id    string
	Name  string
	Email string
}

type Ec2 struct {
	Access string
	Secret string
}

func NewClient(username, password, tenantName, authUrl string) (*Client, error) {
	b := bytes.NewBufferString(fmt.Sprintf(`{"auth": {"passwordCredentials": {"username": "%s", "password":"%s"}, "tenantName": "%s"}}`, username, password, tenantName))
	response, err := http.Post(authUrl+"/tokens", "application/json", b)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	result, _ := ioutil.ReadAll(response.Body)
	var data map[string]map[string]interface{}
	err = json.Unmarshal(result, &data)
	if err != nil {
		return nil, err
	}
	if response.StatusCode > 399 {
		return nil, errors.New(data["error"]["title"].(string))
	}
	token := data["access"]["token"].(map[string]interface{})["id"].(string)
	client := Client{Token: token, authUrl: authUrl}
	catalogs := data["access"]["serviceCatalog"].([]interface{})
	for _, c := range catalogs {
		catalog := c.(map[string]interface{})
		serviceCatalog := ServiceCatalog{
			Name: catalog["name"].(string),
			Type: catalog["type"].(string),
		}
		for _, e := range catalog["endpoints"].([]interface{}) {
			endpoint := map[string]string{}
			for k, v := range e.(map[string]interface{}) {
				endpoint[k] = v.(string)
			}
			serviceCatalog.Endpoints = append(serviceCatalog.Endpoints, endpoint)
		}
		client.Catalogs = append(client.Catalogs, serviceCatalog)
	}
	return &client, nil
}

func (c *Client) Endpoint(service, which string) string {
	var (
		endpoint string
		catalog  ServiceCatalog
	)
	for _, catalog = range c.Catalogs {
		if catalog.Type == service {
			break
		}
	}
	if catalog.Type == service {
		if !strings.Contains(which, "URL") {
			which += "URL"
		}
		endpoint = catalog.Endpoints[0][which] // TODO(fsouza): choose region
	}
	return endpoint
}

func (c *Client) do(method, urlStr string, body io.Reader) (*http.Response, error) {
	request, _ := http.NewRequest(method, urlStr, body)
	request.Header.Set("X-Auth-Token", c.Token)
	if body != nil {
		request.Header.Set("Content-Type", "application/json")
	}
	httpClient := &http.Client{}
	return httpClient.Do(request)
}

func (c *Client) NewTenant(name, description string, enabled bool) (*Tenant, error) {
	b := bytes.NewBufferString(fmt.Sprintf(`{"tenant": {"name": "%s", "description": "%s", "enabled": %t}}`, name, description, enabled))
	response, _ := c.do("POST", c.authUrl+"/tenants", b)
	defer response.Body.Close()
	result, _ := ioutil.ReadAll(response.Body)
	var data map[string]map[string]interface{}
	_ = json.Unmarshal(result, &data)
	tenant := Tenant{
		Id:          data["tenant"]["id"].(string),
		Name:        data["tenant"]["name"].(string),
		Description: data["tenant"]["description"].(string),
	}
	return &tenant, nil
}

func (c *Client) NewUser(name, password, email, tenantId, roleId string, enabled bool) (*User, error) {
	b := bytes.NewBufferString(fmt.Sprintf(`{"user": {"name": "%s", "password": "%s", "tenantId": "%s", "email": "%s", "enabled": %t}}`, name, password, tenantId, email, enabled))
	response, err := c.do("POST", c.authUrl+"/users", b)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	result, _ := ioutil.ReadAll(response.Body)
	var data map[string]map[string]interface{}
	_ = json.Unmarshal(result, &data)
	user := User{
		Id:    data["user"]["id"].(string),
		Name:  data["user"]["name"].(string),
		Email: data["user"]["email"].(string),
	}
	// http://nova.nimbus.dev.globoi.com:35357/v2.0/tenants/d34f63dc72904510a5b3dd25bee6ceaf/users/ce038d17773a4e398df562aa121b7923/roles/OS-KSADM/b07bef22c3264c2f8a4c434eee912b4c
	response, err = c.do("PUT", c.authUrl+"/tenants/"+tenantId+"/users/"+user.Id+"/roles/OS-KSADM/"+roleId, nil)
	if err != nil {
		panic(err)
	}
	return &user, nil
}

func (c *Client) NewEc2(userId, tenantId string) (*Ec2, error) {
	b := bytes.NewBufferString(fmt.Sprintf(`{"tenant_id": "%s"}`, tenantId))
	response, _ := c.do("POST", c.authUrl+"/users/"+userId+"/credentials/OS-EC2", b)
	defer response.Body.Close()
	result, _ := ioutil.ReadAll(response.Body)
	var data map[string]map[string]interface{}
	_ = json.Unmarshal(result, &data)
	ec2 := Ec2{
		Access: data["credential"]["access"].(string),
		Secret: data["credential"]["secret"].(string),
	}
	return &ec2, nil
}

func (c *Client) RemoveEc2(userId, access string) error {
	return c.delete(c.authUrl + "/users/" + userId + "/credentials/OS-EC2/" + access)
}

func (c *Client) RemoveUser(userId, tenantId, roleId string) error {
	// FIXME(fsouza): deal with errors. Keystone keep returning malformed response.
	c.delete(c.authUrl + "/tenants/" + tenantId + "/users/" + userId + "/roles/OS-KSADM/" + roleId)
	return c.delete(c.authUrl + "/users/" + userId)
}

func (c *Client) RemoveTenant(tenantId string) error {
	// FIXME(fsouza): deal with errors. Keystone keep returning malformed response.
	c.delete(c.authUrl + "/tenants/" + tenantId)
	return nil
}

func (c *Client) delete(url string) error {
	if resp, err := c.do("DELETE", url, nil); err != nil {
		return err
	} else if resp.StatusCode > 299 {
		return errorFromResponse(resp)
	}
	return nil
}

func errorFromResponse(response *http.Response) error {
	defer response.Body.Close()
	b, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}
	return errors.New(string(b))
}
<commit_msg>keystone: removed comment<commit_after>package keystone

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

type ServiceCatalog struct {
	Endpoints []map[string]string
	Type      string
	Name      string
}

type Client struct {
	Token    string
	Catalogs []ServiceCatalog
	authUrl  string
}

type Tenant struct {
	Id          string
	Name        string
	Description string
}

type User struct {
	Id    string
	Name  string
	Email string
}

type Ec2 struct {
	Access string
	Secret string
}

func NewClient(username, password, tenantName, authUrl string) (*Client, error) {
	b := bytes.NewBufferString(fmt.Sprintf(`{"auth": {"passwordCredentials": {"username": "%s", "password":"%s"}, "tenantName": "%s"}}`, username, password, tenantName))
	response, err := http.Post(authUrl+"/tokens", "application/json", b)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	result, _ := ioutil.ReadAll(response.Body)
	var data map[string]map[string]interface{}
	err = json.Unmarshal(result, &data)
	if err != nil {
		return nil, err
	}
	if response.StatusCode > 399 {
		return nil, errors.New(data["error"]["title"].(string))
	}
	token := data["access"]["token"].(map[string]interface{})["id"].(string)
	client := Client{Token: token, authUrl: authUrl}
	catalogs := data["access"]["serviceCatalog"].([]interface{})
	for _, c := range catalogs {
		catalog := c.(map[string]interface{})
		serviceCatalog := ServiceCatalog{
			Name: catalog["name"].(string),
			Type: catalog["type"].(string),
		}
		for _, e := range catalog["endpoints"].([]interface{}) {
			endpoint := map[string]string{}
			for k, v := range e.(map[string]interface{}) {
				endpoint[k] = v.(string)
			}
			serviceCatalog.Endpoints = append(serviceCatalog.Endpoints, endpoint)
		}
		client.Catalogs = append(client.Catalogs, serviceCatalog)
	}
	return &client, nil
}

func (c *Client) Endpoint(service, which string) string {
	var (
		endpoint string
		catalog  ServiceCatalog
	)
	for _, catalog = range c.Catalogs {
		if catalog.Type == service {
			break
		}
	}
	if catalog.Type == service {
		if !strings.Contains(which, "URL") {
			which += "URL"
		}
		endpoint = catalog.Endpoints[0][which] // TODO(fsouza): choose region
	}
	return endpoint
}

func (c *Client) do(method, urlStr string, body io.Reader) (*http.Response, error) {
	request, _ := http.NewRequest(method, urlStr, body)
	request.Header.Set("X-Auth-Token", c.Token)
	if body != nil {
		request.Header.Set("Content-Type", "application/json")
	}
	httpClient := &http.Client{}
	return httpClient.Do(request)
}

func (c *Client) NewTenant(name, description string, enabled bool) (*Tenant, error) {
	b := bytes.NewBufferString(fmt.Sprintf(`{"tenant": {"name": "%s", "description": "%s", "enabled": %t}}`, name, description, enabled))
	response, _ := c.do("POST", c.authUrl+"/tenants", b)
	defer response.Body.Close()
	result, _ := ioutil.ReadAll(response.Body)
	var data map[string]map[string]interface{}
	_ = json.Unmarshal(result, &data)
	tenant := Tenant{
		Id:          data["tenant"]["id"].(string),
		Name:        data["tenant"]["name"].(string),
		Description: data["tenant"]["description"].(string),
	}
	return &tenant, nil
}

func (c *Client) NewUser(name, password, email, tenantId, roleId string, enabled bool) (*User, error) {
	b := bytes.NewBufferString(fmt.Sprintf(`{"user": {"name": "%s", "password": "%s", "tenantId": "%s", "email": "%s", "enabled": %t}}`, name, password, tenantId, email, enabled))
	response, err := c.do("POST", c.authUrl+"/users", b)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	result, _ := ioutil.ReadAll(response.Body)
	var data map[string]map[string]interface{}
	_ = json.Unmarshal(result, &data)
	user := User{
		Id:    data["user"]["id"].(string),
		Name:  data["user"]["name"].(string),
		Email: data["user"]["email"].(string),
	}
	response, err = c.do("PUT", c.authUrl+"/tenants/"+tenantId+"/users/"+user.Id+"/roles/OS-KSADM/"+roleId, nil)
	if err != nil {
		panic(err)
	}
	return &user, nil
}

func (c *Client) NewEc2(userId, tenantId string) (*Ec2, error) {
	b := bytes.NewBufferString(fmt.Sprintf(`{"tenant_id": "%s"}`, tenantId))
	response, _ := c.do("POST", c.authUrl+"/users/"+userId+"/credentials/OS-EC2", b)
	defer response.Body.Close()
	result, _ := ioutil.ReadAll(response.Body)
	var data map[string]map[string]interface{}
	_ = json.Unmarshal(result, &data)
	ec2 := Ec2{
		Access: data["credential"]["access"].(string),
		Secret: data["credential"]["secret"].(string),
	}
	return &ec2, nil
}

func (c *Client) RemoveEc2(userId, access string) error {
	return c.delete(c.authUrl + "/users/" + userId + "/credentials/OS-EC2/" + access)
}

func (c *Client) RemoveUser(userId, tenantId, roleId string) error {
	// FIXME(fsouza): deal with errors. Keystone keep returning malformed response.
	c.delete(c.authUrl + "/tenants/" + tenantId + "/users/" + userId + "/roles/OS-KSADM/" + roleId)
	return c.delete(c.authUrl + "/users/" + userId)
}

func (c *Client) RemoveTenant(tenantId string) error {
	// FIXME(fsouza): deal with errors. Keystone keep returning malformed response.
	c.delete(c.authUrl + "/tenants/" + tenantId)
	return nil
}

func (c *Client) delete(url string) error {
	if resp, err := c.do("DELETE", url, nil); err != nil {
		return err
	} else if resp.StatusCode > 299 {
		return errorFromResponse(resp)
	}
	return nil
}

func errorFromResponse(response *http.Response) error {
	defer response.Body.Close()
	b, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}
	return errors.New(string(b))
}
<|endoftext|>


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

package gogs

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"
)

// Permission represents a API permission.
type Permission struct {
	Admin bool `json:"admin"`
	Push  bool `json:"push"`
	Pull  bool `json:"pull"`
}

// Repository represents a API repository.
type Repository struct {
	ID            int64       `json:"id"`
	Owner         *User       `json:"owner"`
	Name          string      `json:"name"`
	FullName      string      `json:"full_name"`
	Description   string      `json:"description"`
	Private       bool        `json:"private"`
	Fork          bool        `json:"fork"`
	Parent        *Repository `json:"parent"`
	Empty         bool        `json:"empty"`
	Mirror        bool        `json:"mirror"`
	Size          int64       `json:"size"`
	HTMLURL       string      `json:"html_url"`
	SSHURL        string      `json:"ssh_url"`
	CloneURL      string      `json:"clone_url"`
	Website       string      `json:"website"`
	Stars         int         `json:"stars_count"`
	Forks         int         `json:"forks_count"`
	Watchers      int         `json:"watchers_count"`
	OpenIssues    int         `json:"open_issues_count"`
	DefaultBranch string      `json:"default_branch"`
	Created       time.Time   `json:"created_at"`
	Updated       time.Time   `json:"updated_at"`
	Permissions   *Permission `json:"permissions,omitempty"`
}

// ListMyRepos lists all repositories for the authenticated user that has access to.
func (c *Client) ListMyRepos() ([]*Repository, error) {
	repos := make([]*Repository, 0, 10)
	return repos, c.getParsedResponse("GET", "/user/repos", nil, nil, &repos)
}

func (c *Client) ListUserRepos(user string) ([]*Repository, error) {
	repos := make([]*Repository, 0, 10)
	return repos, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/repos", user), nil, nil, &repos)
}

func (c *Client) ListOrgRepos(org string) ([]*Repository, error) {
	repos := make([]*Repository, 0, 10)
	return repos, c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/repos", org), nil, nil, &repos)
}

type CreateRepoOption struct {
	Name        string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"`
	Description string `json:"description" binding:"MaxSize(255)"`
	Private     bool   `json:"private"`
	AutoInit    bool   `json:"auto_init"`
	Gitignores  string `json:"gitignores"`
	License     string `json:"license"`
	Readme      string `json:"readme"`
}

// CreateRepo creates a repository for authenticated user.
func (c *Client) CreateRepo(opt CreateRepoOption) (*Repository, error) {
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, err
	}
	repo := new(Repository)
	return repo, c.getParsedResponse("POST", "/user/repos", jsonHeader, bytes.NewReader(body), repo)
}

// CreateOrgRepo creates an organization repository for authenticated user.
func (c *Client) CreateOrgRepo(org string, opt CreateRepoOption) (*Repository, error) {
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, err
	}
	repo := new(Repository)
	return repo, c.getParsedResponse("POST", fmt.Sprintf("/org/%s/repos", org), jsonHeader, bytes.NewReader(body), repo)
}

// GetRepo returns information of a repository of given owner.
func (c *Client) GetRepo(owner, reponame string) (*Repository, error) {
	repo := new(Repository)
	return repo, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s", owner, reponame), nil, nil, repo)
}

// DeleteRepo deletes a repository of user or organization.
func (c *Client) DeleteRepo(owner, repo string) error {
	_, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s", owner, repo), nil, nil)
	return err
}

type MigrateRepoOption struct {
	CloneAddr    string `json:"clone_addr" binding:"Required"`
	AuthUsername string `json:"auth_username"`
	AuthPassword string `json:"auth_password"`
	UID          int    `json:"uid" binding:"Required"`
	RepoName     string `json:"repo_name" binding:"Required"`
	Mirror       bool   `json:"mirror"`
	Private      bool   `json:"private"`
	Description  string `json:"description"`
}

// MigrateRepo migrates a repository from other Git hosting sources for the
// authenticated user.
//
// To migrate a repository for a organization, the authenticated user must be a
// owner of the specified organization.
func (c *Client) MigrateRepo(opt MigrateRepoOption) (*Repository, error) {
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, err
	}
	repo := new(Repository)
	return repo, c.getParsedResponse("POST", "/repos/migrate", jsonHeader, bytes.NewReader(body), repo)
}

type EditIssueTrackerOption struct {
	EnableIssues          *bool   `json:"enable_issues"`
	EnableExternalTracker *bool   `json:"enable_external_tracker"`
	ExternalTrackerURL    *string `json:"external_tracker_url"`
	TrackerURLFormat      *string `json:"tracker_url_format"`
	TrackerIssueStyle     *string `json:"tracker_issue_style"`
}

// EditIssueTracker updates issue tracker options of the repository.
func (c *Client) EditIssueTracker(owner, repo string, opt EditIssueTrackerOption) error {
	body, err := json.Marshal(&opt)
	if err != nil {
		return err
	}
	_, err = c.getResponse("PATCH", fmt.Sprintf("/repos/%s/%s/issue-tracker", owner, repo), jsonHeader, bytes.NewReader(body))
	return err
}

func (c *Client) MirrorSync(owner, repo string) error {
	_, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/mirror-sync", owner, repo), jsonHeader, nil)
	return err
}
<commit_msg>repo: EditWiki implementation (#113)<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package gogs

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"
)

// Permission represents a API permission.
type Permission struct {
	Admin bool `json:"admin"`
	Push  bool `json:"push"`
	Pull  bool `json:"pull"`
}

// Repository represents a API repository.
type Repository struct {
	ID            int64       `json:"id"`
	Owner         *User       `json:"owner"`
	Name          string      `json:"name"`
	FullName      string      `json:"full_name"`
	Description   string      `json:"description"`
	Private       bool        `json:"private"`
	Fork          bool        `json:"fork"`
	Parent        *Repository `json:"parent"`
	Empty         bool        `json:"empty"`
	Mirror        bool        `json:"mirror"`
	Size          int64       `json:"size"`
	HTMLURL       string      `json:"html_url"`
	SSHURL        string      `json:"ssh_url"`
	CloneURL      string      `json:"clone_url"`
	Website       string      `json:"website"`
	Stars         int         `json:"stars_count"`
	Forks         int         `json:"forks_count"`
	Watchers      int         `json:"watchers_count"`
	OpenIssues    int         `json:"open_issues_count"`
	DefaultBranch string      `json:"default_branch"`
	Created       time.Time   `json:"created_at"`
	Updated       time.Time   `json:"updated_at"`
	Permissions   *Permission `json:"permissions,omitempty"`
}

// ListMyRepos lists all repositories for the authenticated user that has access to.
func (c *Client) ListMyRepos() ([]*Repository, error) {
	repos := make([]*Repository, 0, 10)
	return repos, c.getParsedResponse("GET", "/user/repos", nil, nil, &repos)
}

func (c *Client) ListUserRepos(user string) ([]*Repository, error) {
	repos := make([]*Repository, 0, 10)
	return repos, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/repos", user), nil, nil, &repos)
}

func (c *Client) ListOrgRepos(org string) ([]*Repository, error) {
	repos := make([]*Repository, 0, 10)
	return repos, c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/repos", org), nil, nil, &repos)
}

type CreateRepoOption struct {
	Name        string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"`
	Description string `json:"description" binding:"MaxSize(255)"`
	Private     bool   `json:"private"`
	AutoInit    bool   `json:"auto_init"`
	Gitignores  string `json:"gitignores"`
	License     string `json:"license"`
	Readme      string `json:"readme"`
}

// CreateRepo creates a repository for authenticated user.
func (c *Client) CreateRepo(opt CreateRepoOption) (*Repository, error) {
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, err
	}
	repo := new(Repository)
	return repo, c.getParsedResponse("POST", "/user/repos", jsonHeader, bytes.NewReader(body), repo)
}

// CreateOrgRepo creates an organization repository for authenticated user.
func (c *Client) CreateOrgRepo(org string, opt CreateRepoOption) (*Repository, error) {
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, err
	}
	repo := new(Repository)
	return repo, c.getParsedResponse("POST", fmt.Sprintf("/org/%s/repos", org), jsonHeader, bytes.NewReader(body), repo)
}

// GetRepo returns information of a repository of given owner.
func (c *Client) GetRepo(owner, reponame string) (*Repository, error) {
	repo := new(Repository)
	return repo, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s", owner, reponame), nil, nil, repo)
}

// DeleteRepo deletes a repository of user or organization.
func (c *Client) DeleteRepo(owner, repo string) error {
	_, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s", owner, repo), nil, nil)
	return err
}

type MigrateRepoOption struct {
	CloneAddr    string `json:"clone_addr" binding:"Required"`
	AuthUsername string `json:"auth_username"`
	AuthPassword string `json:"auth_password"`
	UID          int    `json:"uid" binding:"Required"`
	RepoName     string `json:"repo_name" binding:"Required"`
	Mirror       bool   `json:"mirror"`
	Private      bool   `json:"private"`
	Description  string `json:"description"`
}

// MigrateRepo migrates a repository from other Git hosting sources for the
// authenticated user.
//
// To migrate a repository for a organization, the authenticated user must be a
// owner of the specified organization.
func (c *Client) MigrateRepo(opt MigrateRepoOption) (*Repository, error) {
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, err
	}
	repo := new(Repository)
	return repo, c.getParsedResponse("POST", "/repos/migrate", jsonHeader, bytes.NewReader(body), repo)
}

type EditIssueTrackerOption struct {
	EnableIssues          *bool   `json:"enable_issues"`
	EnableExternalTracker *bool   `json:"enable_external_tracker"`
	ExternalTrackerURL    *string `json:"external_tracker_url"`
	TrackerURLFormat      *string `json:"tracker_url_format"`
	TrackerIssueStyle     *string `json:"tracker_issue_style"`
}

// EditIssueTracker updates issue tracker options of the repository.
func (c *Client) EditIssueTracker(owner, repo string, opt EditIssueTrackerOption) error {
	body, err := json.Marshal(&opt)
	if err != nil {
		return err
	}
	_, err = c.getResponse("PATCH", fmt.Sprintf("/repos/%s/%s/issue-tracker", owner, repo), jsonHeader, bytes.NewReader(body))
	return err
}

type EditWikiOption struct {
	EnableWiki         *bool   `json:"enable_wiki"`
	AllowPublicWiki    *bool   `json:"allow_public_wiki"`
	EnableExternalWiki *bool   `json:"enable_external_wiki"`
	ExternalWikiURL    *string `json:"external_wiki_url"`
}

// EditWiki updates wiki options of the repository.
func (c *Client) EditWiki(owner, repo string, opt EditWikiOption) error {
	body, err := json.Marshal(&opt)
	if err != nil {
		return err
	}
	_, err = c.getResponse("PATCH", fmt.Sprintf("/repos/%s/%s/wiki", owner, repo), jsonHeader, bytes.NewReader(body))
	return err
}

func (c *Client) MirrorSync(owner, repo string) error {
	_, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/mirror-sync", owner, repo), jsonHeader, nil)
	return err
}
<|endoftext|>