<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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.

// The cmdline provider fetches a remote configuration from the URL specified
// in the kernel boot option "coreos.config.url".

package cmdline

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

	"github.com/coreos/ignition/config"
	"github.com/coreos/ignition/src/log"
	"github.com/coreos/ignition/src/providers"
	"github.com/coreos/ignition/src/providers/util"
	"github.com/coreos/ignition/src/systemd"
)

const (
	name           = "cmdline"
	initialBackoff = 100 * time.Millisecond
	maxBackoff     = 30 * time.Second
	cmdlinePath    = "/proc/cmdline"
	cmdlineUrlFlag = "coreos.config.url"
	oemDevicePath  = "/dev/disk/by-label/OEM" // Device link where oem partition is found.
	oemDirPath     = "/sysroot/usr/share/oem" // OEM dir within root fs to consider for pxe scenarios.
	oemMountPath   = "/mnt/oem"               // Mountpoint where oem partition is mounted when present.
)

func init() {
	providers.Register(creator{})
}

type creator struct{}

func (creator) Name() string {
	return name
}

func (creator) Create(logger log.Logger) providers.Provider {
	return &provider{
		logger:  logger,
		backoff: initialBackoff,
		path:    cmdlinePath,
		client: &http.Client{
			Timeout: 10 * time.Second,
		},
	}
}

type provider struct {
	logger    log.Logger
	backoff   time.Duration
	path      string
	client    *http.Client
	configUrl string
	rawConfig []byte
}

func (provider) Name() string {
	return name
}

func (p provider) FetchConfig() (config.Config, error) {
	if p.rawConfig == nil {
		return config.Config{}, nil
	} else {
		return config.Parse(p.rawConfig)
	}
}

func (p *provider) IsOnline() bool {
	if p.configUrl == "" {
		args, err := ioutil.ReadFile(p.path)
		if err != nil {
			p.logger.Err("couldn't read cmdline")
			return false
		}

		p.configUrl = parseCmdline(args)
		p.logger.Debug("parsed url from cmdline: %q", p.configUrl)
		if p.configUrl == "" {
			// If the cmdline flag wasn't provided, just no-op.
			p.logger.Info("no config URL provided")
			return true
		}
	}

	return p.getRawConfig()

}

func (p provider) ShouldRetry() bool {
	return true
}

func (p *provider) BackoffDuration() time.Duration {
	return util.ExpBackoff(&p.backoff, maxBackoff)
}

func parseCmdline(cmdline []byte) (url string) {
	for _, arg := range strings.Split(string(cmdline), " ") {
		parts := strings.SplitN(strings.TrimSpace(arg), "=", 2)
		key := parts[0]

		if key != cmdlineUrlFlag {
			continue
		}

		if len(parts) == 2 {
			url = parts[1]
		}
	}

	return
}

// getRawConfig gets the raw configuration data from p.configUrl.
// Supported URL schemes are:
// http://	remote resource accessed via http
// oem://	local file in /sysroot/usr/share/oem or /mnt/oem
func (p *provider) getRawConfig() bool {
	url, err := url.Parse(p.configUrl)
	if err != nil {
		p.logger.Err("failed to parse url: %v", err)
		return false
	}

	switch url.Scheme {
	case "http":
		if resp, err := p.client.Get(p.configUrl); err == nil {
			defer resp.Body.Close()

			switch resp.StatusCode {
			case http.StatusOK, http.StatusNoContent:
			default:
				p.logger.Debug("failed fetching: HTTP status: %s",
					resp.Status)
				return false
			}

			p.logger.Debug("successfully fetched")
			p.rawConfig, err = ioutil.ReadAll(resp.Body)
			if err != nil {
				p.logger.Err("failed to read body: %v", err)
				return false
			}
		} else {
			p.logger.Warning("failed fetching: %v", err)
			return false
		}
	case "oem":
		path := filepath.Clean(url.Path)
		if !filepath.IsAbs(path) {
			p.logger.Err("oem path is not absolute: %q", url.Path)
			return false
		}

		// check if present under oemDirPath, if so use it.
		absPath := filepath.Join(oemDirPath, path)
		p.rawConfig, err = ioutil.ReadFile(absPath)
		if os.IsNotExist(err) {
			p.logger.Info("oem config not found in %q, trying %q",
				oemDirPath, oemMountPath)

			// try oemMountPath, requires mounting it.
			err = p.mountOEM()
			if err == nil {
				absPath := filepath.Join(oemMountPath, path)
				p.rawConfig, err = ioutil.ReadFile(absPath)
				p.umountOEM()
			}
		}

		if err != nil {
			p.logger.Err("failed to read oem config: %v", err)
			return false
		}
	default:
		p.logger.Err("unsupported url scheme: %q", url.Scheme)
		return false
	}

	return true
}

// mountOEM waits for the presence of and mounts the oem partition @ oemMountPath.
func (p *provider) mountOEM() error {
	dev := []string{oemDevicePath}
	if err := systemd.WaitOnDevices(dev, "oem-cmdline"); err != nil {
		p.logger.Err("failed to wait for oem device: %v", err)
		return err
	}

	if err := os.MkdirAll(oemMountPath, 0700); err != nil {
		p.logger.Err("failed to create oem mount point: %v", err)
		return err
	}

	if err := p.logger.LogOp(
		func() error {
			return syscall.Mount(dev[0], oemMountPath, "ext4", 0, "")
		},
		"mounting %q at %q", oemDevicePath, oemMountPath,
	); err != nil {
		return fmt.Errorf("failed to mount device %q at %q: %v",
			oemDevicePath, oemMountPath, err)
	}

	return nil
}

// umountOEM unmounts the oem partition @ oemMountPath.
func (p *provider) umountOEM() {
	p.logger.LogOp(
		func() error { return syscall.Unmount(oemMountPath, 0) },
		"unmounting %q", oemMountPath,
	)
}
<commit_msg>providers/cmdline: fix OEM root path for PXE image<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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.

// The cmdline provider fetches a remote configuration from the URL specified
// in the kernel boot option "coreos.config.url".

package cmdline

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

	"github.com/coreos/ignition/config"
	"github.com/coreos/ignition/src/log"
	"github.com/coreos/ignition/src/providers"
	"github.com/coreos/ignition/src/providers/util"
	"github.com/coreos/ignition/src/systemd"
)

const (
	name           = "cmdline"
	initialBackoff = 100 * time.Millisecond
	maxBackoff     = 30 * time.Second
	cmdlinePath    = "/proc/cmdline"
	cmdlineUrlFlag = "coreos.config.url"
	oemDevicePath  = "/dev/disk/by-label/OEM" // Device link where oem partition is found.
	oemDirPath     = "/usr/share/oem"         // OEM dir within root fs to consider for pxe scenarios.
	oemMountPath   = "/mnt/oem"               // Mountpoint where oem partition is mounted when present.
)

func init() {
	providers.Register(creator{})
}

type creator struct{}

func (creator) Name() string {
	return name
}

func (creator) Create(logger log.Logger) providers.Provider {
	return &provider{
		logger:  logger,
		backoff: initialBackoff,
		path:    cmdlinePath,
		client: &http.Client{
			Timeout: 10 * time.Second,
		},
	}
}

type provider struct {
	logger    log.Logger
	backoff   time.Duration
	path      string
	client    *http.Client
	configUrl string
	rawConfig []byte
}

func (provider) Name() string {
	return name
}

func (p provider) FetchConfig() (config.Config, error) {
	if p.rawConfig == nil {
		return config.Config{}, nil
	} else {
		return config.Parse(p.rawConfig)
	}
}

func (p *provider) IsOnline() bool {
	if p.configUrl == "" {
		args, err := ioutil.ReadFile(p.path)
		if err != nil {
			p.logger.Err("couldn't read cmdline")
			return false
		}

		p.configUrl = parseCmdline(args)
		p.logger.Debug("parsed url from cmdline: %q", p.configUrl)
		if p.configUrl == "" {
			// If the cmdline flag wasn't provided, just no-op.
			p.logger.Info("no config URL provided")
			return true
		}
	}

	return p.getRawConfig()

}

func (p provider) ShouldRetry() bool {
	return true
}

func (p *provider) BackoffDuration() time.Duration {
	return util.ExpBackoff(&p.backoff, maxBackoff)
}

func parseCmdline(cmdline []byte) (url string) {
	for _, arg := range strings.Split(string(cmdline), " ") {
		parts := strings.SplitN(strings.TrimSpace(arg), "=", 2)
		key := parts[0]

		if key != cmdlineUrlFlag {
			continue
		}

		if len(parts) == 2 {
			url = parts[1]
		}
	}

	return
}

// getRawConfig gets the raw configuration data from p.configUrl.
// Supported URL schemes are:
// http://	remote resource accessed via http
// oem://	local file in /sysroot/usr/share/oem or /mnt/oem
func (p *provider) getRawConfig() bool {
	url, err := url.Parse(p.configUrl)
	if err != nil {
		p.logger.Err("failed to parse url: %v", err)
		return false
	}

	switch url.Scheme {
	case "http":
		if resp, err := p.client.Get(p.configUrl); err == nil {
			defer resp.Body.Close()

			switch resp.StatusCode {
			case http.StatusOK, http.StatusNoContent:
			default:
				p.logger.Debug("failed fetching: HTTP status: %s",
					resp.Status)
				return false
			}

			p.logger.Debug("successfully fetched")
			p.rawConfig, err = ioutil.ReadAll(resp.Body)
			if err != nil {
				p.logger.Err("failed to read body: %v", err)
				return false
			}
		} else {
			p.logger.Warning("failed fetching: %v", err)
			return false
		}
	case "oem":
		path := filepath.Clean(url.Path)
		if !filepath.IsAbs(path) {
			p.logger.Err("oem path is not absolute: %q", url.Path)
			return false
		}

		// check if present under oemDirPath, if so use it.
		absPath := filepath.Join(oemDirPath, path)
		p.rawConfig, err = ioutil.ReadFile(absPath)
		if os.IsNotExist(err) {
			p.logger.Info("oem config not found in %q, trying %q",
				oemDirPath, oemMountPath)

			// try oemMountPath, requires mounting it.
			err = p.mountOEM()
			if err == nil {
				absPath := filepath.Join(oemMountPath, path)
				p.rawConfig, err = ioutil.ReadFile(absPath)
				p.umountOEM()
			}
		}

		if err != nil {
			p.logger.Err("failed to read oem config: %v", err)
			return false
		}
	default:
		p.logger.Err("unsupported url scheme: %q", url.Scheme)
		return false
	}

	return true
}

// mountOEM waits for the presence of and mounts the oem partition @ oemMountPath.
func (p *provider) mountOEM() error {
	dev := []string{oemDevicePath}
	if err := systemd.WaitOnDevices(dev, "oem-cmdline"); err != nil {
		p.logger.Err("failed to wait for oem device: %v", err)
		return err
	}

	if err := os.MkdirAll(oemMountPath, 0700); err != nil {
		p.logger.Err("failed to create oem mount point: %v", err)
		return err
	}

	if err := p.logger.LogOp(
		func() error {
			return syscall.Mount(dev[0], oemMountPath, "ext4", 0, "")
		},
		"mounting %q at %q", oemDevicePath, oemMountPath,
	); err != nil {
		return fmt.Errorf("failed to mount device %q at %q: %v",
			oemDevicePath, oemMountPath, err)
	}

	return nil
}

// umountOEM unmounts the oem partition @ oemMountPath.
func (p *provider) umountOEM() {
	p.logger.LogOp(
		func() error { return syscall.Unmount(oemMountPath, 0) },
		"unmounting %q", oemMountPath,
	)
}
<|endoftext|>


<commit_before>// Copyright 2015 Cutehacks AS. All rights reserved.
// License can be found in the LICENSE file.

package commands

import (
	"bufio"
	"encoding/xml"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"qpm.io/common"
	"qpm.io/qpm/core"
	"strings"
)

type CheckCommand struct {
	BaseCommand
	pkg *common.PackageWrapper
}

func NewCheckCommand(ctx core.Context) *CheckCommand {
	return &CheckCommand{
		BaseCommand: BaseCommand{
			Ctx: ctx,
		},
	}
}

func (c CheckCommand) Description() string {
	return "Checks the package for common errors"
}

func (c *CheckCommand) RegisterFlags(flags *flag.FlagSet) {
}

func (c *CheckCommand) Run() error {

	// check the package file
	var err error
	c.pkg, err = common.LoadPackage("")
	if err != nil {
		c.Error(err)
		return err
	}

	c.pkg.Validate()

	// check the LICENSE file
	_, err = os.Stat(core.License)
	if err != nil {
		c.Error(err)
		return err
	}

	// check the .pri file
	_, err = os.Stat(c.pkg.PriFile())
	if err != nil {
		c.Error(err)
		return err
	}

	// check the .qrc file
	_, err = os.Stat(c.pkg.QrcFile())
	if err != nil {
		c.Error(err)
		return err
	}

	var prefix string
	prefix, err = c.qrc()
	if err != nil {
		c.Error(err)
		return err
	}
	if prefix != c.pkg.QrcPrefix() {
		c.Error(fmt.Errorf("the QRC prefix (%s) does not equal (%s)", prefix, c.pkg.QrcPrefix()))
	}

	// check the qmldir file
	_, err = os.Stat("qmldir")
	if err != nil {
		c.Error(err)
		return err
	}
	var module string
	module, err = c.qmldir()
	if err != nil {
		c.Error(err)
		return err
	}

	if module != c.pkg.Name {
		c.Error(fmt.Errorf("the qmldir module (%s) does not equal (%s)", module, c.pkg.Name))
	}

	fmt.Printf("OK!\n")

	return nil
}

type QRC_File struct {
	XMLName xml.Name `xml:"file"`
	Content string   `xml:",chardata"`
}

type QRC_Resource struct {
	XMLName xml.Name   `xml:"qresource"`
	Prefix  string     `xml:"prefix,attr"`
	Files   []QRC_File `xml:"file"`
}

type QRC_RCC struct {
	XMLName   xml.Name       `xml:"RCC"`
	Resources []QRC_Resource `xml:"qresource"`
}

func (c *CheckCommand) qrc() (prefix string, err error) {

	file, err := os.Open(c.pkg.QrcFile())
	if err != nil {
		return "", err
	}
	defer file.Close()

	data, _ := ioutil.ReadAll(file)

	var rcc QRC_RCC
	xml.Unmarshal(data, &rcc)

	if rcc.Resources != nil {
		return rcc.Resources[0].Prefix, nil
	}

	return "", err
}

func (c *CheckCommand) qmldir() (prefix string, err error) {

	file, err := os.Open("qmldir")
	if err != nil {
		return "", err
	}
	defer file.Close()

	const key = "module "
	const key_len = len(key)

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		i := strings.Index(line, key)
		if i != -1 {
			return line[i+key_len : len(line)], nil
		}
	}

	return "", err
}
<commit_msg>Don't require .qrc and qmldir files in a package, but give warnings.<commit_after>// Copyright 2015 Cutehacks AS. All rights reserved.
// License can be found in the LICENSE file.

package commands

import (
	"bufio"
	"encoding/xml"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"qpm.io/common"
	"qpm.io/qpm/core"
	"strings"
)

type CheckCommand struct {
	BaseCommand
	pkg *common.PackageWrapper
}

func NewCheckCommand(ctx core.Context) *CheckCommand {
	return &CheckCommand{
		BaseCommand: BaseCommand{
			Ctx: ctx,
		},
	}
}

func (c CheckCommand) Description() string {
	return "Checks the package for common errors"
}

func (c *CheckCommand) RegisterFlags(flags *flag.FlagSet) {
}

func (c *CheckCommand) Run() error {

	// check the package file
	var err error
	c.pkg, err = common.LoadPackage("")
	if err != nil {
		c.Error(err)
		return err
	}

	c.pkg.Validate()

	// check the LICENSE file
	_, err = os.Stat(core.License)
	if err != nil {
		c.Error(err)
		return err
	}

	// check the .pri file
	_, err = os.Stat(c.pkg.PriFile())
	if err != nil {
		c.Error(err)
		return err
	}

	// check the .qrc file
	_, err = os.Stat(c.pkg.QrcFile())
	if err != nil {
		c.Warning(err.Error())
	}

	var prefix string
	prefix, err = c.qrc()
	if err != nil {
		c.Warning(err.Error())
	} else if prefix != c.pkg.QrcPrefix() {
		c.Error(fmt.Errorf("the QRC prefix (%s) does not equal (%s)", prefix, c.pkg.QrcPrefix()))
	}

	// check the qmldir file
	_, err = os.Stat("qmldir")
	if err != nil {
		c.Warning(err.Error())
	} else {
		var module string
		module, err = c.qmldir()
		if err != nil {
			c.Error(err)
			return err
		}
		if module != c.pkg.Name {
			c.Error(fmt.Errorf("the qmldir module (%s) does not equal (%s)", module, c.pkg.Name))
		}
	}

	fmt.Printf("OK!\n")

	return nil
}

type QRC_File struct {
	XMLName xml.Name `xml:"file"`
	Content string   `xml:",chardata"`
}

type QRC_Resource struct {
	XMLName xml.Name   `xml:"qresource"`
	Prefix  string     `xml:"prefix,attr"`
	Files   []QRC_File `xml:"file"`
}

type QRC_RCC struct {
	XMLName   xml.Name       `xml:"RCC"`
	Resources []QRC_Resource `xml:"qresource"`
}

func (c *CheckCommand) qrc() (prefix string, err error) {

	file, err := os.Open(c.pkg.QrcFile())
	if err != nil {
		return "", err
	}
	defer file.Close()

	data, _ := ioutil.ReadAll(file)

	var rcc QRC_RCC
	xml.Unmarshal(data, &rcc)

	if rcc.Resources != nil {
		return rcc.Resources[0].Prefix, nil
	}

	return "", err
}

func (c *CheckCommand) qmldir() (prefix string, err error) {

	file, err := os.Open("qmldir")
	if err != nil {
		return "", err
	}
	defer file.Close()

	const key = "module "
	const key_len = len(key)

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		i := strings.Index(line, key)
		if i != -1 {
			return line[i+key_len : len(line)], nil
		}
	}

	return "", err
}
<|endoftext|>


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

package runtime_test

import (
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

var lldbPath string

func checkLldbPython(t *testing.T) {
	cmd := exec.Command("lldb", "-P")
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Skipf("skipping due to issue running lldb: %v\n%s", err, out)
	}
	lldbPath = strings.TrimSpace(string(out))

	cmd = exec.Command("/usr/bin/python2.7", "-c", "import sys;sys.path.append(sys.argv[1]);import lldb; print('go lldb python support')", lldbPath)
	out, err = cmd.CombinedOutput()

	if err != nil {
		t.Skipf("skipping due to issue running python: %v\n%s", err, out)
	}
	if string(out) != "go lldb python support\n" {
		t.Skipf("skipping due to lack of python lldb support: %s", out)
	}

	if runtime.GOOS == "darwin" {
		// Try to see if we have debugging permissions.
		cmd = exec.Command("/usr/sbin/DevToolsSecurity", "-status")
		out, err = cmd.CombinedOutput()
		if err != nil {
			t.Skipf("DevToolsSecurity failed: %v", err)
		} else if !strings.Contains(string(out), "enabled") {
			t.Skip(string(out))
		}
		cmd = exec.Command("/usr/bin/groups")
		out, err = cmd.CombinedOutput()
		if err != nil {
			t.Skipf("groups failed: %v", err)
		} else if !strings.Contains(string(out), "_developer") {
			t.Skip("Not in _developer group")
		}
	}
}

const lldbHelloSource = `
package main
import "fmt"
func main() {
	mapvar := make(map[string]string,5)
	mapvar["abc"] = "def"
	mapvar["ghi"] = "jkl"
	intvar := 42
	ptrvar := &intvar
	fmt.Println("hi") // line 10
	_ = ptrvar
}
`

const lldbScriptSource = `
import sys
sys.path.append(sys.argv[1])
import lldb
import os

TIMEOUT_SECS = 5

debugger = lldb.SBDebugger.Create()
debugger.SetAsync(True)
target = debugger.CreateTargetWithFileAndArch("a.exe", None)
if target:
  print "Created target"
  main_bp = target.BreakpointCreateByLocation("main.go", 10)
  if main_bp:
    print "Created breakpoint"
  process = target.LaunchSimple(None, None, os.getcwd())
  if process:
    print "Process launched"
    listener = debugger.GetListener()
    process.broadcaster.AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged)
    while True:
      event = lldb.SBEvent()
      if listener.WaitForEvent(TIMEOUT_SECS, event):
        if lldb.SBProcess.GetRestartedFromEvent(event):
          continue
        state = process.GetState()
        if state in [lldb.eStateUnloaded, lldb.eStateLaunching, lldb.eStateRunning]:
          continue
      else:
        print "Timeout launching"
      break
    if state == lldb.eStateStopped:
      for t in process.threads:
        if t.GetStopReason() == lldb.eStopReasonBreakpoint:
          print "Hit breakpoint"
          frame = t.GetFrameAtIndex(0)
          if frame:
            if frame.line_entry:
              print "Stopped at %s:%d" % (frame.line_entry.file.basename, frame.line_entry.line)
            if frame.function:
              print "Stopped in %s" % (frame.function.name,)
            var = frame.FindVariable('intvar')
            if var:
              print "intvar = %s" % (var.GetValue(),)
            else:
              print "no intvar"
    else:
      print "Process state", state
    process.Destroy()
else:
  print "Failed to create target a.exe"

lldb.SBDebugger.Destroy(debugger)
sys.exit()
`

const expectedLldbOutput = `Created target
Created breakpoint
Process launched
Hit breakpoint
Stopped at main.go:10
Stopped in main.main
intvar = 42
`

func TestLldbPython(t *testing.T) {
	testenv.MustHaveGoBuild(t)
	if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final {
		t.Skip("gdb test can fail with GOROOT_FINAL pending")
	}

	checkLldbPython(t)

	dir, err := ioutil.TempDir("", "go-build")
	if err != nil {
		t.Fatalf("failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(dir)

	src := filepath.Join(dir, "main.go")
	err = ioutil.WriteFile(src, []byte(lldbHelloSource), 0644)
	if err != nil {
		t.Fatalf("failed to create file: %v", err)
	}

	// As of 2018-07-17, lldb doesn't support compressed DWARF, so
	// disable it for this test.
	cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-ldflags=-compressdwarf=false", "-o", "a.exe")
	cmd.Dir = dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("building source %v\n%s", err, out)
	}

	src = filepath.Join(dir, "script.py")
	err = ioutil.WriteFile(src, []byte(lldbScriptSource), 0755)
	if err != nil {
		t.Fatalf("failed to create script: %v", err)
	}

	cmd = exec.Command("/usr/bin/python2.7", "script.py", lldbPath)
	cmd.Dir = dir
	got, _ := cmd.CombinedOutput()

	if string(got) != expectedLldbOutput {
		if strings.Contains(string(got), "Timeout launching") {
			t.Skip("Timeout launching")
		}
		t.Fatalf("Unexpected lldb output:\n%s", got)
	}
}
<commit_msg>runtime: fix TestLldbPython test with modules enabled<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime_test

import (
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

var lldbPath string

func checkLldbPython(t *testing.T) {
	cmd := exec.Command("lldb", "-P")
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Skipf("skipping due to issue running lldb: %v\n%s", err, out)
	}
	lldbPath = strings.TrimSpace(string(out))

	cmd = exec.Command("/usr/bin/python2.7", "-c", "import sys;sys.path.append(sys.argv[1]);import lldb; print('go lldb python support')", lldbPath)
	out, err = cmd.CombinedOutput()

	if err != nil {
		t.Skipf("skipping due to issue running python: %v\n%s", err, out)
	}
	if string(out) != "go lldb python support\n" {
		t.Skipf("skipping due to lack of python lldb support: %s", out)
	}

	if runtime.GOOS == "darwin" {
		// Try to see if we have debugging permissions.
		cmd = exec.Command("/usr/sbin/DevToolsSecurity", "-status")
		out, err = cmd.CombinedOutput()
		if err != nil {
			t.Skipf("DevToolsSecurity failed: %v", err)
		} else if !strings.Contains(string(out), "enabled") {
			t.Skip(string(out))
		}
		cmd = exec.Command("/usr/bin/groups")
		out, err = cmd.CombinedOutput()
		if err != nil {
			t.Skipf("groups failed: %v", err)
		} else if !strings.Contains(string(out), "_developer") {
			t.Skip("Not in _developer group")
		}
	}
}

const lldbHelloSource = `
package main
import "fmt"
func main() {
	mapvar := make(map[string]string,5)
	mapvar["abc"] = "def"
	mapvar["ghi"] = "jkl"
	intvar := 42
	ptrvar := &intvar
	fmt.Println("hi") // line 10
	_ = ptrvar
}
`

const lldbScriptSource = `
import sys
sys.path.append(sys.argv[1])
import lldb
import os

TIMEOUT_SECS = 5

debugger = lldb.SBDebugger.Create()
debugger.SetAsync(True)
target = debugger.CreateTargetWithFileAndArch("a.exe", None)
if target:
  print "Created target"
  main_bp = target.BreakpointCreateByLocation("main.go", 10)
  if main_bp:
    print "Created breakpoint"
  process = target.LaunchSimple(None, None, os.getcwd())
  if process:
    print "Process launched"
    listener = debugger.GetListener()
    process.broadcaster.AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged)
    while True:
      event = lldb.SBEvent()
      if listener.WaitForEvent(TIMEOUT_SECS, event):
        if lldb.SBProcess.GetRestartedFromEvent(event):
          continue
        state = process.GetState()
        if state in [lldb.eStateUnloaded, lldb.eStateLaunching, lldb.eStateRunning]:
          continue
      else:
        print "Timeout launching"
      break
    if state == lldb.eStateStopped:
      for t in process.threads:
        if t.GetStopReason() == lldb.eStopReasonBreakpoint:
          print "Hit breakpoint"
          frame = t.GetFrameAtIndex(0)
          if frame:
            if frame.line_entry:
              print "Stopped at %s:%d" % (frame.line_entry.file.basename, frame.line_entry.line)
            if frame.function:
              print "Stopped in %s" % (frame.function.name,)
            var = frame.FindVariable('intvar')
            if var:
              print "intvar = %s" % (var.GetValue(),)
            else:
              print "no intvar"
    else:
      print "Process state", state
    process.Destroy()
else:
  print "Failed to create target a.exe"

lldb.SBDebugger.Destroy(debugger)
sys.exit()
`

const expectedLldbOutput = `Created target
Created breakpoint
Process launched
Hit breakpoint
Stopped at main.go:10
Stopped in main.main
intvar = 42
`

func TestLldbPython(t *testing.T) {
	testenv.MustHaveGoBuild(t)
	if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final {
		t.Skip("gdb test can fail with GOROOT_FINAL pending")
	}

	checkLldbPython(t)

	dir, err := ioutil.TempDir("", "go-build")
	if err != nil {
		t.Fatalf("failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(dir)

	src := filepath.Join(dir, "main.go")
	err = ioutil.WriteFile(src, []byte(lldbHelloSource), 0644)
	if err != nil {
		t.Fatalf("failed to create src file: %v", err)
	}

	mod := filepath.Join(dir, "go.mod")
	err = ioutil.WriteFile(mod, []byte("module lldbtest"), 0644)
	if err != nil {
		t.Fatalf("failed to create mod file: %v", err)
	}

	// As of 2018-07-17, lldb doesn't support compressed DWARF, so
	// disable it for this test.
	cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-ldflags=-compressdwarf=false", "-o", "a.exe")
	cmd.Dir = dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("building source %v\n%s", err, out)
	}

	src = filepath.Join(dir, "script.py")
	err = ioutil.WriteFile(src, []byte(lldbScriptSource), 0755)
	if err != nil {
		t.Fatalf("failed to create script: %v", err)
	}

	cmd = exec.Command("/usr/bin/python2.7", "script.py", lldbPath)
	cmd.Dir = dir
	got, _ := cmd.CombinedOutput()

	if string(got) != expectedLldbOutput {
		if strings.Contains(string(got), "Timeout launching") {
			t.Skip("Timeout launching")
		}
		t.Fatalf("Unexpected lldb output:\n%s", got)
	}
}
<|endoftext|>


<commit_before>package testutil

import "sync/atomic"

var nextPort uint32 = 40000

// UniquePort generates a likely unique port, so that multiple servers can run
// concurrently. Note that it does not actually check that the port is free,
// but uses atomics and a fairly highly port range to maximize the likelihood
// that the port is available.
func UniquePort() uint16 {
	port := uint16(atomic.AddUint32(&nextPort, 1))

	if port == 0 {
		panic("ran out of ports!")
	}

	return port
}
<commit_msg>Up the test port range<commit_after>package testutil

import "sync/atomic"

var nextPort uint32 = 41300

// UniquePort generates a likely unique port, so that multiple servers can run
// concurrently. Note that it does not actually check that the port is free,
// but uses atomics and a fairly highly port range to maximize the likelihood
// that the port is available.
func UniquePort() uint16 {
	port := uint16(atomic.AddUint32(&nextPort, 1))

	if port == 0 {
		panic("ran out of ports!")
	}

	return port
}
<|endoftext|>


<commit_before>package mock_godless

import (
	"testing"

	"github.com/golang/mock/gomock"
	lib "github.com/johnny-morrice/godless"
)

func TestRunQueryJoinSuccess(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mock := NewMockNamespaceTree(ctrl)

	query := &lib.Query{
		OpCode: lib.JOIN,
		TableKey: mainTableKey,
		Join: lib.QueryJoin{
			Rows: []lib.QueryRowJoin{
				lib.QueryRowJoin{
					RowKey: "Row A",
					Entries: map[string]string{
						"Entry A": "Value A",
						"Entry B": "Value B",
					},
				},
				lib.QueryRowJoin{
					RowKey: "Row B",
					Entries: map[string]string{
						"Entry C": "Value C",
					},
				},
				lib.QueryRowJoin{
					RowKey: "Row A",
					Entries: map[string]string{
						"Entry A": "Value D",
						"Entry D": "Value E",
					},
				},
			},
		},
	}

	table := lib.Table{
		Rows: map[string]lib.Row{
			"Row A": lib.Row{
				Entries: map[string][]string {
					"Entry A": []string{"Value A", "Value D"},
					"Entry B": []string{"Value B"},
					"Entry D": []string{"Value E"},
				},
			},
			"Row B": lib.Row{
				Entries: map[string][]string {
					"Entry C": []string{"Value C"},
				},
			},
		},
	}
	mock.EXPECT().JoinTable(mainTableKey, mtchtable(table)).Return(nil)

	joiner := lib.MakeNamespaceTreeJoin(mock)
	query.Visit(joiner)
	resp := joiner.RunQuery()

	if !apiResponseEq(lib.RESPONSE_OK, resp) {
		t.Error("Expected", lib.RESPONSE_OK, "but was", resp)
	}
}

func TestRunQueryJoinInvalid(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mock := NewMockNamespaceTree(ctrl)

	invalidQueries := []*lib.Query{
		// Basically wrong.
		&lib.Query{},
		&lib.Query{OpCode: lib.SELECT},
	}

	for _, q := range invalidQueries {
		joiner := lib.MakeNamespaceTreeJoin(mock)
		q.Visit(joiner)
		resp := joiner.RunQuery()

		if resp.Msg != "error" {
			t.Error("Expected Msg error but received", resp.Msg)
		}

		if resp.Err == nil {
			t.Error("Expected response Err")
		}
	}
}

func mtchtable(t lib.Table) gomock.Matcher {
	return tablematcher{t}
}

type tablematcher struct {
	t lib.Table
}

func (tm tablematcher) String() string {
	return "is matching Table"
}

func (tm tablematcher) Matches(v interface{}) bool {
	other, ok := v.(lib.Table)

	if !ok {
		return false
	}

	return tableEq(tm.t, other)
}

func tableEq(a, b lib.Table) bool {
	if len(a.Rows) != len(b.Rows) {
		return false
	}

	// TODO funky.
	same := true
	a.Foreachrow(func (rkey string, arow lib.Row) {
		brow, present := b.Rows[rkey]

		if !present {
			same = false
		}

		if !rowEq(arow, brow) {
			same = false
		}
	})

	return same
}
<commit_msg>another join mock<commit_after>package mock_godless

import (
	"testing"

	"github.com/pkg/errors"
	"github.com/golang/mock/gomock"
	lib "github.com/johnny-morrice/godless"
)

func TestRunQueryJoinSuccess(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mock := NewMockNamespaceTree(ctrl)

	query := &lib.Query{
		OpCode: lib.JOIN,
		TableKey: mainTableKey,
		Join: lib.QueryJoin{
			Rows: []lib.QueryRowJoin{
				lib.QueryRowJoin{
					RowKey: "Row A",
					Entries: map[string]string{
						"Entry A": "Value A",
						"Entry B": "Value B",
					},
				},
				lib.QueryRowJoin{
					RowKey: "Row B",
					Entries: map[string]string{
						"Entry C": "Value C",
					},
				},
				lib.QueryRowJoin{
					RowKey: "Row A",
					Entries: map[string]string{
						"Entry A": "Value D",
						"Entry D": "Value E",
					},
				},
			},
		},
	}

	table := lib.Table{
		Rows: map[string]lib.Row{
			"Row A": lib.Row{
				Entries: map[string][]string {
					"Entry A": []string{"Value A", "Value D"},
					"Entry B": []string{"Value B"},
					"Entry D": []string{"Value E"},
				},
			},
			"Row B": lib.Row{
				Entries: map[string][]string {
					"Entry C": []string{"Value C"},
				},
			},
		},
	}
	mock.EXPECT().JoinTable(mainTableKey, mtchtable(table)).Return(nil)

	joiner := lib.MakeNamespaceTreeJoin(mock)
	query.Visit(joiner)
	resp := joiner.RunQuery()

	if !apiResponseEq(lib.RESPONSE_OK, resp) {
		t.Error("Expected", lib.RESPONSE_OK, "but was", resp)
	}
}

func TestRunQueryJoinFailure(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mock := NewMockNamespaceTree(ctrl)

	failQuery := &lib.Query{
		OpCode: lib.JOIN,
		TableKey: mainTableKey,
		Join: lib.QueryJoin{
			Rows: []lib.QueryRowJoin{
				lib.QueryRowJoin{
					RowKey: "Row A",
					Entries: map[string]string{
						"Entry A": "Value A",
						"Entry B": "Value B",
					},
				},
			},
		},
	}

	table := lib.Table{
		Rows: map[string]lib.Row{
			"Row A": lib.Row{
				Entries: map[string][]string {
					"Entry A": []string{"Value A"},
					"Entry B": []string{"Value B"},
				},
			},
		},
	}

	mock.EXPECT().JoinTable(mainTableKey, mtchtable(table)).Return(errors.New("Expected error"))

	joiner := lib.MakeNamespaceTreeJoin(mock)
	failQuery.Visit(joiner)
	resp := joiner.RunQuery()

	if resp.Msg != "error" {
		t.Error("Expected Msg error but received", resp.Msg)
	}

	if resp.Err == nil {
		t.Error("Expected response Err")
	}
}

func TestRunQueryJoinInvalid(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mock := NewMockNamespaceTree(ctrl)

	invalidQueries := []*lib.Query{
		// Basically wrong.
		&lib.Query{},
		&lib.Query{OpCode: lib.SELECT},
	}

	for _, q := range invalidQueries {
		joiner := lib.MakeNamespaceTreeJoin(mock)
		q.Visit(joiner)
		resp := joiner.RunQuery()

		if resp.Msg != "error" {
			t.Error("Expected Msg error but received", resp.Msg)
		}

		if resp.Err == nil {
			t.Error("Expected response Err")
		}
	}
}

func mtchtable(t lib.Table) gomock.Matcher {
	return tablematcher{t}
}

type tablematcher struct {
	t lib.Table
}

func (tm tablematcher) String() string {
	return "is matching Table"
}

func (tm tablematcher) Matches(v interface{}) bool {
	other, ok := v.(lib.Table)

	if !ok {
		return false
	}

	return tableEq(tm.t, other)
}

func tableEq(a, b lib.Table) bool {
	if len(a.Rows) != len(b.Rows) {
		return false
	}

	// TODO funky.
	same := true
	a.Foreachrow(func (rkey string, arow lib.Row) {
		brow, present := b.Rows[rkey]

		if !present {
			same = false
		}

		if !rowEq(arow, brow) {
			same = false
		}
	})

	return same
}
<|endoftext|>


<commit_before>package app

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"
	"github.com/PuerkitoBio/goquery"

	"google.golang.org/appengine"
	"google.golang.org/appengine/urlfetch"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/log"
)

type Schedule struct {
	Teacher  string      // 先生のID
	Date     []time.Time // 予約可能日時
	Updated  time.Time
}

const (
	maxDays = 2
	form = "2006-01-02 15:04:05"
)

func init() {
	http.HandleFunc("/check", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {

	ctx := appengine.NewContext(r)
	teacher := os.Getenv("teacher")
	if teacher == "" {
		log.Debugf(ctx, "invalid teacher id: %v", teacher)
		return
	}

	client := urlfetch.Client(ctx)
	site := fmt.Sprintf("http://eikaiwa.dmm.com/teacher/index/%s/", teacher)
	resp, err := client.Get(site)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// yyyy-mm-dd HH:MM:ss
	re := regexp.MustCompile("[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) ([01][0-9]|2[0-3]):[03]0:00")

	available := []time.Time{}

	doc, _ := goquery.NewDocumentFromResponse(resp)
	// get all schedule

	// teacher's name: Second(last) element of document.getElementsByTagName('h1')
	name := doc.Find("h1").Last().Text()
	log.Debugf(ctx, "name : %v", name)

	// teacher's image: document.getElementsByClassName('profile-pic')
	image, _ := doc.Find(".profile-pic").First().Attr("src")
	log.Debugf(ctx, "image : %v", image)

	doc.Find(".oneday").EachWithBreak(func(i int, s *goquery.Selection) bool {
		// 直近のmaxDays日分の予約可能情報を対象とする
		log.Debugf(ctx, "i = %v", i)
		if i >= maxDays {
			return false
		}

		// TODO 受講日情報要らない予感
		date := s.Find(".date").Text() // 受講日
		log.Debugf(ctx, "-----%v-----", date)

		s.Find(".bt-open").Each(func(_ int, s *goquery.Selection) {

			s2, _ := s.Attr("id") // 受講可能時刻
			log.Debugf(ctx, "%v", s2)
			dateString := re.FindString(s2)
			log.Debugf(ctx, "%v", dateString)

			day, _ := time.ParseInLocation(form, dateString, time.FixedZone("Asia/Tokyo", 9*60*60))
			log.Debugf(ctx, "%v", day)

			available = append(available, day)
		})
		return true
	})

	// キーでデータ作ってデータベースに格納してみる
	// とりあえず、対象教師のIDをstringIDに放り込んでみる
	key := datastore.NewKey(ctx, "Schedule", teacher, 0, nil)

	var old Schedule
	if err := datastore.Get(ctx, key, &old); err != nil {
		// Entityが空の場合は見逃す
		if err.Error() != "datastore: no such entity" {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	new := Schedule {
		teacher,
		available,
		time.Now().In(time.FixedZone("Asia/Tokyo", 9*60*60)),
	}

	if _, err := datastore.Put(ctx, key, &new); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	notifications := []string{}
	for _, newVal := range available {
		var notify = true
		for _, oldVal := range old.Date {
			if newVal.Equal(oldVal) {
				notify = false
				break
			}
		}
		if notify {
			notifications = append(notifications, newVal.Format(form))
		}
	}
	log.Debugf(ctx, "notification data: %v, %v", len(notifications), notifications)

	if len(notifications) == 0 {
		return
	}

	token := os.Getenv("slack_token")
	if token != "" {
		values := url.Values{}
		values.Add("token", token)
		values.Add("channel", "#general")
		values.Add("as_user", "false")
		values.Add("username", fmt.Sprintf("%s from DMM Eikaiwa", name))
		values.Add("icon_url", image)
		values.Add("text", fmt.Sprintf(messageFormat, strings.Join(notifications, "\n"), site))

		res, error := client.PostForm("https://slack.com/api/chat.postMessage", values)
		if error != nil {
			log.Debugf(ctx, "senderror %v", error)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		defer res.Body.Close()

		b, err := ioutil.ReadAll(res.Body)
		if err == nil {
			log.Debugf(ctx, "response: %v", string(b))
		}
	}
}

const messageFormat = `
Hi, you can have a lesson below!
%s

Access to <%s>
`
<commit_msg>複数の先生に対応。一旦逐次処理で実装。<commit_after>package app

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"
	"github.com/PuerkitoBio/goquery"

	"golang.org/x/net/context"
	"google.golang.org/appengine"
	"google.golang.org/appengine/urlfetch"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/log"
)

type Schedule struct {
	Teacher  string      // 先生のID
	Date     []time.Time // 予約可能日時
	Updated  time.Time
}

const (
	maxDays = 2
	form = "2006-01-02 15:04:05"
)

func init() {
	http.HandleFunc("/check", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {

	ctx := appengine.NewContext(r)
	teach := os.Getenv("teacher")
	if teach == "" {
		log.Debugf(ctx, "invalid teacher id: %v", teach)
		return
	}

	teachers := strings.Split(teach, ",")
	log.Debugf(ctx, "teachers: %v", teachers)
	for _, teacher := range teachers {
		err := search(ctx, teacher)
		if err != nil {
			log.Warningf(ctx, "err: %v", err)
		}
	}
}

func search(ctx context.Context, teacher string) error {

	client := urlfetch.Client(ctx)
	site := fmt.Sprintf("http://eikaiwa.dmm.com/teacher/index/%s/", teacher)
	resp, err := client.Get(site)
	if err != nil {
		return fmt.Errorf("access error: %s, context: %v", site, err)
	}

	doc, _ := goquery.NewDocumentFromResponse(resp)
	// get all schedule

	// teacher's name: Second(last) element of document.getElementsByTagName('h1')
	name := doc.Find("h1").Last().Text()
	log.Debugf(ctx, "name : %v", name)

	// teacher's image: document.getElementsByClassName('profile-pic')
	image, _ := doc.Find(".profile-pic").First().Attr("src")
	log.Debugf(ctx, "image : %v", image)

	available := []time.Time{}
	// yyyy-mm-dd HH:MM:ss
	re := regexp.MustCompile("[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) ([01][0-9]|2[0-3]):[03]0:00")

	doc.Find(".oneday").EachWithBreak(func(i int, s *goquery.Selection) bool {
		// 直近のmaxDays日分の予約可能情報を対象とする
		log.Debugf(ctx, "i = %v", i)
		if i >= maxDays {
			return false
		}

		// TODO 受講日情報要らない予感
		date := s.Find(".date").Text() // 受講日
		log.Debugf(ctx, "-----%v-----", date)

		s.Find(".bt-open").Each(func(_ int, s *goquery.Selection) {

			s2, _ := s.Attr("id") // 受講可能時刻
			log.Debugf(ctx, "%v", s2)
			dateString := re.FindString(s2)
			log.Debugf(ctx, "%v", dateString)

			day, _ := time.ParseInLocation(form, dateString, time.FixedZone("Asia/Tokyo", 9*60*60))
			log.Debugf(ctx, "%v", day)

			available = append(available, day)
		})
		return true
	})

	key := datastore.NewKey(ctx, "Schedule", teacher, 0, nil)

	var old Schedule
	if err := datastore.Get(ctx, key, &old); err != nil {
		// Entityが空の場合は見逃す
		if err.Error() != "datastore: no such entity" {
			return fmt.Errorf("datastore access error: %s, context: %v", teacher, err)
		}
	}

	new := Schedule {
		teacher,
		available,
		time.Now().In(time.FixedZone("Asia/Tokyo", 9*60*60)),
	}

	if _, err := datastore.Put(ctx, key, &new); err != nil {
		return fmt.Errorf("datastore access error: %s, context: %v", new.Teacher, err)
	}

	notifications := []string{}
	for _, newVal := range available {
		var notify = true
		for _, oldVal := range old.Date {
			if newVal.Equal(oldVal) {
				notify = false
				break
			}
		}
		if notify {
			notifications = append(notifications, newVal.Format(form))
		}
	}
	log.Debugf(ctx, "notification data: %v, %v", len(notifications), notifications)

	if len(notifications) == 0 {
		return nil
	}

	token := os.Getenv("slack_token")
	if token != "" {
		values := url.Values{}
		values.Add("token", token)
		values.Add("channel", "#general")
		values.Add("as_user", "false")
		values.Add("username", fmt.Sprintf("%s from DMM Eikaiwa", name))
		values.Add("icon_url", image)
		values.Add("text", fmt.Sprintf(messageFormat, strings.Join(notifications, "\n"), site))

		res, err := client.PostForm("https://slack.com/api/chat.postMessage", values)
		if err != nil {
			log.Debugf(ctx, "senderror %v", err)
			return fmt.Errorf("noti send error: %s, context: %v", teacher, err)
		}
		defer res.Body.Close()

		b, err := ioutil.ReadAll(res.Body)
		if err == nil {
			log.Debugf(ctx, "response: %v", string(b))
		}
	}
	return nil
}

const messageFormat = `
Hi, you can have a lesson below!
%s

Access to <%s>
`
<|endoftext|>


<commit_before>package inmemory

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"sync"
	"time"

	"github.com/docker/distribution/storagedriver"
	"github.com/docker/distribution/storagedriver/factory"
)

const driverName = "inmemory"

func init() {
	factory.Register(driverName, &inMemoryDriverFactory{})
}

// inMemoryDriverFacotry implements the factory.StorageDriverFactory interface.
type inMemoryDriverFactory struct{}

func (factory *inMemoryDriverFactory) Create(parameters map[string]interface{}) (storagedriver.StorageDriver, error) {
	return New(), nil
}

// Driver is a storagedriver.StorageDriver implementation backed by a local map.
// Intended solely for example and testing purposes.
type Driver struct {
	root  *dir
	mutex sync.RWMutex
}

// New constructs a new Driver.
func New() *Driver {
	return &Driver{root: &dir{
		common: common{
			p:   "/",
			mod: time.Now(),
		},
	}}
}

// Implement the storagedriver.StorageDriver interface.

// GetContent retrieves the content stored at "path" as a []byte.
func (d *Driver) GetContent(path string) ([]byte, error) {
	if !storagedriver.PathRegexp.MatchString(path) {
		return nil, storagedriver.InvalidPathError{Path: path}
	}

	d.mutex.RLock()
	defer d.mutex.RUnlock()

	rc, err := d.ReadStream(path, 0)
	if err != nil {
		return nil, err
	}
	defer rc.Close()

	return ioutil.ReadAll(rc)
}

// PutContent stores the []byte content at a location designated by "path".
func (d *Driver) PutContent(p string, contents []byte) error {
	if !storagedriver.PathRegexp.MatchString(p) {
		return storagedriver.InvalidPathError{Path: p}
	}

	d.mutex.Lock()
	defer d.mutex.Unlock()

	f, err := d.root.mkfile(p)
	if err != nil {
		// TODO(stevvooe): Again, we need to clarify when this is not a
		// directory in StorageDriver API.
		return fmt.Errorf("not a file")
	}

	f.truncate()
	f.WriteAt(contents, 0)

	return nil
}

// ReadStream retrieves an io.ReadCloser for the content stored at "path" with a
// given byte offset.
func (d *Driver) ReadStream(path string, offset int64) (io.ReadCloser, error) {
	if !storagedriver.PathRegexp.MatchString(path) {
		return nil, storagedriver.InvalidPathError{Path: path}
	}

	d.mutex.RLock()
	defer d.mutex.RUnlock()

	if offset < 0 {
		return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
	}

	path = normalize(path)
	found := d.root.find(path)

	if found.path() != path {
		return nil, storagedriver.PathNotFoundError{Path: path}
	}

	if found.isdir() {
		return nil, fmt.Errorf("%q is a directory", path)
	}

	return ioutil.NopCloser(found.(*file).sectionReader(offset)), nil
}

// WriteStream stores the contents of the provided io.ReadCloser at a location
// designated by the given path.
func (d *Driver) WriteStream(path string, offset int64, reader io.Reader) (nn int64, err error) {
	if !storagedriver.PathRegexp.MatchString(path) {
		return 0, storagedriver.InvalidPathError{Path: path}
	}

	d.mutex.Lock()
	defer d.mutex.Unlock()

	if offset < 0 {
		return 0, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
	}

	normalized := normalize(path)

	f, err := d.root.mkfile(normalized)
	if err != nil {
		return 0, fmt.Errorf("not a file")
	}

	// Unlock while we are reading from the source, in case we are reading
	// from the same mfs instance. This can be fixed by a more granular
	// locking model.
	d.mutex.Unlock()
	d.mutex.RLock() // Take the readlock to block other writers.
	var buf bytes.Buffer

	nn, err = buf.ReadFrom(reader)
	if err != nil {
		// TODO(stevvooe): This condition is odd and we may need to clarify:
		// we've read nn bytes from reader but have written nothing to the
		// backend. What is the correct return value? Really, the caller needs
		// to know that the reader has been advanced and reattempting the
		// operation is incorrect.
		d.mutex.RUnlock()
		d.mutex.Lock()
		return nn, err
	}

	d.mutex.RUnlock()
	d.mutex.Lock()
	f.WriteAt(buf.Bytes(), offset)
	return nn, err
}

// Stat returns info about the provided path.
func (d *Driver) Stat(path string) (storagedriver.FileInfo, error) {
	if !storagedriver.PathRegexp.MatchString(path) {
		return nil, storagedriver.InvalidPathError{Path: path}
	}

	d.mutex.RLock()
	defer d.mutex.RUnlock()

	normalized := normalize(path)
	found := d.root.find(path)

	if found.path() != normalized {
		return nil, storagedriver.PathNotFoundError{Path: path}
	}

	fi := storagedriver.FileInfoFields{
		Path:    path,
		IsDir:   found.isdir(),
		ModTime: found.modtime(),
	}

	if !fi.IsDir {
		fi.Size = int64(len(found.(*file).data))
	}

	return storagedriver.FileInfoInternal{FileInfoFields: fi}, nil
}

// List returns a list of the objects that are direct descendants of the given
// path.
func (d *Driver) List(path string) ([]string, error) {
	if !storagedriver.PathRegexp.MatchString(path) && path != "/" {
		return nil, storagedriver.InvalidPathError{Path: path}
	}

	normalized := normalize(path)

	found := d.root.find(normalized)

	if !found.isdir() {
		return nil, fmt.Errorf("not a directory") // TODO(stevvooe): Need error type for this...
	}

	entries, err := found.(*dir).list(normalized)

	if err != nil {
		switch err {
		case errNotExists:
			return nil, storagedriver.PathNotFoundError{Path: path}
		case errIsNotDir:
			return nil, fmt.Errorf("not a directory")
		default:
			return nil, err
		}
	}

	return entries, nil
}

// Move moves an object stored at sourcePath to destPath, removing the original
// object.
func (d *Driver) Move(sourcePath string, destPath string) error {
	if !storagedriver.PathRegexp.MatchString(sourcePath) {
		return storagedriver.InvalidPathError{Path: sourcePath}
	} else if !storagedriver.PathRegexp.MatchString(destPath) {
		return storagedriver.InvalidPathError{Path: destPath}
	}

	d.mutex.Lock()
	defer d.mutex.Unlock()

	normalizedSrc, normalizedDst := normalize(sourcePath), normalize(destPath)

	err := d.root.move(normalizedSrc, normalizedDst)
	switch err {
	case errNotExists:
		return storagedriver.PathNotFoundError{Path: destPath}
	default:
		return err
	}
}

// Delete recursively deletes all objects stored at "path" and its subpaths.
func (d *Driver) Delete(path string) error {
	if !storagedriver.PathRegexp.MatchString(path) {
		return storagedriver.InvalidPathError{Path: path}
	}

	d.mutex.Lock()
	defer d.mutex.Unlock()

	normalized := normalize(path)

	err := d.root.delete(normalized)
	switch err {
	case errNotExists:
		return storagedriver.PathNotFoundError{Path: path}
	default:
		return err
	}
}

// URLFor returns a URL which may be used to retrieve the content stored at the given path.
// May return an UnsupportedMethodErr in certain StorageDriver implementations.
func (d *Driver) URLFor(path string, options map[string]interface{}) (string, error) {
	return "", storagedriver.ErrUnsupportedMethod
}
<commit_msg>[InMemory] Add missing mutex.RLock/RUnlock in List to protect internal map<commit_after>package inmemory

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"sync"
	"time"

	"github.com/docker/distribution/storagedriver"
	"github.com/docker/distribution/storagedriver/factory"
)

const driverName = "inmemory"

func init() {
	factory.Register(driverName, &inMemoryDriverFactory{})
}

// inMemoryDriverFacotry implements the factory.StorageDriverFactory interface.
type inMemoryDriverFactory struct{}

func (factory *inMemoryDriverFactory) Create(parameters map[string]interface{}) (storagedriver.StorageDriver, error) {
	return New(), nil
}

// Driver is a storagedriver.StorageDriver implementation backed by a local map.
// Intended solely for example and testing purposes.
type Driver struct {
	root  *dir
	mutex sync.RWMutex
}

// New constructs a new Driver.
func New() *Driver {
	return &Driver{root: &dir{
		common: common{
			p:   "/",
			mod: time.Now(),
		},
	}}
}

// Implement the storagedriver.StorageDriver interface.

// GetContent retrieves the content stored at "path" as a []byte.
func (d *Driver) GetContent(path string) ([]byte, error) {
	if !storagedriver.PathRegexp.MatchString(path) {
		return nil, storagedriver.InvalidPathError{Path: path}
	}

	d.mutex.RLock()
	defer d.mutex.RUnlock()

	rc, err := d.ReadStream(path, 0)
	if err != nil {
		return nil, err
	}
	defer rc.Close()

	return ioutil.ReadAll(rc)
}

// PutContent stores the []byte content at a location designated by "path".
func (d *Driver) PutContent(p string, contents []byte) error {
	if !storagedriver.PathRegexp.MatchString(p) {
		return storagedriver.InvalidPathError{Path: p}
	}

	d.mutex.Lock()
	defer d.mutex.Unlock()

	f, err := d.root.mkfile(p)
	if err != nil {
		// TODO(stevvooe): Again, we need to clarify when this is not a
		// directory in StorageDriver API.
		return fmt.Errorf("not a file")
	}

	f.truncate()
	f.WriteAt(contents, 0)

	return nil
}

// ReadStream retrieves an io.ReadCloser for the content stored at "path" with a
// given byte offset.
func (d *Driver) ReadStream(path string, offset int64) (io.ReadCloser, error) {
	if !storagedriver.PathRegexp.MatchString(path) {
		return nil, storagedriver.InvalidPathError{Path: path}
	}

	d.mutex.RLock()
	defer d.mutex.RUnlock()

	if offset < 0 {
		return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
	}

	path = normalize(path)
	found := d.root.find(path)

	if found.path() != path {
		return nil, storagedriver.PathNotFoundError{Path: path}
	}

	if found.isdir() {
		return nil, fmt.Errorf("%q is a directory", path)
	}

	return ioutil.NopCloser(found.(*file).sectionReader(offset)), nil
}

// WriteStream stores the contents of the provided io.ReadCloser at a location
// designated by the given path.
func (d *Driver) WriteStream(path string, offset int64, reader io.Reader) (nn int64, err error) {
	if !storagedriver.PathRegexp.MatchString(path) {
		return 0, storagedriver.InvalidPathError{Path: path}
	}

	d.mutex.Lock()
	defer d.mutex.Unlock()

	if offset < 0 {
		return 0, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
	}

	normalized := normalize(path)

	f, err := d.root.mkfile(normalized)
	if err != nil {
		return 0, fmt.Errorf("not a file")
	}

	// Unlock while we are reading from the source, in case we are reading
	// from the same mfs instance. This can be fixed by a more granular
	// locking model.
	d.mutex.Unlock()
	d.mutex.RLock() // Take the readlock to block other writers.
	var buf bytes.Buffer

	nn, err = buf.ReadFrom(reader)
	if err != nil {
		// TODO(stevvooe): This condition is odd and we may need to clarify:
		// we've read nn bytes from reader but have written nothing to the
		// backend. What is the correct return value? Really, the caller needs
		// to know that the reader has been advanced and reattempting the
		// operation is incorrect.
		d.mutex.RUnlock()
		d.mutex.Lock()
		return nn, err
	}

	d.mutex.RUnlock()
	d.mutex.Lock()
	f.WriteAt(buf.Bytes(), offset)
	return nn, err
}

// Stat returns info about the provided path.
func (d *Driver) Stat(path string) (storagedriver.FileInfo, error) {
	if !storagedriver.PathRegexp.MatchString(path) {
		return nil, storagedriver.InvalidPathError{Path: path}
	}

	d.mutex.RLock()
	defer d.mutex.RUnlock()

	normalized := normalize(path)
	found := d.root.find(path)

	if found.path() != normalized {
		return nil, storagedriver.PathNotFoundError{Path: path}
	}

	fi := storagedriver.FileInfoFields{
		Path:    path,
		IsDir:   found.isdir(),
		ModTime: found.modtime(),
	}

	if !fi.IsDir {
		fi.Size = int64(len(found.(*file).data))
	}

	return storagedriver.FileInfoInternal{FileInfoFields: fi}, nil
}

// List returns a list of the objects that are direct descendants of the given
// path.
func (d *Driver) List(path string) ([]string, error) {
	if !storagedriver.PathRegexp.MatchString(path) && path != "/" {
		return nil, storagedriver.InvalidPathError{Path: path}
	}

	d.mutex.RLock()
	defer d.mutex.RUnlock()

	normalized := normalize(path)

	found := d.root.find(normalized)

	if !found.isdir() {
		return nil, fmt.Errorf("not a directory") // TODO(stevvooe): Need error type for this...
	}

	entries, err := found.(*dir).list(normalized)

	if err != nil {
		switch err {
		case errNotExists:
			return nil, storagedriver.PathNotFoundError{Path: path}
		case errIsNotDir:
			return nil, fmt.Errorf("not a directory")
		default:
			return nil, err
		}
	}

	return entries, nil
}

// Move moves an object stored at sourcePath to destPath, removing the original
// object.
func (d *Driver) Move(sourcePath string, destPath string) error {
	if !storagedriver.PathRegexp.MatchString(sourcePath) {
		return storagedriver.InvalidPathError{Path: sourcePath}
	} else if !storagedriver.PathRegexp.MatchString(destPath) {
		return storagedriver.InvalidPathError{Path: destPath}
	}

	d.mutex.Lock()
	defer d.mutex.Unlock()

	normalizedSrc, normalizedDst := normalize(sourcePath), normalize(destPath)

	err := d.root.move(normalizedSrc, normalizedDst)
	switch err {
	case errNotExists:
		return storagedriver.PathNotFoundError{Path: destPath}
	default:
		return err
	}
}

// Delete recursively deletes all objects stored at "path" and its subpaths.
func (d *Driver) Delete(path string) error {
	if !storagedriver.PathRegexp.MatchString(path) {
		return storagedriver.InvalidPathError{Path: path}
	}

	d.mutex.Lock()
	defer d.mutex.Unlock()

	normalized := normalize(path)

	err := d.root.delete(normalized)
	switch err {
	case errNotExists:
		return storagedriver.PathNotFoundError{Path: path}
	default:
		return err
	}
}

// URLFor returns a URL which may be used to retrieve the content stored at the given path.
// May return an UnsupportedMethodErr in certain StorageDriver implementations.
func (d *Driver) URLFor(path string, options map[string]interface{}) (string, error) {
	return "", storagedriver.ErrUnsupportedMethod
}
<|endoftext|>


<commit_before>// Package redigostore offers Redis-based store implementation for throttled using redigo.
package redigostore // import "github.com/throttled/throttled/v2/store/redigostore"

import (
	"strings"
	"time"

	"github.com/gomodule/redigo/redis"
)

const (
	redisCASMissingKey = "key does not exist"
	redisCASScript     = `
local v = redis.call('get', KEYS[1])
if v == false then
  return redis.error_reply("key does not exist")
end
if v ~= ARGV[1] then
  return 0
end
redis.call('setex', KEYS[1], ARGV[3], ARGV[2])
return 1
`
)

// RedigoStore implements a Redis-based store using redigo.
type RedigoStore struct {
	pool   *redis.Pool
	prefix string
	db     int
}

// New creates a new Redis-based store, using the provided pool to get
// its connections. The keys will have the specified keyPrefix, which
// may be an empty string, and the database index specified by db will
// be selected to store the keys. Any updating operations will reset
// the key TTL to the provided value rounded down to the nearest
// second. Depends on Redis 2.6+ for EVAL support.
func New(pool *redis.Pool, keyPrefix string, db int) (*RedigoStore, error) {
	return &RedigoStore{
		pool:   pool,
		prefix: keyPrefix,
		db:     db,
	}, nil
}

// GetWithTime returns the value of the key if it is in the store
// or -1 if it does not exist. It also returns the current time at
// the redis server to microsecond precision.
func (r *RedigoStore) GetWithTime(key string) (int64, time.Time, error) {
	var now time.Time

	key = r.prefix + key

	conn, err := r.getConn()
	if err != nil {
		return 0, now, err
	}
	defer conn.Close()

	conn.Send("TIME")
	conn.Send("GET", key)
	conn.Flush()
	timeReply, err := redis.Values(conn.Receive())
	if err != nil {
		return 0, now, err
	}

	var s, us int64
	if _, err := redis.Scan(timeReply, &s, &us); err != nil {
		return 0, now, err
	}
	now = time.Unix(s, us*int64(time.Microsecond))

	v, err := redis.Int64(conn.Receive())
	if err == redis.ErrNil {
		return -1, now, nil
	} else if err != nil {
		return 0, now, err
	}

	return v, now, nil
}

// SetIfNotExistsWithTTL sets the value of key only if it is not
// already set in the store it returns whether a new value was set.
// If a new value was set, the ttl in the key is also set, though this
// operation is not performed atomically.
func (r *RedigoStore) SetIfNotExistsWithTTL(key string, value int64, ttl time.Duration) (bool, error) {
	key = r.prefix + key

	conn, err := r.getConn()
	if err != nil {
		return false, err
	}
	defer conn.Close()

	v, err := redis.Int64(conn.Do("SETNX", key, value))
	if err != nil {
		return false, err
	}

	updated := v == 1

	ttlSeconds := int(ttl.Seconds())

	// An `EXPIRE 0` will delete the key immediately, so make sure that we set
	// expiry for a minimum of one second out so that our results stay in the
	// store.
	if ttlSeconds < 1 {
		ttlSeconds = 1
	}

	if _, err := conn.Do("EXPIRE", key, ttlSeconds); err != nil {
		return updated, err
	}

	return updated, nil
}

// CompareAndSwapWithTTL atomically compares the value at key to the
// old value. If it matches, it sets it to the new value and returns
// true. Otherwise, it returns false. If the key does not exist in the
// store, it returns false with no error. If the swap succeeds, the
// ttl for the key is updated atomically.
func (r *RedigoStore) CompareAndSwapWithTTL(key string, old, new int64, ttl time.Duration) (bool, error) {
	key = r.prefix + key
	conn, err := r.getConn()
	if err != nil {
		return false, err
	}
	defer conn.Close()

	ttlSeconds := int(ttl.Seconds())

	// An `EXPIRE 0` will delete the key immediately, so make sure that we set
	// expiry for a minimum of one second out so that our results stay in the
	// store.
	if ttlSeconds < 1 {
		ttlSeconds = 1
	}

	swapped, err := redis.Bool(conn.Do("EVAL", redisCASScript, 1, key, old, new, ttlSeconds))
	if err != nil {
		if strings.Contains(err.Error(), redisCASMissingKey) {
			return false, nil
		}

		return false, err
	}

	return swapped, nil
}

// Select the specified database index.
func (r *RedigoStore) getConn() (redis.Conn, error) {
	conn := r.pool.Get()

	// Select the specified database
	if _, err := redis.String(conn.Do("SELECT", r.db)); err != nil {
		conn.Close()
		return nil, err
	}

	return conn, nil
}
<commit_msg>Add support for redisc to redigostore<commit_after>// Package redigostore offers Redis-based store implementation for throttled using redigo.
package redigostore // import "github.com/throttled/throttled/v2/store/redigostore"

import (
	"strings"
	"time"

	"github.com/gomodule/redigo/redis"
)

const (
	redisCASMissingKey = "key does not exist"
	redisCASScript     = `
local v = redis.call('get', KEYS[1])
if v == false then
  return redis.error_reply("key does not exist")
end
if v ~= ARGV[1] then
  return 0
end
redis.call('setex', KEYS[1], ARGV[3], ARGV[2])
return 1
`
)

// RedigoPool is the interface for retrieving a Redis connection from a Redigo
// pool. This same interface is supported by the cluster client in
// https://github.com/mna/redisc.
type RedigoPool interface {
	Get() redis.Conn
}

// RedigoStore implements a Redis-based store using redigo.
type RedigoStore struct {
	pool   RedigoPool
	prefix string
	db     int
}

// New creates a new Redis-based store, using the provided pool to get
// its connections. The keys will have the specified keyPrefix, which
// may be an empty string, and the database index specified by db will
// be selected to store the keys. Any updating operations will reset
// the key TTL to the provided value rounded down to the nearest
// second. Depends on Redis 2.6+ for EVAL support.
func New(pool RedigoPool, keyPrefix string, db int) (*RedigoStore, error) {
	return &RedigoStore{
		pool:   pool,
		prefix: keyPrefix,
		db:     db,
	}, nil
}

// GetWithTime returns the value of the key if it is in the store
// or -1 if it does not exist. It also returns the current time at
// the redis server to microsecond precision.
func (r *RedigoStore) GetWithTime(key string) (int64, time.Time, error) {
	var now time.Time

	key = r.prefix + key

	conn, err := r.getConn()
	if err != nil {
		return 0, now, err
	}
	defer conn.Close()

	conn.Send("TIME")
	conn.Send("GET", key)
	conn.Flush()
	timeReply, err := redis.Values(conn.Receive())
	if err != nil {
		return 0, now, err
	}

	var s, us int64
	if _, err := redis.Scan(timeReply, &s, &us); err != nil {
		return 0, now, err
	}
	now = time.Unix(s, us*int64(time.Microsecond))

	v, err := redis.Int64(conn.Receive())
	if err == redis.ErrNil {
		return -1, now, nil
	} else if err != nil {
		return 0, now, err
	}

	return v, now, nil
}

// SetIfNotExistsWithTTL sets the value of key only if it is not
// already set in the store it returns whether a new value was set.
// If a new value was set, the ttl in the key is also set, though this
// operation is not performed atomically.
func (r *RedigoStore) SetIfNotExistsWithTTL(key string, value int64, ttl time.Duration) (bool, error) {
	key = r.prefix + key

	conn, err := r.getConn()
	if err != nil {
		return false, err
	}
	defer conn.Close()

	v, err := redis.Int64(conn.Do("SETNX", key, value))
	if err != nil {
		return false, err
	}

	updated := v == 1

	ttlSeconds := int(ttl.Seconds())

	// An `EXPIRE 0` will delete the key immediately, so make sure that we set
	// expiry for a minimum of one second out so that our results stay in the
	// store.
	if ttlSeconds < 1 {
		ttlSeconds = 1
	}

	if _, err := conn.Do("EXPIRE", key, ttlSeconds); err != nil {
		return updated, err
	}

	return updated, nil
}

// CompareAndSwapWithTTL atomically compares the value at key to the
// old value. If it matches, it sets it to the new value and returns
// true. Otherwise, it returns false. If the key does not exist in the
// store, it returns false with no error. If the swap succeeds, the
// ttl for the key is updated atomically.
func (r *RedigoStore) CompareAndSwapWithTTL(key string, old, new int64, ttl time.Duration) (bool, error) {
	key = r.prefix + key
	conn, err := r.getConn()
	if err != nil {
		return false, err
	}
	defer conn.Close()

	ttlSeconds := int(ttl.Seconds())

	// An `EXPIRE 0` will delete the key immediately, so make sure that we set
	// expiry for a minimum of one second out so that our results stay in the
	// store.
	if ttlSeconds < 1 {
		ttlSeconds = 1
	}

	swapped, err := redis.Bool(conn.Do("EVAL", redisCASScript, 1, key, old, new, ttlSeconds))
	if err != nil {
		if strings.Contains(err.Error(), redisCASMissingKey) {
			return false, nil
		}

		return false, err
	}

	return swapped, nil
}

// Select the specified database index.
func (r *RedigoStore) getConn() (redis.Conn, error) {
	conn := r.pool.Get()

	// Select the specified database
	if _, err := redis.String(conn.Do("SELECT", r.db)); err != nil {
		conn.Close()
		return nil, err
	}

	return conn, nil
}
<|endoftext|>


<commit_before>package terraform

import (
	"log"
)

// NodeApplyableResource represents a resource that is "applyable":
// it may need to have its record in the state adjusted to match configuration.
//
// Unlike in the plan walk, this resource node does not DynamicExpand. Instead,
// it should be inserted into the same graph as any instances of the nodes
// with dependency edges ensuring that the resource is evaluated before any
// of its instances, which will turn ensure that the whole-resource record
// in the state is suitably prepared to receive any updates to instances.
type NodeApplyableResource struct {
	*NodeAbstractResource
}

var (
	_ GraphNodeResource             = (*NodeApplyableResource)(nil)
	_ GraphNodeEvalable             = (*NodeApplyableResource)(nil)
	_ GraphNodeProviderConsumer     = (*NodeApplyableResource)(nil)
	_ GraphNodeAttachResourceConfig = (*NodeApplyableResource)(nil)
)

func (n *NodeApplyableResource) Name() string {
	return n.NodeAbstractResource.Name() + " (prepare state)"
}

// GraphNodeEvalable
func (n *NodeApplyableResource) EvalTree() EvalNode {
	addr := n.ResourceAddr()
	config := n.Config
	providerAddr := n.ResolvedProvider

	if config == nil {
		// Nothing to do, then.
		log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", addr)
		return &EvalNoop{}
	}

	return &EvalWriteResourceState{
		Addr:         addr.Resource,
		Config:       config,
		ProviderAddr: providerAddr,
	}
}
<commit_msg>core: NodeApplyableResource only depends on count and for_each<commit_after>package terraform

import (
	"log"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/dag"
	"github.com/hashicorp/terraform/lang"
)

// NodeApplyableResource represents a resource that is "applyable":
// it may need to have its record in the state adjusted to match configuration.
//
// Unlike in the plan walk, this resource node does not DynamicExpand. Instead,
// it should be inserted into the same graph as any instances of the nodes
// with dependency edges ensuring that the resource is evaluated before any
// of its instances, which will turn ensure that the whole-resource record
// in the state is suitably prepared to receive any updates to instances.
type NodeApplyableResource struct {
	*NodeAbstractResource
}

var (
	_ GraphNodeResource             = (*NodeApplyableResource)(nil)
	_ GraphNodeEvalable             = (*NodeApplyableResource)(nil)
	_ GraphNodeProviderConsumer     = (*NodeApplyableResource)(nil)
	_ GraphNodeAttachResourceConfig = (*NodeApplyableResource)(nil)
	_ GraphNodeReferencer           = (*NodeApplyableResource)(nil)
)

func (n *NodeApplyableResource) Name() string {
	return n.NodeAbstractResource.Name() + " (prepare state)"
}

func (n *NodeApplyableResource) References() []*addrs.Reference {
	if n.Config == nil {
		log.Printf("[WARN] NodeApplyableResource %q: no configuration, so can't determine References", dag.VertexName(n))
		return nil
	}

	var result []*addrs.Reference

	// Since this node type only updates resource-level metadata, we only
	// need to worry about the parts of the configuration that affect
	// our "each mode": the count and for_each meta-arguments.
	refs, _ := lang.ReferencesInExpr(n.Config.Count)
	result = append(result, refs...)
	refs, _ = lang.ReferencesInExpr(n.Config.ForEach)
	result = append(result, refs...)

	return result
}

// GraphNodeEvalable
func (n *NodeApplyableResource) EvalTree() EvalNode {
	addr := n.ResourceAddr()
	config := n.Config
	providerAddr := n.ResolvedProvider

	if config == nil {
		// Nothing to do, then.
		log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", addr)
		return &EvalNoop{}
	}

	return &EvalWriteResourceState{
		Addr:         addr.Resource,
		Config:       config,
		ProviderAddr: providerAddr,
	}
}
<|endoftext|>


<commit_before>package builds

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	exutil "github.com/openshift/origin/test/extended/util"
	"github.com/openshift/origin/test/extended/util/jenkins"
)

func debugAnyJenkinsFailure(br *exutil.BuildResult, name string, oc *exutil.CLI, dumpMaster bool) {
	if !br.BuildSuccess {
		fmt.Fprintf(g.GinkgoWriter, "\n\n START debugAnyJenkinsFailure\n\n")
		j := jenkins.NewRef(oc)
		jobLog, err := j.GetLastJobConsoleLogs(name)
		if err == nil {
			fmt.Fprintf(g.GinkgoWriter, "\n %s job log:\n%s", name, jobLog)
		} else {
			fmt.Fprintf(g.GinkgoWriter, "\n error getting %s job log: %#v", name, err)
		}
		if dumpMaster {
			exutil.DumpDeploymentLogs("jenkins", oc)
		}
		fmt.Fprintf(g.GinkgoWriter, "\n\n END debugAnyJenkinsFailure\n\n")
	}
}

var _ = g.Describe("[builds][Slow] openshift pipeline build", func() {
	defer g.GinkgoRecover()
	var (
		jenkinsTemplatePath    = exutil.FixturePath("..", "..", "examples", "jenkins", "jenkins-ephemeral-template.json")
		mavenSlavePipelinePath = exutil.FixturePath("..", "..", "examples", "jenkins", "pipeline", "maven-pipeline.yaml")
		//orchestrationPipelinePath = exutil.FixturePath("..", "..", "examples", "jenkins", "pipeline", "mapsapp-pipeline.yaml")
		blueGreenPipelinePath = exutil.FixturePath("..", "..", "examples", "jenkins", "pipeline", "bluegreen-pipeline.yaml")

		oc                       = exutil.NewCLI("jenkins-pipeline", exutil.KubeConfigPath())
		ticker                   *time.Ticker
		j                        *jenkins.JenkinsRef
		dcLogFollow              *exec.Cmd
		dcLogStdOut, dcLogStdErr *bytes.Buffer
		setupJenkins             = func() {
			// Deploy Jenkins
			g.By(fmt.Sprintf("calling oc new-app -f %q", jenkinsTemplatePath))
			err := oc.Run("new-app").Args("-f", jenkinsTemplatePath).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("waiting for jenkins deployment")
			err = exutil.WaitForADeploymentToComplete(oc.KubeClient().Core().ReplicationControllers(oc.Namespace()), "jenkins", oc)
			o.Expect(err).NotTo(o.HaveOccurred())

			j = jenkins.NewRef(oc)

			g.By("wait for jenkins to come up")
			_, err = j.WaitForContent("", 200, 10*time.Minute, "")

			if err != nil {
				exutil.DumpDeploymentLogs("jenkins", oc)
			}

			o.Expect(err).NotTo(o.HaveOccurred())

			// Start capturing logs from this deployment config.
			// This command will terminate if the Jenkins instance crashes. This
			// ensures that even if the Jenkins DC restarts, we should capture
			// logs from the crash.
			dcLogFollow, dcLogStdOut, dcLogStdErr, err = oc.Run("logs").Args("-f", "dc/jenkins").Background()
			o.Expect(err).NotTo(o.HaveOccurred())

		}
	)

	g.AfterEach(func() {
		if os.Getenv(jenkins.DisableJenkinsGCSTats) == "" {
			g.By("stop jenkins gc tracking")
			ticker.Stop()
		}
	})

	g.BeforeEach(func() {
		setupJenkins()

		if os.Getenv(jenkins.DisableJenkinsGCSTats) == "" {
			g.By("start jenkins gc tracking")
			ticker = jenkins.StartJenkinsGCTracking(oc, oc.Namespace())
		}

		g.By("waiting for builder service account")
		err := exutil.WaitForBuilderAccount(oc.KubeClient().Core().ServiceAccounts(oc.Namespace()))
		o.Expect(err).NotTo(o.HaveOccurred())
	})

	g.Context("Pipeline with maven slave", func() {
		g.It("should build and complete successfully", func() {
			// instantiate the template
			g.By(fmt.Sprintf("calling oc new-app -f %q", mavenSlavePipelinePath))
			err := oc.Run("new-app").Args("-f", mavenSlavePipelinePath).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			// start the build
			g.By("starting the pipeline build and waiting for it to complete")
			br, _ := exutil.StartBuildAndWait(oc, "openshift-jee-sample")
			debugAnyJenkinsFailure(br, oc.Namespace()+"-openshift-jee-sample", oc, true)
			br.AssertSuccess()

			// wait for the service to be running
			g.By("expecting the openshift-jee-sample service to be deployed and running")
			_, err = exutil.GetEndpointAddress(oc, "openshift-jee-sample")
			o.Expect(err).NotTo(o.HaveOccurred())
		})
	})

	/*g.Context("Orchestration pipeline", func() {
		g.It("should build and complete successfully", func() {
			setupJenkins()

			// instantiate the template
			g.By(fmt.Sprintf("calling oc new-app -f %q", orchestrationPipelinePath))
			err := oc.Run("new-app").Args("-f", orchestrationPipelinePath).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			// start the build
			g.By("starting the pipeline build and waiting for it to complete")
			br, _ := exutil.StartBuildAndWait(oc, "mapsapp-pipeline")
			debugAnyJenkinsFailure(br, oc.Namespace()+"-mapsapp-pipeline", oc, true)
			debugAnyJenkinsFailure(br, oc.Namespace()+"-mlbparks-pipeline", oc, false)
			debugAnyJenkinsFailure(br, oc.Namespace()+"-nationalparks-pipeline", oc, false)
			debugAnyJenkinsFailure(br, oc.Namespace()+"-parksmap-pipeline", oc, false)
			br.AssertSuccess()

			// wait for the service to be running
			g.By("expecting the parksmap-web service to be deployed and running")
			_, err = exutil.GetEndpointAddress(oc, "parksmap")
			o.Expect(err).NotTo(o.HaveOccurred())
		})
	})*/

	g.Context("Blue-green pipeline", func() {
		g.It("Blue-green pipeline should build and complete successfully", func() {
			// instantiate the template
			g.By(fmt.Sprintf("calling oc new-app -f %q", blueGreenPipelinePath))
			err := oc.Run("new-app").Args("-f", blueGreenPipelinePath).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			wg := &sync.WaitGroup{}
			wg.Add(1)

			// start the build
			go func() {
				defer g.GinkgoRecover()
				g.By("starting the bluegreen pipeline build and waiting for it to complete")
				br, _ := exutil.StartBuildAndWait(oc, "bluegreen-pipeline")
				debugAnyJenkinsFailure(br, oc.Namespace()+"-bluegreen-pipeline", oc, true)
				br.AssertSuccess()

				g.By("verifying that the main route has been switched to green")
				value, err := oc.Run("get").Args("route", "nodejs-mongodb-example", "-o", "jsonpath={ .spec.to.name }").Output()
				o.Expect(err).NotTo(o.HaveOccurred())
				activeRoute := strings.TrimSpace(value)
				g.By("verifying that the active route is 'nodejs-mongodb-example-green'")
				o.Expect(activeRoute).To(o.Equal("nodejs-mongodb-example-green"))
				wg.Done()
			}()

			// wait for the green service to be available
			g.By("waiting for the nodejs-mongodb-example-green service to be available")
			_, err = exutil.GetEndpointAddress(oc, "nodejs-mongodb-example-green")
			o.Expect(err).NotTo(o.HaveOccurred())

			// approve the Jenkins pipeline
			g.By("Waiting for the approval prompt")
			jobName := oc.Namespace() + "-bluegreen-pipeline"
			_, err = j.WaitForContent("Approve?", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("Approving the current build")
			_, _, err = j.Post(nil, fmt.Sprintf("job/%s/lastBuild/input/Approval/proceedEmpty", jobName), "")
			o.Expect(err).NotTo(o.HaveOccurred())

			// wait for first build completion and verification
			g.By("Waiting for the build to complete successfully")
			wg.Wait()

			wg = &sync.WaitGroup{}
			wg.Add(1)

			// start the build again
			go func() {
				defer g.GinkgoRecover()
				g.By("starting the bluegreen pipeline build and waiting for it to complete")
				br, _ := exutil.StartBuildAndWait(oc, "bluegreen-pipeline")
				debugAnyJenkinsFailure(br, oc.Namespace()+"-bluegreen-pipeline", oc, true)
				br.AssertSuccess()

				g.By("verifying that the main route has been switched to blue")
				value, err := oc.Run("get").Args("route", "nodejs-mongodb-example", "-o", "jsonpath={ .spec.to.name }").Output()
				o.Expect(err).NotTo(o.HaveOccurred())
				activeRoute := strings.TrimSpace(value)
				g.By("verifying that the active route is 'nodejs-mongodb-example-blue'")
				o.Expect(activeRoute).To(o.Equal("nodejs-mongodb-example-blue"))
				wg.Done()
			}()

			// wait for the blue service to be available
			g.By("waiting for the nodejs-mongodb-example-blue service to be available")
			_, err = exutil.GetEndpointAddress(oc, "nodejs-mongodb-example-blue")
			o.Expect(err).NotTo(o.HaveOccurred())

			// approve the Jenkins pipeline
			g.By("Waiting for the approval prompt")
			_, err = j.WaitForContent("Approve?", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("Approving the current build")
			_, _, err = j.Post(nil, fmt.Sprintf("job/%s/lastBuild/input/Approval/proceedEmpty", jobName), "")
			o.Expect(err).NotTo(o.HaveOccurred())

			wg.Wait()
		})
	})

})
<commit_msg>fix jenkins blue-green ext test so it does not block forever on build/deploy error<commit_after>package builds

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	exutil "github.com/openshift/origin/test/extended/util"
	"github.com/openshift/origin/test/extended/util/jenkins"
	sutil "github.com/openshift/source-to-image/pkg/util"
)

func debugAnyJenkinsFailure(br *exutil.BuildResult, name string, oc *exutil.CLI, dumpMaster bool) {
	if !br.BuildSuccess {
		fmt.Fprintf(g.GinkgoWriter, "\n\n START debugAnyJenkinsFailure\n\n")
		j := jenkins.NewRef(oc)
		jobLog, err := j.GetLastJobConsoleLogs(name)
		if err == nil {
			fmt.Fprintf(g.GinkgoWriter, "\n %s job log:\n%s", name, jobLog)
		} else {
			fmt.Fprintf(g.GinkgoWriter, "\n error getting %s job log: %#v", name, err)
		}
		if dumpMaster {
			exutil.DumpDeploymentLogs("jenkins", oc)
		}
		fmt.Fprintf(g.GinkgoWriter, "\n\n END debugAnyJenkinsFailure\n\n")
	}
}

var _ = g.Describe("[builds][Slow] openshift pipeline build", func() {
	defer g.GinkgoRecover()
	var (
		jenkinsTemplatePath    = exutil.FixturePath("..", "..", "examples", "jenkins", "jenkins-ephemeral-template.json")
		mavenSlavePipelinePath = exutil.FixturePath("..", "..", "examples", "jenkins", "pipeline", "maven-pipeline.yaml")
		//orchestrationPipelinePath = exutil.FixturePath("..", "..", "examples", "jenkins", "pipeline", "mapsapp-pipeline.yaml")
		blueGreenPipelinePath = exutil.FixturePath("..", "..", "examples", "jenkins", "pipeline", "bluegreen-pipeline.yaml")

		oc                       = exutil.NewCLI("jenkins-pipeline", exutil.KubeConfigPath())
		ticker                   *time.Ticker
		j                        *jenkins.JenkinsRef
		dcLogFollow              *exec.Cmd
		dcLogStdOut, dcLogStdErr *bytes.Buffer
		setupJenkins             = func() {
			// Deploy Jenkins
			g.By(fmt.Sprintf("calling oc new-app -f %q", jenkinsTemplatePath))
			err := oc.Run("new-app").Args("-f", jenkinsTemplatePath).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("waiting for jenkins deployment")
			err = exutil.WaitForADeploymentToComplete(oc.KubeClient().Core().ReplicationControllers(oc.Namespace()), "jenkins", oc)
			o.Expect(err).NotTo(o.HaveOccurred())

			j = jenkins.NewRef(oc)

			g.By("wait for jenkins to come up")
			_, err = j.WaitForContent("", 200, 10*time.Minute, "")

			if err != nil {
				exutil.DumpDeploymentLogs("jenkins", oc)
			}

			o.Expect(err).NotTo(o.HaveOccurred())

			// Start capturing logs from this deployment config.
			// This command will terminate if the Jenkins instance crashes. This
			// ensures that even if the Jenkins DC restarts, we should capture
			// logs from the crash.
			dcLogFollow, dcLogStdOut, dcLogStdErr, err = oc.Run("logs").Args("-f", "dc/jenkins").Background()
			o.Expect(err).NotTo(o.HaveOccurred())

		}
	)

	g.AfterEach(func() {
		if os.Getenv(jenkins.DisableJenkinsGCSTats) == "" {
			g.By("stop jenkins gc tracking")
			ticker.Stop()
		}
	})

	g.BeforeEach(func() {
		setupJenkins()

		if os.Getenv(jenkins.DisableJenkinsGCSTats) == "" {
			g.By("start jenkins gc tracking")
			ticker = jenkins.StartJenkinsGCTracking(oc, oc.Namespace())
		}

		g.By("waiting for builder service account")
		err := exutil.WaitForBuilderAccount(oc.KubeClient().Core().ServiceAccounts(oc.Namespace()))
		o.Expect(err).NotTo(o.HaveOccurred())
	})

	g.Context("Pipeline with maven slave", func() {
		g.It("should build and complete successfully", func() {
			// instantiate the template
			g.By(fmt.Sprintf("calling oc new-app -f %q", mavenSlavePipelinePath))
			err := oc.Run("new-app").Args("-f", mavenSlavePipelinePath).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			// start the build
			g.By("starting the pipeline build and waiting for it to complete")
			br, _ := exutil.StartBuildAndWait(oc, "openshift-jee-sample")
			debugAnyJenkinsFailure(br, oc.Namespace()+"-openshift-jee-sample", oc, true)
			br.AssertSuccess()

			// wait for the service to be running
			g.By("expecting the openshift-jee-sample service to be deployed and running")
			_, err = exutil.GetEndpointAddress(oc, "openshift-jee-sample")
			o.Expect(err).NotTo(o.HaveOccurred())
		})
	})

	/*g.Context("Orchestration pipeline", func() {
		g.It("should build and complete successfully", func() {
			setupJenkins()

			// instantiate the template
			g.By(fmt.Sprintf("calling oc new-app -f %q", orchestrationPipelinePath))
			err := oc.Run("new-app").Args("-f", orchestrationPipelinePath).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			// start the build
			g.By("starting the pipeline build and waiting for it to complete")
			br, _ := exutil.StartBuildAndWait(oc, "mapsapp-pipeline")
			debugAnyJenkinsFailure(br, oc.Namespace()+"-mapsapp-pipeline", oc, true)
			debugAnyJenkinsFailure(br, oc.Namespace()+"-mlbparks-pipeline", oc, false)
			debugAnyJenkinsFailure(br, oc.Namespace()+"-nationalparks-pipeline", oc, false)
			debugAnyJenkinsFailure(br, oc.Namespace()+"-parksmap-pipeline", oc, false)
			br.AssertSuccess()

			// wait for the service to be running
			g.By("expecting the parksmap-web service to be deployed and running")
			_, err = exutil.GetEndpointAddress(oc, "parksmap")
			o.Expect(err).NotTo(o.HaveOccurred())
		})
	})*/

	g.Context("Blue-green pipeline", func() {
		g.It("Blue-green pipeline should build and complete successfully", func() {
			// instantiate the template
			g.By(fmt.Sprintf("calling oc new-app -f %q", blueGreenPipelinePath))
			err := oc.Run("new-app").Args("-f", blueGreenPipelinePath).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			wg := &sync.WaitGroup{}
			wg.Add(1)

			// start the build
			go func() {
				defer g.GinkgoRecover()
				defer wg.Done()
				g.By("starting the bluegreen pipeline build and waiting for it to complete")
				br, _ := exutil.StartBuildAndWait(oc, "bluegreen-pipeline")
				debugAnyJenkinsFailure(br, oc.Namespace()+"-bluegreen-pipeline", oc, true)
				br.AssertSuccess()

				g.By("verifying that the main route has been switched to green")
				value, err := oc.Run("get").Args("route", "nodejs-mongodb-example", "-o", "jsonpath={ .spec.to.name }").Output()
				o.Expect(err).NotTo(o.HaveOccurred())
				activeRoute := strings.TrimSpace(value)
				g.By("verifying that the active route is 'nodejs-mongodb-example-green'")
				o.Expect(activeRoute).To(o.Equal("nodejs-mongodb-example-green"))
			}()

			// wait for the green service to be available
			g.By("waiting for the nodejs-mongodb-example-green service to be available")
			_, err = exutil.GetEndpointAddress(oc, "nodejs-mongodb-example-green")
			o.Expect(err).NotTo(o.HaveOccurred())

			// approve the Jenkins pipeline
			g.By("Waiting for the approval prompt")
			jobName := oc.Namespace() + "-bluegreen-pipeline"
			_, err = j.WaitForContent("Approve?", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("Approving the current build")
			_, _, err = j.Post(nil, fmt.Sprintf("job/%s/lastBuild/input/Approval/proceedEmpty", jobName), "")
			o.Expect(err).NotTo(o.HaveOccurred())

			// wait for first build completion and verification
			g.By("Waiting for the first build to complete successfully")
			err = sutil.TimeoutAfter(time.Minute*10, "first blue-green build timed out before WaitGroup quit blocking", func(timeoutTimer *time.Timer) error {
				g.By("start wg.Wait() for build completion and verification")
				wg.Wait()
				g.By("build completion and verification good to go")
				return nil
			})
			o.Expect(err).NotTo(o.HaveOccurred())

			wg = &sync.WaitGroup{}
			wg.Add(1)

			// start the build again
			go func() {
				defer g.GinkgoRecover()
				defer wg.Done()
				g.By("starting the bluegreen pipeline build and waiting for it to complete")
				br, _ := exutil.StartBuildAndWait(oc, "bluegreen-pipeline")
				debugAnyJenkinsFailure(br, oc.Namespace()+"-bluegreen-pipeline", oc, true)
				br.AssertSuccess()

				g.By("verifying that the main route has been switched to blue")
				value, err := oc.Run("get").Args("route", "nodejs-mongodb-example", "-o", "jsonpath={ .spec.to.name }").Output()
				o.Expect(err).NotTo(o.HaveOccurred())
				activeRoute := strings.TrimSpace(value)
				g.By("verifying that the active route is 'nodejs-mongodb-example-blue'")
				o.Expect(activeRoute).To(o.Equal("nodejs-mongodb-example-blue"))
			}()

			// wait for the blue service to be available
			g.By("waiting for the nodejs-mongodb-example-blue service to be available")
			_, err = exutil.GetEndpointAddress(oc, "nodejs-mongodb-example-blue")
			o.Expect(err).NotTo(o.HaveOccurred())

			// approve the Jenkins pipeline
			g.By("Waiting for the approval prompt")
			_, err = j.WaitForContent("Approve?", 200, 10*time.Minute, "job/%s/lastBuild/consoleText", jobName)
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("Approving the current build")
			_, _, err = j.Post(nil, fmt.Sprintf("job/%s/lastBuild/input/Approval/proceedEmpty", jobName), "")
			o.Expect(err).NotTo(o.HaveOccurred())

			// wait for second build completion and verification
			g.By("Waiting for the second build to complete successfully")
			err = sutil.TimeoutAfter(time.Minute*10, "second blue-green build timed out before WaitGroup quit blocking", func(timeoutTimer *time.Timer) error {
				g.By("start wg.Wait() for build completion and verification")
				wg.Wait()
				g.By("build completion and verification good to go")
				return nil
			})
			o.Expect(err).NotTo(o.HaveOccurred())
		})
	})

})
<|endoftext|>


<commit_before>package integration

import (
	"bytes"
	"math/rand"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libcompose/docker"
	"github.com/samalba/dockerclient"

	. "gopkg.in/check.v1"
)

var random = rand.New(rand.NewSource(time.Now().Unix()))

func RandStr(n int) string {
	letters := []rune("abcdefghijklmnopqrstuvwxyz")
	b := make([]rune, n)
	for i := range b {
		b[i] = letters[random.Intn(len(letters))]
	}
	return string(b)
}

type RunSuite struct {
	command  string
	projects []string
}

var _ = Suite(&RunSuite{
	command: "../bundles/libcompose-cli_linux-amd64",
})

func (s *RunSuite) CreateProjectFromText(c *C, input string) string {
	return s.ProjectFromText(c, "create", input)
}

func (s *RunSuite) RandomProject() string {
	return "test-project-" + RandStr(7)
}

func (s *RunSuite) ProjectFromText(c *C, command, input string) string {
	projectName := s.RandomProject()
	return s.FromText(c, projectName, command, input)
}

func (s *RunSuite) FromText(c *C, projectName, command string, argsAndInput ...string) string {
	args := []string{"--verbose", "-p", projectName, "-f", "-", command}
	args = append(args, argsAndInput[0:len(argsAndInput)-1]...)

	input := argsAndInput[len(argsAndInput)-1]

	if command == "up" {
		args = append(args, "-d")
	} else if command == "down" {
		args = append(args, "--timeout", "0")
	} else if command == "restart" {
		args = append(args, "--timeout", "0")
	} else if command == "stop" {
		args = append(args, "--timeout", "0")
	}

	logrus.Infof("Running %s %v", command, args)

	cmd := exec.Command(s.command, args...)
	cmd.Stdin = bytes.NewBufferString(strings.Replace(input, "\t", "  ", -1))
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stdout

	err := cmd.Run()
	if err != nil {
		logrus.Errorf("Failed to run %s %v: %v\n with input:\n%s", s.command, err, args, input)
	}

	c.Assert(err, IsNil)

	return projectName
}

func GetClient(c *C) dockerclient.Client {
	client, err := docker.CreateClient(docker.ClientOpts{})

	c.Assert(err, IsNil)

	return client
}

func (s *RunSuite) GetContainerByName(c *C, name string) *dockerclient.ContainerInfo {
	client := GetClient(c)
	container, err := docker.GetContainerByName(client, name)

	c.Assert(err, IsNil)

	if container == nil {
		return nil
	}

	info, err := client.InspectContainer(container.Id)

	c.Assert(err, IsNil)

	return info
}

func (s *RunSuite) GetContainersByProject(c *C, project string) []dockerclient.Container {
	client := GetClient(c)
	containers, err := docker.GetContainersByFilter(client, docker.PROJECT.Eq(project))

	c.Assert(err, IsNil)

	return containers
}
<commit_msg>Clean containers after each tests.<commit_after>package integration

import (
	"bytes"
	"math/rand"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libcompose/docker"
	"github.com/samalba/dockerclient"

	. "gopkg.in/check.v1"
)

var random = rand.New(rand.NewSource(time.Now().Unix()))

func RandStr(n int) string {
	letters := []rune("abcdefghijklmnopqrstuvwxyz")
	b := make([]rune, n)
	for i := range b {
		b[i] = letters[random.Intn(len(letters))]
	}
	return string(b)
}

type RunSuite struct {
	command  string
	projects []string
}

func (r *RunSuite) TearDownTest(c *C) {
	// Delete all containers
	client := GetClient(c)
	containers, err := client.ListContainers(true, false, "")
	c.Assert(err, IsNil)
	for _, container := range containers {
		err := client.RemoveContainer(container.Id, true, true)
		c.Assert(err, IsNil)
	}
}

var _ = Suite(&RunSuite{
	command: "../bundles/libcompose-cli_linux-amd64",
})

func (s *RunSuite) CreateProjectFromText(c *C, input string) string {
	return s.ProjectFromText(c, "create", input)
}

func (s *RunSuite) RandomProject() string {
	return "test-project-" + RandStr(7)
}

func (s *RunSuite) ProjectFromText(c *C, command, input string) string {
	projectName := s.RandomProject()
	return s.FromText(c, projectName, command, input)
}

func (s *RunSuite) FromText(c *C, projectName, command string, argsAndInput ...string) string {
	args := []string{"--verbose", "-p", projectName, "-f", "-", command}
	args = append(args, argsAndInput[0:len(argsAndInput)-1]...)

	input := argsAndInput[len(argsAndInput)-1]

	if command == "up" {
		args = append(args, "-d")
	} else if command == "down" {
		args = append(args, "--timeout", "0")
	} else if command == "restart" {
		args = append(args, "--timeout", "0")
	} else if command == "stop" {
		args = append(args, "--timeout", "0")
	}

	logrus.Infof("Running %s %v", command, args)

	cmd := exec.Command(s.command, args...)
	cmd.Stdin = bytes.NewBufferString(strings.Replace(input, "\t", "  ", -1))
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stdout

	err := cmd.Run()
	if err != nil {
		logrus.Errorf("Failed to run %s %v: %v\n with input:\n%s", s.command, err, args, input)
	}

	c.Assert(err, IsNil)

	return projectName
}

func GetClient(c *C) dockerclient.Client {
	client, err := docker.CreateClient(docker.ClientOpts{})

	c.Assert(err, IsNil)

	return client
}

func (s *RunSuite) GetContainerByName(c *C, name string) *dockerclient.ContainerInfo {
	client := GetClient(c)
	container, err := docker.GetContainerByName(client, name)

	c.Assert(err, IsNil)

	if container == nil {
		return nil
	}

	info, err := client.InspectContainer(container.Id)

	c.Assert(err, IsNil)

	return info
}

func (s *RunSuite) GetContainersByProject(c *C, project string) []dockerclient.Container {
	client := GetClient(c)
	containers, err := docker.GetContainersByFilter(client, docker.PROJECT.Eq(project))

	c.Assert(err, IsNil)

	return containers
}
<|endoftext|>


<commit_before>package spotify

import (
	"errors"
	"github.com/stretchr/testify/assert"
	"net/http"
	"testing"
)

func TestClient_NextPage(t *testing.T) {
	testTable := []struct {
		Name         string
		Input        *basePage
		ExpectedPath string
		Err          error
	}{
		{
			"success",
			&basePage{
				Next:  "/v1/albums/0sNOF9WDwhWunNAHPD3Baj/tracks",
				Total: 600,
			},
			"/v1/albums/0sNOF9WDwhWunNAHPD3Baj/tracks",
			nil,
		},
		{
			"no more pages",
			&basePage{
				Next: "",
			},
			"",
			ErrNoMorePages,
		},
		{
			"nil pointer error",
			nil,
			"",
			errors.New("spotify: p must be a non-nil pointer to a page"),
		},
	}

	for _, tt := range testTable {
		t.Run(tt.Name, func(t *testing.T) {
			wasCalled := false
			client, server := testClientString(200, `{"total": 100}`, func(request *http.Request) {
				wasCalled = true
				assert.Equal(t, tt.ExpectedPath, request.URL.RequestURI())
			})
			if tt.Input != nil && tt.Input.Next != "" {
				tt.Input.Next = server.URL + tt.Input.Next // add fake server url so we intercept the message
			}

			err := client.NextPage(tt.Input)
			assert.Equal(t, tt.ExpectedPath != "", wasCalled)
			if tt.Err == nil {
				assert.NoError(t, err)
				assert.Equal(t, 100, tt.Input.Total) // value should be from original 600
			} else {
				assert.EqualError(t, err, tt.Err.Error())
			}
		})
	}
}

func TestClient_PreviousPage(t *testing.T) {
	testTable := []struct {
		Name         string
		Input        *basePage
		ExpectedPath string
		Err          error
	}{
		{
			"success",
			&basePage{
				Previous: "/v1/albums/0sNOF9WDwhWunNAHPD3Baj/tracks",
				Total:    600,
			},
			"/v1/albums/0sNOF9WDwhWunNAHPD3Baj/tracks",
			nil,
		},
		{
			"no more pages",
			&basePage{
				Previous: "",
			},
			"",
			ErrNoMorePages,
		},
		{
			"nil pointer error",
			nil,
			"",
			errors.New("spotify: p must be a non-nil pointer to a page"),
		},
	}

	for _, tt := range testTable {
		t.Run(tt.Name, func(t *testing.T) {
			wasCalled := false
			client, server := testClientString(200, `{"total": 100}`, func(request *http.Request) {
				wasCalled = true
				assert.Equal(t, tt.ExpectedPath, request.URL.RequestURI())
			})
			if tt.Input != nil && tt.Input.Previous != "" {
				tt.Input.Previous = server.URL + tt.Input.Previous // add fake server url so we intercept the message
			}

			err := client.PreviousPage(tt.Input)
			assert.Equal(t, tt.ExpectedPath != "", wasCalled)
			if tt.Err == nil {
				assert.NoError(t, err)
				assert.Equal(t, 100, tt.Input.Total) // value should be from original 600
			} else {
				assert.EqualError(t, err, tt.Err.Error())
			}
		})
	}
}
<commit_msg>Fix tests from broken merge<commit_after>package spotify

import (
	"context"
	"errors"
	"github.com/stretchr/testify/assert"
	"net/http"
	"testing"
)

func TestClient_NextPage(t *testing.T) {
	testTable := []struct {
		Name         string
		Input        *basePage
		ExpectedPath string
		Err          error
	}{
		{
			"success",
			&basePage{
				Next:  "/v1/albums/0sNOF9WDwhWunNAHPD3Baj/tracks",
				Total: 600,
			},
			"/v1/albums/0sNOF9WDwhWunNAHPD3Baj/tracks",
			nil,
		},
		{
			"no more pages",
			&basePage{
				Next: "",
			},
			"",
			ErrNoMorePages,
		},
		{
			"nil pointer error",
			nil,
			"",
			errors.New("spotify: p must be a non-nil pointer to a page"),
		},
	}

	for _, tt := range testTable {
		t.Run(tt.Name, func(t *testing.T) {
			wasCalled := false
			client, server := testClientString(200, `{"total": 100}`, func(request *http.Request) {
				wasCalled = true
				assert.Equal(t, tt.ExpectedPath, request.URL.RequestURI())
			})
			if tt.Input != nil && tt.Input.Next != "" {
				tt.Input.Next = server.URL + tt.Input.Next // add fake server url so we intercept the message
			}

			err := client.NextPage(context.Background(), tt.Input)
			assert.Equal(t, tt.ExpectedPath != "", wasCalled)
			if tt.Err == nil {
				assert.NoError(t, err)
				assert.Equal(t, 100, tt.Input.Total) // value should be from original 600
			} else {
				assert.EqualError(t, err, tt.Err.Error())
			}
		})
	}
}

func TestClient_PreviousPage(t *testing.T) {
	testTable := []struct {
		Name         string
		Input        *basePage
		ExpectedPath string
		Err          error
	}{
		{
			"success",
			&basePage{
				Previous: "/v1/albums/0sNOF9WDwhWunNAHPD3Baj/tracks",
				Total:    600,
			},
			"/v1/albums/0sNOF9WDwhWunNAHPD3Baj/tracks",
			nil,
		},
		{
			"no more pages",
			&basePage{
				Previous: "",
			},
			"",
			ErrNoMorePages,
		},
		{
			"nil pointer error",
			nil,
			"",
			errors.New("spotify: p must be a non-nil pointer to a page"),
		},
	}

	for _, tt := range testTable {
		t.Run(tt.Name, func(t *testing.T) {
			wasCalled := false
			client, server := testClientString(200, `{"total": 100}`, func(request *http.Request) {
				wasCalled = true
				assert.Equal(t, tt.ExpectedPath, request.URL.RequestURI())
			})
			if tt.Input != nil && tt.Input.Previous != "" {
				tt.Input.Previous = server.URL + tt.Input.Previous // add fake server url so we intercept the message
			}

			err := client.PreviousPage(context.Background(), tt.Input)
			assert.Equal(t, tt.ExpectedPath != "", wasCalled)
			if tt.Err == nil {
				assert.NoError(t, err)
				assert.Equal(t, 100, tt.Input.Total) // value should be from original 600
			} else {
				assert.EqualError(t, err, tt.Err.Error())
			}
		})
	}
}
<|endoftext|>