<commit_before>package controllers

import (
	"log"
	"net"
	"net/http"

	"github.com/gophergala/goffee/Godeps/_workspace/src/github.com/oschwald/geoip2-golang" // IP returnss the remote IP address and other info
	"github.com/gophergala/goffee/web/render"
)

func IP(w http.ResponseWriter, req *http.Request) {
	db, err := geoip2.Open("geoip/GeoLite2-Country.mmdb")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	host, _, err := net.SplitHostPort(req.RemoteAddr)
	if err != nil {
		render.JSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}

	// If you are using strings that may be invalid, check that ip is not nil
	ip := net.ParseIP(host)

	record, err := db.Country(ip)

	var responseJSON map[string]string

	if err != nil || record.Country.IsoCode == "" {
		responseJSON = map[string]string{
			"IP": ip.String(),
		}
	} else {
		responseJSON = map[string]string{
			"IP":      ip.String(),
			"Country": record.Country.IsoCode,
		}
	}

	render.JSON(w, http.StatusOK, responseJSON)
}
<commit_msg>Fix IP reflector<commit_after>package controllers

import (
	"log"
	"net"
	"net/http"

	"github.com/gophergala/goffee/Godeps/_workspace/src/github.com/oschwald/geoip2-golang" // IP returnss the remote IP address and other info
	"github.com/gophergala/goffee/web/render"
)

func IP(w http.ResponseWriter, req *http.Request) {
	db, err := geoip2.Open("geoip/GeoLite2-Country.mmdb")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	host, _, err := net.SplitHostPort(req.RemoteAddr)
	if err != nil {
		host = req.RemoteAddr
	}

	// If you are using strings that may be invalid, check that ip is not nil
	ip := net.ParseIP(host)

	record, err := db.Country(ip)

	var responseJSON map[string]string

	if err != nil || record.Country.IsoCode == "" {
		responseJSON = map[string]string{
			"IP": ip.String(),
		}
	} else {
		responseJSON = map[string]string{
			"IP":      ip.String(),
			"Country": record.Country.IsoCode,
		}
	}

	render.JSON(w, http.StatusOK, responseJSON)
}
<|endoftext|>


<commit_before>package sl

import (
	"command/loader"
	"errors"
	"strings"
)

// SLCommand implements the images various interfaces, such as Fetcher,
// Deleter, Modifier, etc..
type SLCommand struct {
	*SLImages
}

// NewCommand returns a new instance of SLCommand
func NewCommand(args []string) (*SLCommand, []string, error) {
	var conf struct {
		// just so we can use the Env and TOML loader more efficiently with out
		// any complex hacks
		SL SLConfig
	}

	if err := loader.Load(&conf, args); err != nil {
		return nil, nil, err
	}

	slImages, err := New(&conf.SL)
	if err != nil {
		return nil, nil, err
	}

	remainingArgs := loader.ExcludeArgs(&conf, args)

	return &SLCommand{
		SLImages: slImages,
	}, remainingArgs, nil
}

// List implements the command.Lister interface
func (cmd *SLCommand) List(args []string) error {
	l := newListFlags()
	if err := l.flagSet.Parse(args); err != nil {
		return nil // we don't return error, the usage will be printed instead
	}

	if len(l.imageIds) == 1 {
		image, err := cmd.ImageByID(l.imageIds[0])
		if err != nil {
			return err
		}
		return (Images{image}).Print(l.output)
	}

	if len(l.imageIds) != 0 {
		images, err := cmd.ImagesByIDs(l.imageIds...)
		if err != nil {
			return err
		}
		return images.Print(l.output)
	}

	images, err := cmd.Images()
	if err != nil {
		return err
	}

	if l.all {
		return images.Print(l.output)
	}

	var filtered Images
	// Filter out system images and not taggable ones.
	for _, img := range images {
		isSystem := strings.HasSuffix(img.Name, "-SWAP") || strings.HasSuffix(img.Name, "-METADATA")
		if isSystem || img.NotTaggable {
			continue
		}
		filtered = append(filtered, img)
	}
	return filtered.Print(l.output)
}

// Modify manages the tags of the given images. It can create, override or
// delete tags associated with the given Template ids.
func (cmd *SLCommand) Modify(args []string) error {
	l := newModifyFlags()
	if err := l.flagSet.Parse(args); err != nil {
		return nil // we don't return error, the usage will be printed instead
	}

	if len(l.imageIds) == 0 {
		return errors.New("no value for -ids flag")
	}

	createTags := newTags(l.createTags)
	deleteTags := newTags(l.deleteTags)
	if len(createTags) != 0 && len(deleteTags) != 0 {
		patchFn := func(orig Tags) {
			for k, v := range createTags {
				orig[k] = v
			}
			for k := range deleteTags {
				delete(orig, k)
			}
		}
		return cmd.patchTags(patchFn, l.force, l.imageIds...)
	} else if len(createTags) != 0 {
		return cmd.createTags(createTags, l.force, l.imageIds...)
	} else if len(deleteTags) != 0 {
		return cmd.deleteTags(deleteTags, l.force, l.imageIds...)
	}
	return errors.New("neither -create-tags nor -delete-tags flag was specified")
}

// Delete deletes Block Device Templates by the given ids.
func (cmd *SLCommand) Delete(args []string) error {
	l := newModifyFlags()
	if err := l.flagSet.Parse(args); err != nil {
		return nil // we don't return error, the usage will be printed instead
	}

	if len(l.imageIds) == 0 {
		return errors.New("no value for -ids flag")
	}

	return cmd.DeleteImages(l.imageIds...)
}

// Help prints the help message for the given command
func (a *SLCommand) Help(command string) string {
	var help string

	global := `
  -username        "..."       Sofleyer Username (env: IMAGES_SL_USERNAME)
  -api-key         "..."       Softlayer API Key (env: IMAGES_SL_API_KEY)
`
	switch command {
	case "modify":
		help = newModifyFlags().helpMsg
	case "list":
		help = newListFlags().helpMsg
	case "delete":
		help = newDeleteFlags().helpMsg
	default:
		return "no help found for command " + command
	}

	help += global
	return help
}
<commit_msg>provider/sl: print only parent templates<commit_after>package sl

import (
	"command/loader"
	"errors"
	"strings"
)

// SLCommand implements the images various interfaces, such as Fetcher,
// Deleter, Modifier, etc..
type SLCommand struct {
	*SLImages
}

// NewCommand returns a new instance of SLCommand
func NewCommand(args []string) (*SLCommand, []string, error) {
	var conf struct {
		// just so we can use the Env and TOML loader more efficiently with out
		// any complex hacks
		SL SLConfig
	}

	if err := loader.Load(&conf, args); err != nil {
		return nil, nil, err
	}

	slImages, err := New(&conf.SL)
	if err != nil {
		return nil, nil, err
	}

	remainingArgs := loader.ExcludeArgs(&conf, args)

	return &SLCommand{
		SLImages: slImages,
	}, remainingArgs, nil
}

// List implements the command.Lister interface
func (cmd *SLCommand) List(args []string) error {
	l := newListFlags()
	if err := l.flagSet.Parse(args); err != nil {
		return nil // we don't return error, the usage will be printed instead
	}

	if len(l.imageIds) == 1 {
		image, err := cmd.ImageByID(l.imageIds[0])
		if err != nil {
			return err
		}
		return (Images{image}).Print(l.output)
	}

	if len(l.imageIds) != 0 {
		images, err := cmd.ImagesByIDs(l.imageIds...)
		if err != nil {
			return err
		}
		return images.Print(l.output)
	}

	images, err := cmd.Images()
	if err != nil {
		return err
	}

	if l.all {
		return images.Print(l.output)
	}

	var filtered Images
	// Filter out system images and not taggable ones.
	for _, img := range images {
		isSystem := strings.HasSuffix(img.Name, "-SWAP") || strings.HasSuffix(img.Name, "-METADATA") || img.GlobalID == ""
		if isSystem || img.NotTaggable {
			continue
		}
		filtered = append(filtered, img)
	}
	return filtered.Print(l.output)
}

// Modify manages the tags of the given images. It can create, override or
// delete tags associated with the given Template ids.
func (cmd *SLCommand) Modify(args []string) error {
	l := newModifyFlags()
	if err := l.flagSet.Parse(args); err != nil {
		return nil // we don't return error, the usage will be printed instead
	}

	if len(l.imageIds) == 0 {
		return errors.New("no value for -ids flag")
	}

	createTags := newTags(l.createTags)
	deleteTags := newTags(l.deleteTags)
	if len(createTags) != 0 && len(deleteTags) != 0 {
		patchFn := func(orig Tags) {
			for k, v := range createTags {
				orig[k] = v
			}
			for k := range deleteTags {
				delete(orig, k)
			}
		}
		return cmd.patchTags(patchFn, l.force, l.imageIds...)
	} else if len(createTags) != 0 {
		return cmd.createTags(createTags, l.force, l.imageIds...)
	} else if len(deleteTags) != 0 {
		return cmd.deleteTags(deleteTags, l.force, l.imageIds...)
	}
	return errors.New("neither -create-tags nor -delete-tags flag was specified")
}

// Delete deletes Block Device Templates by the given ids.
func (cmd *SLCommand) Delete(args []string) error {
	l := newModifyFlags()
	if err := l.flagSet.Parse(args); err != nil {
		return nil // we don't return error, the usage will be printed instead
	}

	if len(l.imageIds) == 0 {
		return errors.New("no value for -ids flag")
	}

	return cmd.DeleteImages(l.imageIds...)
}

// Help prints the help message for the given command
func (a *SLCommand) Help(command string) string {
	var help string

	global := `
  -username        "..."       Sofleyer Username (env: IMAGES_SL_USERNAME)
  -api-key         "..."       Softlayer API Key (env: IMAGES_SL_API_KEY)
`
	switch command {
	case "modify":
		help = newModifyFlags().helpMsg
	case "list":
		help = newListFlags().helpMsg
	case "delete":
		help = newDeleteFlags().helpMsg
	default:
		return "no help found for command " + command
	}

	help += global
	return help
}
<|endoftext|>


<commit_before>package cephvolumedriver

import (
	"errors"
	"io"
	"path"
	"sync"

	"bytes"
	"fmt"
	"os/exec"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/volume"
	"github.com/opencontainers/runc/libcontainer/utils"
)

const (
	CephImageSizeMB   = 1024 * 1024 // 1TB
	LuksDevMapperPath = "/dev/mapper/"
	cryptoLuksFsType  = "crypto_LUKS"
)

func New() *Root {
	return &Root{
		volumes: make(map[string]*Volume),
	}
}

type Root struct {
	m       sync.Mutex
	volumes map[string]*Volume
}

func (r *Root) Name() string {
	return "ceph"
}

func (r *Root) Create(name string, _ map[string]string) (volume.Volume, error) {
	r.m.Lock()
	defer r.m.Unlock()

	v, exists := r.volumes[name]
	if !exists {
		v = &Volume{
			driverName:           r.Name(),
			name:                 name,
			mappedDevicePath:     "", // Will be set by Mount()
			mappedLuksDevicePath: "", // Will be set by Mount()
		}
		r.volumes[name] = v
	}

	return v, nil
}

// Get looks up the volume for the given name and returns it if found
func (r *Root) Get(name string) (volume.Volume, error) {
	r.m.Lock()
	v, exists := r.volumes[name]
	r.m.Unlock()
	if !exists {
		return nil, fmt.Errorf("volume not found")

	}
	return v, nil
}

// List lists all the volumes
func (r *Root) List() ([]volume.Volume, error) {
	var ls []volume.Volume
	r.m.Lock()
	for _, v := range r.volumes {
		ls = append(ls, v)
	}
	r.m.Unlock()
	return ls, nil
}

func (r *Root) Scope() string {
	return volume.LocalScope
}

func mapCephVolume(name string) (string, error) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd := exec.Command("rbd", "map", name)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	var mappedDevicePath string
	if err := cmd.Run(); err == nil {
		mappedDevicePath = strings.TrimRight(stdout.String(), "\n")
		logrus.Infof("Succeeded in mapping Ceph volume '%s' to %s", name, mappedDevicePath)
		return mappedDevicePath, nil
	} else {
		msg := fmt.Sprintf("Failed to map Ceph volume '%s': %s - %s", name, err, strings.TrimRight(stderr.String(), "\n"))
		logrus.Errorf(msg)
		return "", errors.New(msg)
	}
}

func (r *Root) Remove(v volume.Volume) error {
	r.m.Lock()
	defer r.m.Unlock()
	delete(r.volumes, v.Name())
	return nil
}

func unmapCephVolume(name, mappedDevicePath string) error {
	cmd := exec.Command("rbd", "unmap", mappedDevicePath)
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err == nil {
		logrus.Infof("Succeeded in unmapping Ceph volume '%s' from %s", name, mappedDevicePath)
	} else {
		logrus.Errorf("Failed to unmap Ceph volume '%s' from %s: %s - %s", name, mappedDevicePath, err, strings.TrimRight(stderr.String(), "\n"))
	}
	return err
}

type Volume struct {
	m         sync.Mutex
	usedCount int
	// unique name of the volume
	name string
	// driverName is the name of the driver that created the volume.
	driverName string
	// the path to the device to which the Ceph volume has been mapped
	mappedDevicePath string
	// the path to the LUKS folder to which the Ceph device has been mapped
	mappedLuksDevicePath string
}

func (v *Volume) Name() string {
	return v.name
}

func (v *Volume) DriverName() string {
	return v.driverName
}

func (v *Volume) Path() string {
	return ""
}

func (v *Volume) Mount(id string) (mappedDevicePath string, returnedError error) {
	v.m.Lock()
	defer v.m.Unlock()
	logrus.Debugf("Mount: %s", mappedDevicePath)
	// Note that if Mount() returns an error, Docker will still call Unmount(), so there is no need to call release() if anything fails
	if err := v.use(); err != nil {
		return "", err
	}

	// TODO: Might want to map with --options rw/ro here, but then we need to sneak in the RW flag somehow
	var stdout bytes.Buffer
	var stderr bytes.Buffer

	cmd := exec.Command("rbd", "create", v.Name(), "--size", strconv.Itoa(CephImageSizeMB))
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err == nil {
		logrus.Infof("Created Ceph volume '%s'", v.Name())
		v.mappedDevicePath, err = mapCephVolume(v.Name())
		if err != nil {
			return "", err
		}
	} else if strings.Contains(stderr.String(), fmt.Sprintf("rbd image %s already exists", v.Name())) {
		logrus.Infof("Found existing Ceph volume %s", v.Name())
		v.mappedDevicePath, err = mapCephVolume(v.Name())
		if err != nil {
			return "", err
		}
	} else {
		msg := fmt.Sprintf("Failed to create Ceph volume '%s' - %s", v.Name(), err)
		logrus.Errorf(msg)
		return "", errors.New(msg)
	}

	// Check that the volume already has a filesystem
	fsType, err := utils.DeviceHasFilesystem(v.mappedDevicePath)
	if err != nil {
		return "", err
	}

	deviceToMount := v.mappedDevicePath

	if fsType == cryptoLuksFsType {
		cmd = exec.Command("cryptsetup", "luksOpen", "--key-file=-", deviceToMount, v.Name())
		stdin, err := cmd.StdinPipe()
		if err != nil {
			logrus.Errorf("Failed to luksOpen Ceph volume '%s' (device %s) - %s", v.Name(), deviceToMount, err)
			return "", err
		}
		defer stdin.Close()
		if err := cmd.Start(); err != nil {
			logrus.Errorf("Failed to luksOpen Ceph volume '%s' (device %s) - %s", v.Name(), deviceToMount, err)
			return "", err
		}

		key, err := getLuksKey(v.Name())
		if err != nil {
			logrus.Errorf("Failed to luksOpen Ceph volume '%s' (device %s) - %s", v.Name(), deviceToMount, err)
			return "", err
		}

		io.WriteString(stdin, key)
		stdin.Close()
		if err := cmd.Wait(); err != nil {
			logrus.Errorf("Failed to luksOpen Ceph volume '%s' (device %s) - %s", v.Name(), deviceToMount, err)
			return "", err
		}

		v.mappedLuksDevicePath = path.Join(LuksDevMapperPath, v.Name())
		deviceToMount = v.mappedLuksDevicePath

		fsType, err = utils.DeviceHasFilesystem(deviceToMount)
		logrus.Errorf("Checked filesystem in %s: %s", deviceToMount, fsType)
		if err != nil {
			return "", err
		}
	}

	if fsType == "" {
		cmd = exec.Command("mkfs.ext4", "-m0", "-E", "nodiscard,lazy_itable_init=0,lazy_journal_init=0,packed_meta_blocks=1", deviceToMount)
		logrus.Infof("Creating ext4 filesystem in newly created Ceph volume '%s' (device %s)", v.Name(), deviceToMount)
		if err := cmd.Run(); err != nil {
			logrus.Errorf("Failed to create ext4 filesystem in newly created Ceph volume '%s' (device %s) - %s", v.Name(), deviceToMount, err)
			return "", err
		}
	}

	fsckCmd, err := exec.Command("fsck", "-a", deviceToMount).Output()
	if err != nil {
		logrus.Errorf("Failed to check filesystem in %s - %s", v.Name(), err)
		return "", err
	}
	logrus.Infof("Checked filesystem in %s: %s", v.Name(), fsckCmd)

	// The return value from this method will be passed to the container
	return deviceToMount, nil
}

func (v *Volume) Unmount(id string) error {
	v.m.Lock()
	defer v.m.Unlock()

	if err := v.release(); err != nil {
		return err
	}

	fsType, err := utils.DeviceHasFilesystem(v.mappedDevicePath)
	if err != nil {
		return err
	}

	if fsType == cryptoLuksFsType {
		cmd := exec.Command("cryptsetup", "luksClose", v.Name())
		if err := cmd.Run(); err != nil {
			logrus.Errorf("Failed to luksClose Ceph volume '%s' (device %s) - %s", v.Name(), v.mappedDevicePath, err)
			return err
		}
	}
	//if v.usedCount == 0 { // Even if the volume is attempted to be used multiple times, only the first use will actually succeed in mapping it
	unmapCephVolume(v.name, v.mappedDevicePath)
	//}

	return nil
}

func (v *Volume) Status() map[string]interface{} {
	return nil
}

func (v *Volume) use() error {
	// Note that the call to use() is assumed to be contained in a v.m.Lock()/Unlock() (the mutex isn't reentrant, so we can't lock it again here)
	v.usedCount++ // If Mount() fails, Unmount() (and therefore release()) will be called, so we need to increment usedCount even if the volume is already in use
	if v.usedCount > 1 {
		msg := fmt.Sprintf("The Ceph volume '%s' is already being used by a running container in this Docker daemon", v.Name())
		logrus.Errorf(msg)
		return errors.New(msg)
	}
	return nil
}

func (v *Volume) release() error {
	// Note that the call to release() is assumed to be contained in a v.m.Lock()/Unlock() (the mutex isn't reentrant, so we can't lock it again here)
	if v.usedCount == 0 { // Shouldn't happen as long as Docker calls Mount()/Unmount() the way we think, but we've misunderstood the call sequence before
		msg := fmt.Sprintf("Bug: The Ceph volume '%s' is being released more times than it has been used", v.Name())
		logrus.Errorf(msg)
		return errors.New(msg)
	}
	v.usedCount--
	return nil
}

func getLuksKey(name string) (string, error) {
	return name, nil
}
<commit_msg>Remove ref counting from ceph volume driver (#19)<commit_after>package cephvolumedriver

import (
	"errors"
	"io"
	"path"
	"sync"

	"bytes"
	"fmt"
	"os/exec"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/volume"
	"github.com/opencontainers/runc/libcontainer/utils"
)

const (
	CephImageSizeMB   = 1024 * 1024 // 1TB
	LuksDevMapperPath = "/dev/mapper/"
	cryptoLuksFsType  = "crypto_LUKS"
)

func New() *Root {
	return &Root{
		volumes: make(map[string]*Volume),
	}
}

type Root struct {
	m       sync.Mutex
	volumes map[string]*Volume
}

func (r *Root) Name() string {
	return "ceph"
}

func (r *Root) Create(name string, _ map[string]string) (volume.Volume, error) {
	r.m.Lock()
	defer r.m.Unlock()

	v, exists := r.volumes[name]
	if !exists {
		v = &Volume{
			driverName:           r.Name(),
			name:                 name,
			mappedDevicePath:     "", // Will be set by Mount()
			mappedLuksDevicePath: "", // Will be set by Mount()
		}
		r.volumes[name] = v
	}

	return v, nil
}

// Get looks up the volume for the given name and returns it if found
func (r *Root) Get(name string) (volume.Volume, error) {
	r.m.Lock()
	v, exists := r.volumes[name]
	r.m.Unlock()
	if !exists {
		return nil, fmt.Errorf("volume not found")

	}
	return v, nil
}

// List lists all the volumes
func (r *Root) List() ([]volume.Volume, error) {
	var ls []volume.Volume
	r.m.Lock()
	for _, v := range r.volumes {
		ls = append(ls, v)
	}
	r.m.Unlock()
	return ls, nil
}

func (r *Root) Scope() string {
	return volume.LocalScope
}

func mapCephVolume(name string) (string, error) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd := exec.Command("rbd", "map", name)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	var mappedDevicePath string
	if err := cmd.Run(); err == nil {
		mappedDevicePath = strings.TrimRight(stdout.String(), "\n")
		logrus.Infof("Succeeded in mapping Ceph volume '%s' to %s", name, mappedDevicePath)
		return mappedDevicePath, nil
	} else {
		msg := fmt.Sprintf("Failed to map Ceph volume '%s': %s - %s", name, err, strings.TrimRight(stderr.String(), "\n"))
		logrus.Errorf(msg)
		return "", errors.New(msg)
	}
}

func (r *Root) Remove(v volume.Volume) error {
	r.m.Lock()
	defer r.m.Unlock()
	delete(r.volumes, v.Name())
	return nil
}

func unmapCephVolume(name, mappedDevicePath string) error {
	cmd := exec.Command("rbd", "unmap", mappedDevicePath)
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err == nil {
		logrus.Infof("Succeeded in unmapping Ceph volume '%s' from %s", name, mappedDevicePath)
	} else {
		logrus.Errorf("Failed to unmap Ceph volume '%s' from %s: %s - %s", name, mappedDevicePath, err, strings.TrimRight(stderr.String(), "\n"))
	}
	return err
}

type Volume struct {
	m         sync.Mutex
	// unique name of the volume
	name string
	// driverName is the name of the driver that created the volume.
	driverName string
	// the path to the device to which the Ceph volume has been mapped
	mappedDevicePath string
	// the path to the LUKS folder to which the Ceph device has been mapped
	mappedLuksDevicePath string
}

func (v *Volume) Name() string {
	return v.name
}

func (v *Volume) DriverName() string {
	return v.driverName
}

func (v *Volume) Path() string {
	return ""
}

func (v *Volume) Mount(id string) (mappedDevicePath string, returnedError error) {
	v.m.Lock()
	defer v.m.Unlock()
	logrus.Debugf("Mount: %s", mappedDevicePath)
	// The RBD wrapper is the gatekeeper so no need to keep track of the usage

	// TODO: Might want to map with --options rw/ro here, but then we need to sneak in the RW flag somehow
	var stdout bytes.Buffer
	var stderr bytes.Buffer

	cmd := exec.Command("rbd", "create", v.Name(), "--size", strconv.Itoa(CephImageSizeMB))
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err == nil {
		logrus.Infof("Created Ceph volume '%s'", v.Name())
		v.mappedDevicePath, err = mapCephVolume(v.Name())
		if err != nil {
			return "", err
		}
	} else if strings.Contains(stderr.String(), fmt.Sprintf("rbd image %s already exists", v.Name())) {
		logrus.Infof("Found existing Ceph volume %s", v.Name())
		v.mappedDevicePath, err = mapCephVolume(v.Name())
		if err != nil {
			return "", err
		}
	} else {
		msg := fmt.Sprintf("Failed to create Ceph volume '%s' - %s", v.Name(), err)
		logrus.Errorf(msg)
		return "", errors.New(msg)
	}

	// Check that the volume already has a filesystem
	fsType, err := utils.DeviceHasFilesystem(v.mappedDevicePath)
	if err != nil {
		return "", err
	}

	deviceToMount := v.mappedDevicePath

	if fsType == cryptoLuksFsType {
		cmd = exec.Command("cryptsetup", "luksOpen", "--key-file=-", deviceToMount, v.Name())
		stdin, err := cmd.StdinPipe()
		if err != nil {
			logrus.Errorf("Failed to luksOpen Ceph volume '%s' (device %s) - %s", v.Name(), deviceToMount, err)
			return "", err
		}
		defer stdin.Close()
		if err := cmd.Start(); err != nil {
			logrus.Errorf("Failed to luksOpen Ceph volume '%s' (device %s) - %s", v.Name(), deviceToMount, err)
			return "", err
		}

		key, err := getLuksKey(v.Name())
		if err != nil {
			logrus.Errorf("Failed to luksOpen Ceph volume '%s' (device %s) - %s", v.Name(), deviceToMount, err)
			return "", err
		}

		io.WriteString(stdin, key)
		stdin.Close()
		if err := cmd.Wait(); err != nil {
			logrus.Errorf("Failed to luksOpen Ceph volume '%s' (device %s) - %s", v.Name(), deviceToMount, err)
			return "", err
		}

		v.mappedLuksDevicePath = path.Join(LuksDevMapperPath, v.Name())
		deviceToMount = v.mappedLuksDevicePath

		fsType, err = utils.DeviceHasFilesystem(deviceToMount)
		logrus.Errorf("Checked filesystem in %s: %s", deviceToMount, fsType)
		if err != nil {
			return "", err
		}
	}

	if fsType == "" {
		cmd = exec.Command("mkfs.ext4", "-m0", "-E", "nodiscard,lazy_itable_init=0,lazy_journal_init=0,packed_meta_blocks=1", deviceToMount)
		logrus.Infof("Creating ext4 filesystem in newly created Ceph volume '%s' (device %s)", v.Name(), deviceToMount)
		if err := cmd.Run(); err != nil {
			logrus.Errorf("Failed to create ext4 filesystem in newly created Ceph volume '%s' (device %s) - %s", v.Name(), deviceToMount, err)
			return "", err
		}
	}

	fsckCmd, err := exec.Command("fsck", "-a", deviceToMount).Output()
	if err != nil {
		logrus.Errorf("Failed to check filesystem in %s - %s", v.Name(), err)
		return "", err
	}
	logrus.Infof("Checked filesystem in %s: %s", v.Name(), fsckCmd)

	// The return value from this method will be passed to the container
	return deviceToMount, nil
}

func (v *Volume) Unmount(id string) error {
	v.m.Lock()
	defer v.m.Unlock()
	fsType, err := utils.DeviceHasFilesystem(v.mappedDevicePath)
	if err != nil {
		return err
	}

	if fsType == cryptoLuksFsType {
		cmd := exec.Command("cryptsetup", "luksClose", v.Name())
		if err := cmd.Run(); err != nil {
			logrus.Errorf("Failed to luksClose Ceph volume '%s' (device %s) - %s", v.Name(), v.mappedDevicePath, err)
			return err
		}
	}
	unmapCephVolume(v.name, v.mappedDevicePath)
	return nil
}

func (v *Volume) Status() map[string]interface{} {
	return nil
}

func getLuksKey(name string) (string, error) {
	return name, nil
}
<|endoftext|>


<commit_before>package mcstore

import (
	"time"

	"fmt"

	r "github.com/dancannon/gorethink"
	"github.com/emicklei/go-restful"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/schema"
	"github.com/materials-commons/mcstore/pkg/ws/rest"
	"github.com/materials-commons/mcstore/server/mcstore/uploads"
	"github.com/willf/bitset"
)

var _ = fmt.Println

// An uploadResource handles all upload requests.
type uploadResource struct {
	log *app.Logger
}

// UploadEntry is a client side representation of an upload.
type UploadEntry struct {
	RequestID   string    `json:"request_id"`
	FileName    string    `json:"filename"`
	DirectoryID string    `json:"directory_id"`
	ProjectID   string    `json:"project_id"`
	Size        int64     `json:"size"`
	Host        string    `json:"host"`
	Checksum    string    `json:"checksum"`
	Birthtime   time.Time `json:"birthtime"`
}

// newUploadResource creates a new upload resource
func newUploadResource() rest.Service {
	return &uploadResource{
		log: app.NewLog("resource", "upload"),
	}
}

// WebService creates an instance of the upload web service.
func (r *uploadResource) WebService() *restful.WebService {
	ws := new(restful.WebService)

	ws.Path("/upload").Produces(restful.MIME_JSON).Consumes(restful.MIME_JSON)

	ws.Route(ws.POST("").To(rest.RouteHandler(r.createUploadRequest)).
		Doc("Creates a new upload request").
		Reads(CreateUploadRequest{}).
		Writes(CreateUploadResponse{}))

	ws.Route(ws.POST("/chunk").To(rest.RouteHandler(r.uploadFileChunk)).
		Consumes("multipart/form-data").
		Writes(UploadChunkResponse{}).
		Doc("Upload a file chunk"))

	ws.Route(ws.DELETE("{id}").To(rest.RouteHandler1(r.deleteUploadRequest)).
		Doc("Deletes an existing upload request").
		Param(ws.PathParameter("id", "upload request to delete").DataType("string")))

	ws.Route(ws.GET("{project}").To(rest.RouteHandler(r.listProjectUploadRequests)).
		Param(ws.PathParameter("project", "project id").DataType("string")).
		Doc("Lists upload requests for project").
		Writes([]UploadEntry{}))

	return ws
}

// CreateRequest describes the JSON request a client will send
// to create a new upload request.
type CreateUploadRequest struct {
	ProjectID     string `json:"project_id"`
	DirectoryID   string `json:"directory_id"`
	DirectoryPath string `json:"directory_path"`
	FileName      string `json:"filename"`
	FileSize      int64  `json:"filesize"`
	ChunkSize     int32  `json:"chunk_size"`
	FileMTime     string `json:"filemtime"`
	Checksum      string `json: "checksum"`
}

// uploadCreateResponse is the format of JSON sent back containing
// the upload request ID.
type CreateUploadResponse struct {
	RequestID     string `json:"request_id"`
	StartingBlock uint   `json:"starting_block"`
}

type uploadRequester struct {
	idService  uploads.IDService
	dirService DirService
}

func newUploadRequester(session *r.Session) *uploadRequester {
	return &uploadRequester{
		idService:  uploads.NewIDServiceUsingSession(session),
		dirService: newDirServiceUsingSession(session),
	}
}

// createUploadRequest services requests to create a new upload id. It validates
// the given request, and ensures that the returned upload id is unique. Upload
// requests are persisted until deleted or a successful upload occurs.
func (ur *uploadResource) createUploadRequest(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	session := request.Attribute("session").(*r.Session)
	uploadRequester := newUploadRequester(session)
	cr, err := uploadRequester.request2IDRequest(request, user.ID)
	if err != nil {
		app.Log.Debugf("request2IDRequst failed", err)
		return nil, err
	}

	upload, err := uploadRequester.idService.ID(cr)
	if err != nil {
		app.Log.Debugf("idService.ID failed", err)
		return nil, err
	}

	startingBlock := findStartingBlock(upload.File.Blocks)

	resp := CreateUploadResponse{
		RequestID:     upload.ID,
		StartingBlock: startingBlock,
	}

	return &resp, nil
}

// request2IDRequest fills out an id request to send to the idService. It handles request parameter errors.
func (u *uploadRequester) request2IDRequest(request *restful.Request, userID string) (uploads.IDRequest, error) {
	var req CreateUploadRequest
	var cr uploads.IDRequest

	if err := request.ReadEntity(&req); err != nil {
		app.Log.Debugf("request2IDRequest ReadEntity failed: %s", err)
		return cr, err
	}

	app.Log.Debugf("CreateUploadRequest %#v", req)

	fileMTime, err := time.Parse(time.RFC1123, req.FileMTime)
	if err != nil {
		app.Log.Debugf("request2IDRequest time.Parse failed on %s: %s", req.FileMTime, err)
		return cr, err
	}

	if req.ChunkSize == 0 {
		req.ChunkSize = 1024 * 1024
	}

	directoryID, err := u.getDirectoryID(req)
	if err != nil {
		app.Log.Debugf("makeIDRequest getDirectoryID failed: %s", err)
		return cr, err
	}

	cr = uploads.IDRequest{
		User:        userID,
		DirectoryID: directoryID,
		ProjectID:   req.ProjectID,
		FileName:    req.FileName,
		FileSize:    req.FileSize,
		FileMTime:   fileMTime,
		ChunkSize:   req.ChunkSize,
		Checksum:    req.Checksum,
		Host:        request.Request.RemoteAddr,
		Birthtime:   time.Now(),
	}

	return cr, nil
}

// getDirectoryID returns the directoryID. A user can pass either a directoryID
// or a directory path. If a directory path is passed in, then the method will
// get the directoryID associated with that path in the project. If the path
// doesn't exist it will create it.
func (u *uploadRequester) getDirectoryID(req CreateUploadRequest) (directoryID string, err error) {
	switch {
	case req.DirectoryID == "" && req.DirectoryPath == "":
		app.Log.Debugf("No directoryID or directoryPath specified")
		return "", app.ErrInvalid
	case req.DirectoryID != "":
		return req.DirectoryID, nil
	default:
		dir, err := u.dirService.createDir(req.ProjectID, req.DirectoryPath)
		if err != nil {
			app.Log.Debugf("CreateDir %s %s failed: %s", req.ProjectID, req.DirectoryPath, err)
			return "", err
		}
		return dir.ID, nil
	}
}

// findStartingBlock returns the block to start at. Blocks start
// at 1, since this is what the flow.js client expects. This
// method takes that into account.
func findStartingBlock(blocks *bitset.BitSet) uint {
	if blocks.None() {
		// Nothing set, start at 1
		return 1
	}

	// Create the complement and return first set.
	complement := blocks.Complement()
	if block, status := complement.NextSet(0); !status {
		// This shouldn't happen, but safest case is to check
		// for it and tell the client to resend everything.
		return 1
	} else {
		return block + 1
	}
}

type UploadChunkResponse struct {
	FileID string `json:"file_id"`
	Done   bool   `json:"done"`
}

// uploadFileChunk uploads a new file chunk.
func (ur *uploadResource) uploadFileChunk(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	session := request.Attribute("session").(*r.Session)
	flowRequest, err := form2FlowRequest(request)
	if err != nil {
		ur.log.Errorf("Error converting form to flow.Request: %s", err)
		return nil, err
	}

	req := uploads.UploadRequest{
		Request: flowRequest,
	}

	uploadService := uploads.NewUploadServiceUsingSession(session)
	if uploadStatus, err := uploadService.Upload(&req); err != nil {
		return nil, err
	} else {
		uploadResp := &UploadChunkResponse{
			FileID: uploadStatus.FileID,
			Done:   uploadStatus.Done,
		}
		return uploadResp, nil
	}
}

// deleteUploadRequest will delete an existing upload request. It validates that
// the requesting user has access to delete the request.
func (ur *uploadResource) deleteUploadRequest(request *restful.Request, response *restful.Response, user schema.User) error {
	session := request.Attribute("session").(*r.Session)
	idService := uploads.NewIDServiceUsingSession(session)
	uploadID := request.PathParameter("id")
	return idService.Delete(uploadID, user.ID)
}

// listProjectUploadRequests returns the upload requests for the project if the requester
// has access to the project.
func (ur *uploadResource) listProjectUploadRequests(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	session := request.Attribute("session").(*r.Session)
	idService := uploads.NewIDServiceUsingSession(session)
	projectID := request.PathParameter("project")
	entries, err := idService.UploadsForProject(projectID, user.ID)
	switch {
	case err == app.ErrNotFound:
		var uploads []UploadEntry
		return uploads, nil
	case err != nil:
		return nil, err
	default:
		return uploads2uploadEntries(entries), nil
	}
}

// uploads2uploadEntries converts schema.Upload array into an array of UploadEntry.
func uploads2uploadEntries(projectUploads []schema.Upload) []UploadEntry {
	entries := make([]UploadEntry, len(projectUploads))
	for i, entry := range projectUploads {
		entries[i] = UploadEntry{
			RequestID:   entry.ID,
			FileName:    entry.File.Name,
			DirectoryID: entry.DirectoryID,
			ProjectID:   entry.ProjectID,
			Size:        entry.File.Size,
			Host:        entry.Host,
			Checksum:    entry.File.Checksum,
			Birthtime:   entry.Birthtime,
		}
	}
	return entries
}
<commit_msg>Switch gorethink package name from r to rethinkdb. Change resource variable back to r.<commit_after>package mcstore

import (
	"time"

	"fmt"

	rethinkdb "github.com/dancannon/gorethink"
	"github.com/emicklei/go-restful"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/schema"
	"github.com/materials-commons/mcstore/pkg/ws/rest"
	"github.com/materials-commons/mcstore/server/mcstore/uploads"
	"github.com/willf/bitset"
)

var _ = fmt.Println

// An uploadResource handles all upload requests.
type uploadResource struct {
	log *app.Logger
}

// UploadEntry is a client side representation of an upload.
type UploadEntry struct {
	RequestID   string    `json:"request_id"`
	FileName    string    `json:"filename"`
	DirectoryID string    `json:"directory_id"`
	ProjectID   string    `json:"project_id"`
	Size        int64     `json:"size"`
	Host        string    `json:"host"`
	Checksum    string    `json:"checksum"`
	Birthtime   time.Time `json:"birthtime"`
}

// newUploadResource creates a new upload resource
func newUploadResource() rest.Service {
	return &uploadResource{
		log: app.NewLog("resource", "upload"),
	}
}

// WebService creates an instance of the upload web service.
func (r *uploadResource) WebService() *restful.WebService {
	ws := new(restful.WebService)

	ws.Path("/upload").Produces(restful.MIME_JSON).Consumes(restful.MIME_JSON)

	ws.Route(ws.POST("").To(rest.RouteHandler(r.createUploadRequest)).
		Doc("Creates a new upload request").
		Reads(CreateUploadRequest{}).
		Writes(CreateUploadResponse{}))

	ws.Route(ws.POST("/chunk").To(rest.RouteHandler(r.uploadFileChunk)).
		Consumes("multipart/form-data").
		Writes(UploadChunkResponse{}).
		Doc("Upload a file chunk"))

	ws.Route(ws.DELETE("{id}").To(rest.RouteHandler1(r.deleteUploadRequest)).
		Doc("Deletes an existing upload request").
		Param(ws.PathParameter("id", "upload request to delete").DataType("string")))

	ws.Route(ws.GET("{project}").To(rest.RouteHandler(r.listProjectUploadRequests)).
		Param(ws.PathParameter("project", "project id").DataType("string")).
		Doc("Lists upload requests for project").
		Writes([]UploadEntry{}))

	return ws
}

// CreateRequest describes the JSON request a client will send
// to create a new upload request.
type CreateUploadRequest struct {
	ProjectID     string `json:"project_id"`
	DirectoryID   string `json:"directory_id"`
	DirectoryPath string `json:"directory_path"`
	FileName      string `json:"filename"`
	FileSize      int64  `json:"filesize"`
	ChunkSize     int32  `json:"chunk_size"`
	FileMTime     string `json:"filemtime"`
	Checksum      string `json: "checksum"`
}

// uploadCreateResponse is the format of JSON sent back containing
// the upload request ID.
type CreateUploadResponse struct {
	RequestID     string `json:"request_id"`
	StartingBlock uint   `json:"starting_block"`
}

type uploadRequester struct {
	idService  uploads.IDService
	dirService DirService
}

func newUploadRequester(session *rethinkdb.Session) *uploadRequester {
	return &uploadRequester{
		idService:  uploads.NewIDServiceUsingSession(session),
		dirService: newDirServiceUsingSession(session),
	}
}

// createUploadRequest services requests to create a new upload id. It validates
// the given request, and ensures that the returned upload id is unique. Upload
// requests are persisted until deleted or a successful upload occurs.
func (r *uploadResource) createUploadRequest(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	session := request.Attribute("session").(*rethinkdb.Session)
	uploadRequester := newUploadRequester(session)
	cr, err := uploadRequester.request2IDRequest(request, user.ID)
	if err != nil {
		app.Log.Debugf("request2IDRequst failed", err)
		return nil, err
	}

	upload, err := uploadRequester.idService.ID(cr)
	if err != nil {
		app.Log.Debugf("idService.ID failed", err)
		return nil, err
	}

	startingBlock := findStartingBlock(upload.File.Blocks)

	resp := CreateUploadResponse{
		RequestID:     upload.ID,
		StartingBlock: startingBlock,
	}

	return &resp, nil
}

// request2IDRequest fills out an id request to send to the idService. It handles request parameter errors.
func (u *uploadRequester) request2IDRequest(request *restful.Request, userID string) (uploads.IDRequest, error) {
	var req CreateUploadRequest
	var cr uploads.IDRequest

	if err := request.ReadEntity(&req); err != nil {
		app.Log.Debugf("request2IDRequest ReadEntity failed: %s", err)
		return cr, err
	}

	app.Log.Debugf("CreateUploadRequest %#v", req)

	fileMTime, err := time.Parse(time.RFC1123, req.FileMTime)
	if err != nil {
		app.Log.Debugf("request2IDRequest time.Parse failed on %s: %s", req.FileMTime, err)
		return cr, err
	}

	if req.ChunkSize == 0 {
		req.ChunkSize = 1024 * 1024
	}

	directoryID, err := u.getDirectoryID(req)
	if err != nil {
		app.Log.Debugf("makeIDRequest getDirectoryID failed: %s", err)
		return cr, err
	}

	cr = uploads.IDRequest{
		User:        userID,
		DirectoryID: directoryID,
		ProjectID:   req.ProjectID,
		FileName:    req.FileName,
		FileSize:    req.FileSize,
		FileMTime:   fileMTime,
		ChunkSize:   req.ChunkSize,
		Checksum:    req.Checksum,
		Host:        request.Request.RemoteAddr,
		Birthtime:   time.Now(),
	}

	return cr, nil
}

// getDirectoryID returns the directoryID. A user can pass either a directoryID
// or a directory path. If a directory path is passed in, then the method will
// get the directoryID associated with that path in the project. If the path
// doesn't exist it will create it.
func (u *uploadRequester) getDirectoryID(req CreateUploadRequest) (directoryID string, err error) {
	switch {
	case req.DirectoryID == "" && req.DirectoryPath == "":
		app.Log.Debugf("No directoryID or directoryPath specified")
		return "", app.ErrInvalid
	case req.DirectoryID != "":
		return req.DirectoryID, nil
	default:
		dir, err := u.dirService.createDir(req.ProjectID, req.DirectoryPath)
		if err != nil {
			app.Log.Debugf("CreateDir %s %s failed: %s", req.ProjectID, req.DirectoryPath, err)
			return "", err
		}
		return dir.ID, nil
	}
}

// findStartingBlock returns the block to start at. Blocks start
// at 1, since this is what the flow.js client expects. This
// method takes that into account.
func findStartingBlock(blocks *bitset.BitSet) uint {
	if blocks.None() {
		// Nothing set, start at 1
		return 1
	}

	// Create the complement and return first set.
	complement := blocks.Complement()
	if block, status := complement.NextSet(0); !status {
		// This shouldn't happen, but safest case is to check
		// for it and tell the client to resend everything.
		return 1
	} else {
		return block + 1
	}
}

type UploadChunkResponse struct {
	FileID string `json:"file_id"`
	Done   bool   `json:"done"`
}

// uploadFileChunk uploads a new file chunk.
func (r *uploadResource) uploadFileChunk(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	session := request.Attribute("session").(*rethinkdb.Session)
	flowRequest, err := form2FlowRequest(request)
	if err != nil {
		r.log.Errorf("Error converting form to flow.Request: %s", err)
		return nil, err
	}

	req := uploads.UploadRequest{
		Request: flowRequest,
	}

	uploadService := uploads.NewUploadServiceUsingSession(session)
	if uploadStatus, err := uploadService.Upload(&req); err != nil {
		return nil, err
	} else {
		uploadResp := &UploadChunkResponse{
			FileID: uploadStatus.FileID,
			Done:   uploadStatus.Done,
		}
		return uploadResp, nil
	}
}

// deleteUploadRequest will delete an existing upload request. It validates that
// the requesting user has access to delete the request.
func (r *uploadResource) deleteUploadRequest(request *restful.Request, response *restful.Response, user schema.User) error {
	session := request.Attribute("session").(*rethinkdb.Session)
	idService := uploads.NewIDServiceUsingSession(session)
	uploadID := request.PathParameter("id")
	return idService.Delete(uploadID, user.ID)
}

// listProjectUploadRequests returns the upload requests for the project if the requester
// has access to the project.
func (r *uploadResource) listProjectUploadRequests(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	session := request.Attribute("session").(*rethinkdb.Session)
	idService := uploads.NewIDServiceUsingSession(session)
	projectID := request.PathParameter("project")
	entries, err := idService.UploadsForProject(projectID, user.ID)
	switch {
	case err == app.ErrNotFound:
		var uploads []UploadEntry
		return uploads, nil
	case err != nil:
		return nil, err
	default:
		return uploads2uploadEntries(entries), nil
	}
}

// uploads2uploadEntries converts schema.Upload array into an array of UploadEntry.
func uploads2uploadEntries(projectUploads []schema.Upload) []UploadEntry {
	entries := make([]UploadEntry, len(projectUploads))
	for i, entry := range projectUploads {
		entries[i] = UploadEntry{
			RequestID:   entry.ID,
			FileName:    entry.File.Name,
			DirectoryID: entry.DirectoryID,
			ProjectID:   entry.ProjectID,
			Size:        entry.File.Size,
			Host:        entry.Host,
			Checksum:    entry.File.Checksum,
			Birthtime:   entry.Birthtime,
		}
	}
	return entries
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/acm"
	multierror "github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsAcmCertificateValidation() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAcmCertificateValidationCreate,
		Read:   resourceAwsAcmCertificateValidationRead,
		Delete: resourceAwsAcmCertificateValidationDelete,

		Schema: map[string]*schema.Schema{
			"certificate_arn": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"validation_record_fqdns": {
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
		},
		Timeouts: &schema.ResourceTimeout{
			Create: schema.DefaultTimeout(45 * time.Minute),
		},
	}
}

func resourceAwsAcmCertificateValidationCreate(d *schema.ResourceData, meta interface{}) error {
	certificate_arn := d.Get("certificate_arn").(string)

	acmconn := meta.(*AWSClient).acmconn
	params := &acm.DescribeCertificateInput{
		CertificateArn: aws.String(certificate_arn),
	}

	resp, err := acmconn.DescribeCertificate(params)

	if err != nil {
		return fmt.Errorf("Error describing certificate: %s", err)
	}

	if *resp.Certificate.Type != "AMAZON_ISSUED" {
		return fmt.Errorf("Certificate %s has type %s, no validation necessary", *resp.Certificate.CertificateArn, *resp.Certificate.Type)
	}

	if validation_record_fqdns, ok := d.GetOk("validation_record_fqdns"); ok {
		err := resourceAwsAcmCertificateCheckValidationRecords(validation_record_fqdns.(*schema.Set).List(), resp.Certificate, acmconn)
		if err != nil {
			return err
		}
	} else {
		log.Printf("[INFO] No validation_record_fqdns set, skipping check")
	}

	return resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
		resp, err := acmconn.DescribeCertificate(params)

		if err != nil {
			return resource.NonRetryableError(fmt.Errorf("Error describing certificate: %s", err))
		}

		if *resp.Certificate.Status != "ISSUED" {
			return resource.RetryableError(fmt.Errorf("Expected certificate to be issued but was in state %s", *resp.Certificate.Status))
		}

		log.Printf("[INFO] ACM Certificate validation for %s done, certificate was issued", certificate_arn)
		return resource.NonRetryableError(resourceAwsAcmCertificateValidationRead(d, meta))
	})
}

func resourceAwsAcmCertificateCheckValidationRecords(validationRecordFqdns []interface{}, cert *acm.CertificateDetail, conn *acm.ACM) error {
	expectedFqdns := make(map[string]*acm.DomainValidation)

	if len(cert.DomainValidationOptions) == 0 {
		input := &acm.DescribeCertificateInput{
			CertificateArn: cert.CertificateArn,
		}
		err := resource.Retry(1*time.Minute, func() *resource.RetryError {
			log.Printf("[DEBUG] Certificate domain validation options empty for %q, retrying", *cert.CertificateArn)
			output, err := conn.DescribeCertificate(input)
			if err != nil {
				return resource.NonRetryableError(err)
			}
			if len(output.Certificate.DomainValidationOptions) == 0 {
				return resource.RetryableError(fmt.Errorf("Certificate domain validation options empty for %s", *cert.CertificateArn))
			}
			cert = output.Certificate
			return nil
		})
		if err != nil {
			return err
		}
	}
	for _, v := range cert.DomainValidationOptions {
		if v.ValidationMethod != nil {
			if *v.ValidationMethod != acm.ValidationMethodDns {
				return fmt.Errorf("validation_record_fqdns is only valid for DNS validation")
			}
			newExpectedFqdn := strings.TrimSuffix(*v.ResourceRecord.Name, ".")
			expectedFqdns[newExpectedFqdn] = v
		} else if len(v.ValidationEmails) > 0 {
			// ACM API sometimes is not sending ValidationMethod for EMAIL validation
			return fmt.Errorf("validation_record_fqdns is only valid for DNS validation")
		}
	}

	for _, v := range validationRecordFqdns {
		delete(expectedFqdns, strings.TrimSuffix(v.(string), "."))
	}

	if len(expectedFqdns) > 0 {
		var errors error
		for expectedFqdn, domainValidation := range expectedFqdns {
			errors = multierror.Append(errors, fmt.Errorf("missing %s DNS validation record: %s", *domainValidation.DomainName, expectedFqdn))
		}
		return errors
	}

	return nil
}

func resourceAwsAcmCertificateValidationRead(d *schema.ResourceData, meta interface{}) error {
	acmconn := meta.(*AWSClient).acmconn

	params := &acm.DescribeCertificateInput{
		CertificateArn: aws.String(d.Get("certificate_arn").(string)),
	}

	resp, err := acmconn.DescribeCertificate(params)

	if err != nil && isAWSErr(err, acm.ErrCodeResourceNotFoundException, "") {
		d.SetId("")
		return nil
	} else if err != nil {
		return fmt.Errorf("Error describing certificate: %s", err)
	}

	if *resp.Certificate.Status != "ISSUED" {
		log.Printf("[INFO] Certificate status not issued, was %s, tainting validation", *resp.Certificate.Status)
		d.SetId("")
	} else {
		d.SetId((*resp.Certificate.IssuedAt).String())
	}
	return nil
}

func resourceAwsAcmCertificateValidationDelete(d *schema.ResourceData, meta interface{}) error {
	// No need to do anything, certificate will be deleted when acm_certificate is deleted
	return nil
}
<commit_msg>final retries creating and checking validation for ACM certificates<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/acm"
	multierror "github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsAcmCertificateValidation() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAcmCertificateValidationCreate,
		Read:   resourceAwsAcmCertificateValidationRead,
		Delete: resourceAwsAcmCertificateValidationDelete,

		Schema: map[string]*schema.Schema{
			"certificate_arn": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"validation_record_fqdns": {
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
		},
		Timeouts: &schema.ResourceTimeout{
			Create: schema.DefaultTimeout(45 * time.Minute),
		},
	}
}

func resourceAwsAcmCertificateValidationCreate(d *schema.ResourceData, meta interface{}) error {
	certificate_arn := d.Get("certificate_arn").(string)

	acmconn := meta.(*AWSClient).acmconn
	params := &acm.DescribeCertificateInput{
		CertificateArn: aws.String(certificate_arn),
	}

	resp, err := acmconn.DescribeCertificate(params)

	if err != nil {
		return fmt.Errorf("Error describing certificate: %s", err)
	}

	if *resp.Certificate.Type != "AMAZON_ISSUED" {
		return fmt.Errorf("Certificate %s has type %s, no validation necessary", *resp.Certificate.CertificateArn, *resp.Certificate.Type)
	}

	if validation_record_fqdns, ok := d.GetOk("validation_record_fqdns"); ok {
		err := resourceAwsAcmCertificateCheckValidationRecords(validation_record_fqdns.(*schema.Set).List(), resp.Certificate, acmconn)
		if err != nil {
			return err
		}
	} else {
		log.Printf("[INFO] No validation_record_fqdns set, skipping check")
	}

	err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
		resp, err := acmconn.DescribeCertificate(params)

		if err != nil {
			return resource.NonRetryableError(fmt.Errorf("Error describing certificate: %s", err))
		}

		if *resp.Certificate.Status != "ISSUED" {
			return resource.RetryableError(fmt.Errorf("Expected certificate to be issued but was in state %s", *resp.Certificate.Status))
		}

		log.Printf("[INFO] ACM Certificate validation for %s done, certificate was issued", certificate_arn)
		return resource.NonRetryableError(resourceAwsAcmCertificateValidationRead(d, meta))
	})
	if isResourceTimeoutError(err) {
		resp, err = acmconn.DescribeCertificate(params)
		if *resp.Certificate.Status != "ISSUED" {
			return fmt.Errorf("Expected certificate to be issued but was in state %s", *resp.Certificate.Status)
		}
	}
	if err != nil {
		return fmt.Errorf("Error describing created certificate: %s", err)
	}
	return nil
}

func resourceAwsAcmCertificateCheckValidationRecords(validationRecordFqdns []interface{}, cert *acm.CertificateDetail, conn *acm.ACM) error {
	expectedFqdns := make(map[string]*acm.DomainValidation)

	if len(cert.DomainValidationOptions) == 0 {
		input := &acm.DescribeCertificateInput{
			CertificateArn: cert.CertificateArn,
		}
		var err error
		var output *acm.DescribeCertificateOutput
		err = resource.Retry(1*time.Minute, func() *resource.RetryError {
			log.Printf("[DEBUG] Certificate domain validation options empty for %q, retrying", *cert.CertificateArn)
			output, err = conn.DescribeCertificate(input)
			if err != nil {
				return resource.NonRetryableError(err)
			}
			if len(output.Certificate.DomainValidationOptions) == 0 {
				return resource.RetryableError(fmt.Errorf("Certificate domain validation options empty for %s", *cert.CertificateArn))
			}
			cert = output.Certificate
			return nil
		})
		if isResourceTimeoutError(err) {
			output, err = conn.DescribeCertificate(input)
			if len(output.Certificate.DomainValidationOptions) == 0 {
				return fmt.Errorf("Certificate domain validation options empty for %s", *cert.CertificateArn)
			}
		}
		if err != nil {
			return fmt.Errorf("Error checking certificate domain validation options: %s", err)
		}
		cert = output.Certificate
	}
	for _, v := range cert.DomainValidationOptions {
		if v.ValidationMethod != nil {
			if *v.ValidationMethod != acm.ValidationMethodDns {
				return fmt.Errorf("validation_record_fqdns is only valid for DNS validation")
			}
			newExpectedFqdn := strings.TrimSuffix(*v.ResourceRecord.Name, ".")
			expectedFqdns[newExpectedFqdn] = v
		} else if len(v.ValidationEmails) > 0 {
			// ACM API sometimes is not sending ValidationMethod for EMAIL validation
			return fmt.Errorf("validation_record_fqdns is only valid for DNS validation")
		}
	}

	for _, v := range validationRecordFqdns {
		delete(expectedFqdns, strings.TrimSuffix(v.(string), "."))
	}

	if len(expectedFqdns) > 0 {
		var errors error
		for expectedFqdn, domainValidation := range expectedFqdns {
			errors = multierror.Append(errors, fmt.Errorf("missing %s DNS validation record: %s", *domainValidation.DomainName, expectedFqdn))
		}
		return errors
	}

	return nil
}

func resourceAwsAcmCertificateValidationRead(d *schema.ResourceData, meta interface{}) error {
	acmconn := meta.(*AWSClient).acmconn

	params := &acm.DescribeCertificateInput{
		CertificateArn: aws.String(d.Get("certificate_arn").(string)),
	}

	resp, err := acmconn.DescribeCertificate(params)

	if err != nil && isAWSErr(err, acm.ErrCodeResourceNotFoundException, "") {
		d.SetId("")
		return nil
	} else if err != nil {
		return fmt.Errorf("Error describing certificate: %s", err)
	}

	if *resp.Certificate.Status != "ISSUED" {
		log.Printf("[INFO] Certificate status not issued, was %s, tainting validation", *resp.Certificate.Status)
		d.SetId("")
	} else {
		d.SetId((*resp.Certificate.IssuedAt).String())
	}
	return nil
}

func resourceAwsAcmCertificateValidationDelete(d *schema.ResourceData, meta interface{}) error {
	// No need to do anything, certificate will be deleted when acm_certificate is deleted
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 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 regexp_test

import (
	"fmt"
	"regexp"
	"strings"
)

func Example() {
	// Compile the expression once, usually at init time.
	// Use raw strings to avoid having to quote the backslashes.
	var validID = regexp.MustCompile(`^[a-z]+\[[0-9]+\]$`)

	fmt.Println(validID.MatchString("adam[23]"))
	fmt.Println(validID.MatchString("eve[7]"))
	fmt.Println(validID.MatchString("Job[48]"))
	fmt.Println(validID.MatchString("snakey"))
	// Output:
	// true
	// true
	// false
	// false
}

func ExampleMatch() {
	matched, err := regexp.Match(`foo.*`, []byte(`seafood`))
	fmt.Println(matched, err)
	matched, err = regexp.Match(`bar.*`, []byte(`seafood`))
	fmt.Println(matched, err)
	matched, err = regexp.Match(`a(b`, []byte(`seafood`))
	fmt.Println(matched, err)

	// Output:
	// true <nil>
	// false <nil>
	// false error parsing regexp: missing closing ): `a(b`
}

func ExampleMatchString() {
	matched, err := regexp.MatchString(`foo.*`, "seafood")
	fmt.Println(matched, err)
	matched, err = regexp.MatchString(`bar.*`, "seafood")
	fmt.Println(matched, err)
	matched, err = regexp.MatchString(`a(b`, "seafood")
	fmt.Println(matched, err)
	// Output:
	// true <nil>
	// false <nil>
	// false error parsing regexp: missing closing ): `a(b`
}

func ExampleQuoteMeta() {
	fmt.Println(regexp.QuoteMeta(`Escaping symbols like: .+*?()|[]{}^$`))
	// Output:
	// Escaping symbols like: \.\+\*\?\(\)\|\[\]\{\}\^\$
}

func ExampleRegexp_Find() {
	re := regexp.MustCompile(`foo.?`)
	fmt.Printf("%q\n", re.Find([]byte(`seafood fool`)))

	// Output:
	// "food"
}

func ExampleRegexp_FindAll() {
	re := regexp.MustCompile(`foo.?`)
	fmt.Printf("%q\n", re.FindAll([]byte(`seafood fool`), -1))

	// Output:
	// ["food" "fool"]
}

func ExampleRegexp_FindAllSubmatch() {
	re := regexp.MustCompile(`foo(.?)`)
	fmt.Printf("%q\n", re.FindAllSubmatch([]byte(`seafood fool`), -1))

	// Output:
	// [["food" "d"] ["fool" "l"]]
}

func ExampleRegexp_FindSubmatch() {
	re := regexp.MustCompile(`foo(.?)`)
	fmt.Printf("%q\n", re.FindSubmatch([]byte(`seafood fool`)))

	// Output:
	// ["food" "d"]
}

func ExampleRegexp_Match() {
	re := regexp.MustCompile(`foo.?`)
	fmt.Println(re.Match([]byte(`seafood fool`)))

	// Output:
	// true
}

func ExampleRegexp_FindString() {
	re := regexp.MustCompile(`foo.?`)
	fmt.Printf("%q\n", re.FindString("seafood fool"))
	fmt.Printf("%q\n", re.FindString("meat"))
	// Output:
	// "food"
	// ""
}

func ExampleRegexp_FindStringIndex() {
	re := regexp.MustCompile(`ab?`)
	fmt.Println(re.FindStringIndex("tablett"))
	fmt.Println(re.FindStringIndex("foo") == nil)
	// Output:
	// [1 3]
	// true
}

func ExampleRegexp_FindStringSubmatch() {
	re := regexp.MustCompile(`a(x*)b(y|z)c`)
	fmt.Printf("%q\n", re.FindStringSubmatch("-axxxbyc-"))
	fmt.Printf("%q\n", re.FindStringSubmatch("-abzc-"))
	// Output:
	// ["axxxbyc" "xxx" "y"]
	// ["abzc" "" "z"]
}

func ExampleRegexp_FindAllString() {
	re := regexp.MustCompile(`a.`)
	fmt.Println(re.FindAllString("paranormal", -1))
	fmt.Println(re.FindAllString("paranormal", 2))
	fmt.Println(re.FindAllString("graal", -1))
	fmt.Println(re.FindAllString("none", -1))
	// Output:
	// [ar an al]
	// [ar an]
	// [aa]
	// []
}

func ExampleRegexp_FindAllStringSubmatch() {
	re := regexp.MustCompile(`a(x*)b`)
	fmt.Printf("%q\n", re.FindAllStringSubmatch("-ab-", -1))
	fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxb-", -1))
	fmt.Printf("%q\n", re.FindAllStringSubmatch("-ab-axb-", -1))
	fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxb-ab-", -1))
	// Output:
	// [["ab" ""]]
	// [["axxb" "xx"]]
	// [["ab" ""] ["axb" "x"]]
	// [["axxb" "xx"] ["ab" ""]]
}

func ExampleRegexp_FindAllStringSubmatchIndex() {
	re := regexp.MustCompile(`a(x*)b`)
	// Indices:
	//    01234567   012345678
	//    -ab-axb-   -axxb-ab-
	fmt.Println(re.FindAllStringSubmatchIndex("-ab-", -1))
	fmt.Println(re.FindAllStringSubmatchIndex("-axxb-", -1))
	fmt.Println(re.FindAllStringSubmatchIndex("-ab-axb-", -1))
	fmt.Println(re.FindAllStringSubmatchIndex("-axxb-ab-", -1))
	fmt.Println(re.FindAllStringSubmatchIndex("-foo-", -1))
	// Output:
	// [[1 3 2 2]]
	// [[1 5 2 4]]
	// [[1 3 2 2] [4 7 5 6]]
	// [[1 5 2 4] [6 8 7 7]]
	// []
}

func ExampleRegexp_MatchString() {
	re := regexp.MustCompile(`(gopher){2}`)
	fmt.Println(re.MatchString("gopher"))
	fmt.Println(re.MatchString("gophergopher"))
	fmt.Println(re.MatchString("gophergophergopher"))
	// Output:
	// false
	// true
	// true
}

func ExampleRegexp_NumSubexp() {
	re := regexp.MustCompile(`(.*)((a)b)(.*)a`)
	fmt.Println(re.NumSubexp())
	// Output:
	// 4
}

func ExampleRegexp_ReplaceAll() {
	re := regexp.MustCompile(`a(x*)b`)
	fmt.Printf("%s\n", re.ReplaceAll([]byte("-ab-axxb-"), []byte("T")))
	fmt.Printf("%s\n", re.ReplaceAll([]byte("-ab-axxb-"), []byte("$1")))
	fmt.Printf("%s\n", re.ReplaceAll([]byte("-ab-axxb-"), []byte("$1W")))
	fmt.Printf("%s\n", re.ReplaceAll([]byte("-ab-axxb-"), []byte("${1}W")))
	// Output:
	// -T-T-
	// --xx-
	// ---
	// -W-xxW-
}

func ExampleRegexp_ReplaceAllLiteralString() {
	re := regexp.MustCompile(`a(x*)b`)
	fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "T"))
	fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "$1"))
	fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "${1}"))
	// Output:
	// -T-T-
	// -$1-$1-
	// -${1}-${1}-
}

func ExampleRegexp_ReplaceAllString() {
	re := regexp.MustCompile(`a(x*)b`)
	fmt.Println(re.ReplaceAllString("-ab-axxb-", "T"))
	fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1"))
	fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1W"))
	fmt.Println(re.ReplaceAllString("-ab-axxb-", "${1}W"))
	// Output:
	// -T-T-
	// --xx-
	// ---
	// -W-xxW-
}

func ExampleRegexp_ReplaceAllStringFunc() {
	re := regexp.MustCompile(`[^aeiou]`)
	fmt.Println(re.ReplaceAllStringFunc("seafood fool", strings.ToUpper))
	// Output:
	// SeaFooD FooL
}

func ExampleRegexp_SubexpNames() {
	re := regexp.MustCompile(`(?P<first>[a-zA-Z]+) (?P<last>[a-zA-Z]+)`)
	fmt.Println(re.MatchString("Alan Turing"))
	fmt.Printf("%q\n", re.SubexpNames())
	reversed := fmt.Sprintf("${%s} ${%s}", re.SubexpNames()[2], re.SubexpNames()[1])
	fmt.Println(reversed)
	fmt.Println(re.ReplaceAllString("Alan Turing", reversed))
	// Output:
	// true
	// ["" "first" "last"]
	// ${last} ${first}
	// Turing Alan
}

func ExampleRegexp_Split() {
	a := regexp.MustCompile(`a`)
	fmt.Println(a.Split("banana", -1))
	fmt.Println(a.Split("banana", 0))
	fmt.Println(a.Split("banana", 1))
	fmt.Println(a.Split("banana", 2))
	zp := regexp.MustCompile(`z+`)
	fmt.Println(zp.Split("pizza", -1))
	fmt.Println(zp.Split("pizza", 0))
	fmt.Println(zp.Split("pizza", 1))
	fmt.Println(zp.Split("pizza", 2))
	// Output:
	// [b n n ]
	// []
	// [banana]
	// [b nana]
	// [pi a]
	// []
	// [pizza]
	// [pi a]
}

func ExampleRegexp_Expand() {
	content := []byte(`
	# comment line
	option1: value1
	option2: value2

	# another comment line
	option3: value3
`)

	// Regex pattern captures "key: value" pair from the content.
	pattern := regexp.MustCompile(`(?m)(?P<key>\w+):\s+(?P<value>\w+)$`)

	// Template to convert "key: value" to "key=value" by
	// referencing the values captured by the regex pattern.
	template := []byte("$key=$value\n")

	result := []byte{}

	// For each match of the regex in the content.
	for _, submatches := range pattern.FindAllSubmatchIndex(content, -1) {
		// Apply the captured submatches to the template and append the output
		// to the result.
		result = pattern.Expand(result, template, content, submatches)
	}
	fmt.Println(string(result))
	// Output:
	// option1=value1
	// option2=value2
	// option3=value3
}

func ExampleRegexp_ExpandString() {
	content := `
	# comment line
	option1: value1
	option2: value2

	# another comment line
	option3: value3
`

	// Regex pattern captures "key: value" pair from the content.
	pattern := regexp.MustCompile(`(?m)(?P<key>\w+):\s+(?P<value>\w+)$`)

	// Template to convert "key: value" to "key=value" by
	// referencing the values captured by the regex pattern.
	template := "$key=$value\n"

	result := []byte{}

	// For each match of the regex in the content.
	for _, submatches := range pattern.FindAllStringSubmatchIndex(content, -1) {
		// Apply the captured submatches to the template and append the output
		// to the result.
		result = pattern.ExpandString(result, template, content, submatches)
	}
	fmt.Println(string(result))
	// Output:
	// option1=value1
	// option2=value2
	// option3=value3
}

func ExampleRegexp_FindIndex() {
	content := []byte(`
	# comment line
	option1: value1
	option2: value2
`)
	// Regex pattern captures "key: value" pair from the content.
	pattern := regexp.MustCompile(`(?m)(?P<key>\w+):\s+(?P<value>\w+)$`)

	loc := pattern.FindIndex(content)
	fmt.Println(loc)
	fmt.Println(string(content[loc[0]:loc[1]]))
	// Output:
	// [18 33]
	// option1: value1
}
func ExampleRegexp_FindAllSubmatchIndex() {
	content := []byte(`
	# comment line
	option1: value1
	option2: value2
`)
	// Regex pattern captures "key: value" pair from the content.
	pattern := regexp.MustCompile(`(?m)(?P<key>\w+):\s+(?P<value>\w+)$`)
	allIndexes := pattern.FindAllSubmatchIndex(content, -1)
	for _, loc := range allIndexes {
		fmt.Println(loc)
		fmt.Println(string(content[loc[0]:loc[1]]))
		fmt.Println(string(content[loc[2]:loc[3]]))
		fmt.Println(string(content[loc[4]:loc[5]]))
	}
	// Output:
	// [18 33 18 25 27 33]
	// option1: value1
	// option1
	// value1
	// [35 50 35 42 44 50]
	// option2: value2
	// option2
	// value2
}
<commit_msg>regexp: add more examples for Regexp methods<commit_after>// Copyright 2013 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 regexp_test

import (
	"fmt"
	"regexp"
	"strings"
)

func Example() {
	// Compile the expression once, usually at init time.
	// Use raw strings to avoid having to quote the backslashes.
	var validID = regexp.MustCompile(`^[a-z]+\[[0-9]+\]$`)

	fmt.Println(validID.MatchString("adam[23]"))
	fmt.Println(validID.MatchString("eve[7]"))
	fmt.Println(validID.MatchString("Job[48]"))
	fmt.Println(validID.MatchString("snakey"))
	// Output:
	// true
	// true
	// false
	// false
}

func ExampleMatch() {
	matched, err := regexp.Match(`foo.*`, []byte(`seafood`))
	fmt.Println(matched, err)
	matched, err = regexp.Match(`bar.*`, []byte(`seafood`))
	fmt.Println(matched, err)
	matched, err = regexp.Match(`a(b`, []byte(`seafood`))
	fmt.Println(matched, err)

	// Output:
	// true <nil>
	// false <nil>
	// false error parsing regexp: missing closing ): `a(b`
}

func ExampleMatchString() {
	matched, err := regexp.MatchString(`foo.*`, "seafood")
	fmt.Println(matched, err)
	matched, err = regexp.MatchString(`bar.*`, "seafood")
	fmt.Println(matched, err)
	matched, err = regexp.MatchString(`a(b`, "seafood")
	fmt.Println(matched, err)
	// Output:
	// true <nil>
	// false <nil>
	// false error parsing regexp: missing closing ): `a(b`
}

func ExampleQuoteMeta() {
	fmt.Println(regexp.QuoteMeta(`Escaping symbols like: .+*?()|[]{}^$`))
	// Output:
	// Escaping symbols like: \.\+\*\?\(\)\|\[\]\{\}\^\$
}

func ExampleRegexp_Find() {
	re := regexp.MustCompile(`foo.?`)
	fmt.Printf("%q\n", re.Find([]byte(`seafood fool`)))

	// Output:
	// "food"
}

func ExampleRegexp_FindAll() {
	re := regexp.MustCompile(`foo.?`)
	fmt.Printf("%q\n", re.FindAll([]byte(`seafood fool`), -1))

	// Output:
	// ["food" "fool"]
}

func ExampleRegexp_FindAllSubmatch() {
	re := regexp.MustCompile(`foo(.?)`)
	fmt.Printf("%q\n", re.FindAllSubmatch([]byte(`seafood fool`), -1))

	// Output:
	// [["food" "d"] ["fool" "l"]]
}

func ExampleRegexp_FindSubmatch() {
	re := regexp.MustCompile(`foo(.?)`)
	fmt.Printf("%q\n", re.FindSubmatch([]byte(`seafood fool`)))

	// Output:
	// ["food" "d"]
}

func ExampleRegexp_Match() {
	re := regexp.MustCompile(`foo.?`)
	fmt.Println(re.Match([]byte(`seafood fool`)))
	fmt.Println(re.Match([]byte(`something else`)))

	// Output:
	// true
	// false
}

func ExampleRegexp_FindString() {
	re := regexp.MustCompile(`foo.?`)
	fmt.Printf("%q\n", re.FindString("seafood fool"))
	fmt.Printf("%q\n", re.FindString("meat"))
	// Output:
	// "food"
	// ""
}

func ExampleRegexp_FindStringIndex() {
	re := regexp.MustCompile(`ab?`)
	fmt.Println(re.FindStringIndex("tablett"))
	fmt.Println(re.FindStringIndex("foo") == nil)
	// Output:
	// [1 3]
	// true
}

func ExampleRegexp_FindStringSubmatch() {
	re := regexp.MustCompile(`a(x*)b(y|z)c`)
	fmt.Printf("%q\n", re.FindStringSubmatch("-axxxbyc-"))
	fmt.Printf("%q\n", re.FindStringSubmatch("-abzc-"))
	// Output:
	// ["axxxbyc" "xxx" "y"]
	// ["abzc" "" "z"]
}

func ExampleRegexp_FindAllString() {
	re := regexp.MustCompile(`a.`)
	fmt.Println(re.FindAllString("paranormal", -1))
	fmt.Println(re.FindAllString("paranormal", 2))
	fmt.Println(re.FindAllString("graal", -1))
	fmt.Println(re.FindAllString("none", -1))
	// Output:
	// [ar an al]
	// [ar an]
	// [aa]
	// []
}

func ExampleRegexp_FindAllStringSubmatch() {
	re := regexp.MustCompile(`a(x*)b`)
	fmt.Printf("%q\n", re.FindAllStringSubmatch("-ab-", -1))
	fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxb-", -1))
	fmt.Printf("%q\n", re.FindAllStringSubmatch("-ab-axb-", -1))
	fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxb-ab-", -1))
	// Output:
	// [["ab" ""]]
	// [["axxb" "xx"]]
	// [["ab" ""] ["axb" "x"]]
	// [["axxb" "xx"] ["ab" ""]]
}

func ExampleRegexp_FindAllStringSubmatchIndex() {
	re := regexp.MustCompile(`a(x*)b`)
	// Indices:
	//    01234567   012345678
	//    -ab-axb-   -axxb-ab-
	fmt.Println(re.FindAllStringSubmatchIndex("-ab-", -1))
	fmt.Println(re.FindAllStringSubmatchIndex("-axxb-", -1))
	fmt.Println(re.FindAllStringSubmatchIndex("-ab-axb-", -1))
	fmt.Println(re.FindAllStringSubmatchIndex("-axxb-ab-", -1))
	fmt.Println(re.FindAllStringSubmatchIndex("-foo-", -1))
	// Output:
	// [[1 3 2 2]]
	// [[1 5 2 4]]
	// [[1 3 2 2] [4 7 5 6]]
	// [[1 5 2 4] [6 8 7 7]]
	// []
}

func ExampleRegexp_MatchString() {
	re := regexp.MustCompile(`(gopher){2}`)
	fmt.Println(re.MatchString("gopher"))
	fmt.Println(re.MatchString("gophergopher"))
	fmt.Println(re.MatchString("gophergophergopher"))
	// Output:
	// false
	// true
	// true
}

func ExampleRegexp_NumSubexp() {
	re0 := regexp.MustCompile(`a.`)
	fmt.Printf("%d\n", re0.NumSubexp())

	re := regexp.MustCompile(`(.*)((a)b)(.*)a`)
	fmt.Println(re.NumSubexp())
	// Output:
	// 0
	// 4
}

func ExampleRegexp_ReplaceAll() {
	re := regexp.MustCompile(`a(x*)b`)
	fmt.Printf("%s\n", re.ReplaceAll([]byte("-ab-axxb-"), []byte("T")))
	fmt.Printf("%s\n", re.ReplaceAll([]byte("-ab-axxb-"), []byte("$1")))
	fmt.Printf("%s\n", re.ReplaceAll([]byte("-ab-axxb-"), []byte("$1W")))
	fmt.Printf("%s\n", re.ReplaceAll([]byte("-ab-axxb-"), []byte("${1}W")))
	// Output:
	// -T-T-
	// --xx-
	// ---
	// -W-xxW-
}

func ExampleRegexp_ReplaceAllLiteralString() {
	re := regexp.MustCompile(`a(x*)b`)
	fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "T"))
	fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "$1"))
	fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "${1}"))
	// Output:
	// -T-T-
	// -$1-$1-
	// -${1}-${1}-
}

func ExampleRegexp_ReplaceAllString() {
	re := regexp.MustCompile(`a(x*)b`)
	fmt.Println(re.ReplaceAllString("-ab-axxb-", "T"))
	fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1"))
	fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1W"))
	fmt.Println(re.ReplaceAllString("-ab-axxb-", "${1}W"))
	// Output:
	// -T-T-
	// --xx-
	// ---
	// -W-xxW-
}

func ExampleRegexp_ReplaceAllStringFunc() {
	re := regexp.MustCompile(`[^aeiou]`)
	fmt.Println(re.ReplaceAllStringFunc("seafood fool", strings.ToUpper))
	// Output:
	// SeaFooD FooL
}

func ExampleRegexp_SubexpNames() {
	re := regexp.MustCompile(`(?P<first>[a-zA-Z]+) (?P<last>[a-zA-Z]+)`)
	fmt.Println(re.MatchString("Alan Turing"))
	fmt.Printf("%q\n", re.SubexpNames())
	reversed := fmt.Sprintf("${%s} ${%s}", re.SubexpNames()[2], re.SubexpNames()[1])
	fmt.Println(reversed)
	fmt.Println(re.ReplaceAllString("Alan Turing", reversed))
	// Output:
	// true
	// ["" "first" "last"]
	// ${last} ${first}
	// Turing Alan
}

func ExampleRegexp_Split() {
	a := regexp.MustCompile(`a`)
	fmt.Println(a.Split("banana", -1))
	fmt.Println(a.Split("banana", 0))
	fmt.Println(a.Split("banana", 1))
	fmt.Println(a.Split("banana", 2))
	zp := regexp.MustCompile(`z+`)
	fmt.Println(zp.Split("pizza", -1))
	fmt.Println(zp.Split("pizza", 0))
	fmt.Println(zp.Split("pizza", 1))
	fmt.Println(zp.Split("pizza", 2))
	// Output:
	// [b n n ]
	// []
	// [banana]
	// [b nana]
	// [pi a]
	// []
	// [pizza]
	// [pi a]
}

func ExampleRegexp_Expand() {
	content := []byte(`
	# comment line
	option1: value1
	option2: value2

	# another comment line
	option3: value3
`)

	// Regex pattern captures "key: value" pair from the content.
	pattern := regexp.MustCompile(`(?m)(?P<key>\w+):\s+(?P<value>\w+)$`)

	// Template to convert "key: value" to "key=value" by
	// referencing the values captured by the regex pattern.
	template := []byte("$key=$value\n")

	result := []byte{}

	// For each match of the regex in the content.
	for _, submatches := range pattern.FindAllSubmatchIndex(content, -1) {
		// Apply the captured submatches to the template and append the output
		// to the result.
		result = pattern.Expand(result, template, content, submatches)
	}
	fmt.Println(string(result))
	// Output:
	// option1=value1
	// option2=value2
	// option3=value3
}

func ExampleRegexp_ExpandString() {
	content := `
	# comment line
	option1: value1
	option2: value2

	# another comment line
	option3: value3
`

	// Regex pattern captures "key: value" pair from the content.
	pattern := regexp.MustCompile(`(?m)(?P<key>\w+):\s+(?P<value>\w+)$`)

	// Template to convert "key: value" to "key=value" by
	// referencing the values captured by the regex pattern.
	template := "$key=$value\n"

	result := []byte{}

	// For each match of the regex in the content.
	for _, submatches := range pattern.FindAllStringSubmatchIndex(content, -1) {
		// Apply the captured submatches to the template and append the output
		// to the result.
		result = pattern.ExpandString(result, template, content, submatches)
	}
	fmt.Println(string(result))
	// Output:
	// option1=value1
	// option2=value2
	// option3=value3
}

func ExampleRegexp_FindIndex() {
	content := []byte(`
	# comment line
	option1: value1
	option2: value2
`)
	// Regex pattern captures "key: value" pair from the content.
	pattern := regexp.MustCompile(`(?m)(?P<key>\w+):\s+(?P<value>\w+)$`)

	loc := pattern.FindIndex(content)
	fmt.Println(loc)
	fmt.Println(string(content[loc[0]:loc[1]]))
	// Output:
	// [18 33]
	// option1: value1
}

func ExampleRegexp_FindAllSubmatchIndex() {
	content := []byte(`
	# comment line
	option1: value1
	option2: value2
`)
	// Regex pattern captures "key: value" pair from the content.
	pattern := regexp.MustCompile(`(?m)(?P<key>\w+):\s+(?P<value>\w+)$`)
	allIndexes := pattern.FindAllSubmatchIndex(content, -1)
	for _, loc := range allIndexes {
		fmt.Println(loc)
		fmt.Println(string(content[loc[0]:loc[1]]))
		fmt.Println(string(content[loc[2]:loc[3]]))
		fmt.Println(string(content[loc[4]:loc[5]]))
	}
	// Output:
	// [18 33 18 25 27 33]
	// option1: value1
	// option1
	// value1
	// [35 50 35 42 44 50]
	// option2: value2
	// option2
	// value2
}

func ExampleRegexp_FindAllIndex() {
	content := []byte("London")
	re := regexp.MustCompile(`o.`)
	fmt.Println(re.FindAllIndex(content, 1))
	fmt.Println(re.FindAllIndex(content, -1))
	// Output:
	// [[1 3]]
	// [[1 3] [4 6]]
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/cri-o/cri-o/internal/version"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"k8s.io/release/pkg/command"
	"k8s.io/release/pkg/git"
	"k8s.io/release/pkg/util"
)

const branch = "gh-pages"

var outputPath string

func main() {
	// Parse CLI flags
	flag.StringVar(&outputPath,
		"output-path", "", "the output path for the release notes",
	)
	flag.Parse()

	logrus.SetFormatter(&logrus.TextFormatter{DisableTimestamp: true})
	logrus.SetLevel(logrus.DebugLevel)
	command.SetGlobalVerbose(true)

	if err := run(); err != nil {
		logrus.Fatalf("unable to %v", err)
	}
}

func run() error {
	// Precheck environemt
	if !util.IsEnvSet("GITHUB_TOKEN") {
		return errors.Errorf("GITHUB_TOKEN environemt variable is not set")
	}

	logrus.Infof("Ensuring output path %s", outputPath)
	if err := os.MkdirAll(outputPath, 0o755); err != nil {
		return errors.Wrap(err, "create output path")
	}

	// Get latest release version
	startTag := util.AddTagPrefix(decVersion(version.Version))
	logrus.Infof("Using start tag %s", startTag)

	endTag := util.AddTagPrefix(version.Version)
	logrus.Infof("Using end tag %s", endTag)

	// Generate the notes
	repo, err := git.OpenRepo(".")
	if err != nil {
		return errors.Wrap(err, "open local repo")
	}

	head, err := repo.Head()
	if err != nil {
		return errors.Wrap(err, "get repository HEAD")
	}
	logrus.Infof("Using HEAD commit %s", head)

	targetBranch := git.Master
	currentBranch, err := repo.CurrentBranch()
	if err != nil {
		return errors.Wrap(err, "get current branch")
	}
	logrus.Infof("Found current branch %s", currentBranch)
	if git.IsReleaseBranch(currentBranch) && currentBranch != git.Master {
		targetBranch = currentBranch
	}
	logrus.Infof("Using target branch %s", targetBranch)

	templateFile, err := ioutil.TempFile("", "")
	if err != nil {
		return errors.Wrap(err, "writing template file")
	}
	defer func() { err = os.RemoveAll(templateFile.Name()) }()

	// Check if we're on a tag and adapt variables if necessary
	bundleVersion := head[:9]
	shortHead := head[:7]
	endRev := head
	if output, err := command.New(
		"git", "describe", "--exact-match",
	).RunSilentSuccessOutput(); err == nil {
		foundTag := output.OutputTrimNL()
		bundleVersion = foundTag
		shortHead = foundTag
		endRev = foundTag
	}

	if _, err := templateFile.WriteString(fmt.Sprintf(`# CRI-O %s

The release notes have been generated for the commit range
[%s...%s](https://github.com/cri-o/cri-o/compare/%s...%s) on %s.

## Downloads

Download the static release bundle via our Google Cloud Bucket:
[crio-%s.tar.gz][0]

[0]: https://storage.googleapis.com/k8s-conform-cri-o/artifacts/crio-%s.tar.gz

## Changelog since %s

{{with .NotesWithActionRequired -}}
### Urgent Upgrade Notes

{{range .}} {{println "-" .}} {{end}}
{{end}}

{{- if .Notes -}}
### Changes by Kind
{{ range .Notes}}
#### {{.Kind | prettyKind}}
{{range $note := .NoteEntries }}{{println " -" $note}}{{end}}
{{- end -}}
{{- end -}}
`,
		endTag,
		startTag, shortHead,
		startTag, endRev,
		time.Now().Format(time.RFC1123),
		bundleVersion, bundleVersion,
		startTag,
	)); err != nil {
		return errors.Wrap(err, "writing tmplate to file")
	}

	logrus.Infof("Generating release notes")
	outputFile := endTag + ".md"
	outputFilePath := filepath.Join(outputPath, outputFile)
	os.RemoveAll(outputFilePath)
	if err := command.Execute(
		"./build/bin/release-notes",
		"--github-org=cri-o",
		"--github-repo=cri-o",
		"--branch="+targetBranch,
		"--repo-path=/tmp/cri-o-repo",
		"--required-author=",
		"--start-rev="+startTag,
		"--end-sha="+head,
		"--output="+outputFilePath,
		"--toc",
		"--format=go-template:"+templateFile.Name(),
	); err != nil {
		return errors.Wrap(err, "generate release notes")
	}

	// Update gh-pages branch if not a pull request and running in CircleCI
	if util.IsEnvSet("CIRCLECI") && !util.IsEnvSet("CIRCLE_PULL_REQUEST") {
		content, err := ioutil.ReadFile(outputFilePath)
		if err != nil {
			return errors.Wrap(err, "open generated release notes")
		}

		logrus.Infof("Checking out branch %s", branch)
		if err := repo.Checkout(branch); err != nil {
			return errors.Wrapf(err, "checkout %s branch", branch)
		}
		defer func() { err = repo.Checkout(currentBranch) }()

		// Write the target file
		if err := ioutil.WriteFile(outputFile, content, 0o644); err != nil {
			return errors.Wrap(err, "write content to file")
		}

		if err := repo.Add(outputFile); err != nil {
			return errors.Wrap(err, "add file to repo")
		}

		// Update the README
		readmeFile := "README.md"
		logrus.Infof("Updating %s", readmeFile)
		readmeSlice, err := readLines(readmeFile)
		if err != nil {
			return errors.Wrapf(err, "open %s file", readmeFile)
		}
		link := fmt.Sprintf("- [%s](%s)", endTag, outputFile)

		// Item not in list
		alreadyExistingIndex := indexOfPrefix(link, readmeSlice)
		if alreadyExistingIndex < 0 {
			firstListEntry := indexOfPrefix("- ", readmeSlice)

			if firstListEntry < 0 {
				// No list available, just append
				readmeSlice = append(readmeSlice, link)
			} else {
				// Insert into slice
				readmeSlice = append(
					readmeSlice[:firstListEntry],
					append([]string{link}, readmeSlice[firstListEntry:]...)...,
				)
			}
		} else {
			readmeSlice[alreadyExistingIndex] = link
		}
		if err := ioutil.WriteFile(
			readmeFile, []byte(strings.Join(readmeSlice, "\n")), 0o644,
		); err != nil {
			return errors.Wrap(err, "write content to file")
		}
		if err := repo.Add(readmeFile); err != nil {
			return errors.Wrap(err, "add file to repo")
		}

		// Publish the changes
		if err := repo.Commit("Update release notes"); err != nil {
			return errors.Wrap(err, "commit")
		}

		if err := repo.Push(branch); err != nil {
			return errors.Wrap(err, "push changes")
		}
	}

	return nil
}

func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}

func indexOfPrefix(prefix string, slice []string) int {
	for k, v := range slice {
		if strings.HasPrefix(v, prefix) {
			return k
		}
	}
	return -1
}

func decVersion(tag string) string {
	sv, err := util.TagStringToSemver(strings.TrimSpace(tag))
	if err != nil {
		panic(err)
	}

	// clear any RC
	sv.Pre = nil

	if sv.Patch > 0 { // nolint: gocritic
		sv.Patch-- // 1.17.2 -> 1.17.1
	} else if sv.Minor > 0 {
		sv.Minor-- // 1.18.0 -> 1.17.0
	} else if sv.Major > 0 {
		sv.Major-- // 1.19.0 -> 2.0.0 (should never happen)
	} else {
		panic(fmt.Sprintf("unable to decrement version %v", sv))
	}

	return sv.String()
}
<commit_msg>Fix release notes generation<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/cri-o/cri-o/internal/version"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"k8s.io/release/pkg/command"
	"k8s.io/release/pkg/git"
	"k8s.io/release/pkg/util"
)

const branch = "gh-pages"

var outputPath string

func main() {
	// Parse CLI flags
	flag.StringVar(&outputPath,
		"output-path", "", "the output path for the release notes",
	)
	flag.Parse()

	logrus.SetFormatter(&logrus.TextFormatter{DisableTimestamp: true})
	logrus.SetLevel(logrus.DebugLevel)
	command.SetGlobalVerbose(true)

	if err := run(); err != nil {
		logrus.Fatalf("unable to %v", err)
	}
}

func run() error {
	// Precheck environemt
	if !util.IsEnvSet("GITHUB_TOKEN") {
		return errors.Errorf("GITHUB_TOKEN environemt variable is not set")
	}

	logrus.Infof("Ensuring output path %s", outputPath)
	if err := os.MkdirAll(outputPath, 0o755); err != nil {
		return errors.Wrap(err, "create output path")
	}

	// Get latest release version
	startTag := util.AddTagPrefix(decVersion(version.Version))
	logrus.Infof("Using start tag %s", startTag)

	endTag := util.AddTagPrefix(version.Version)
	logrus.Infof("Using end tag %s", endTag)

	// Generate the notes
	repo, err := git.OpenRepo(".")
	if err != nil {
		return errors.Wrap(err, "open local repo")
	}

	head, err := repo.Head()
	if err != nil {
		return errors.Wrap(err, "get repository HEAD")
	}
	logrus.Infof("Using HEAD commit %s", head)

	targetBranch := git.Master
	currentBranch, err := repo.CurrentBranch()
	if err != nil {
		return errors.Wrap(err, "get current branch")
	}
	logrus.Infof("Found current branch %s", currentBranch)
	if git.IsReleaseBranch(currentBranch) && currentBranch != git.Master {
		targetBranch = currentBranch
	}
	logrus.Infof("Using target branch %s", targetBranch)

	templateFile, err := ioutil.TempFile("", "")
	if err != nil {
		return errors.Wrap(err, "writing template file")
	}
	defer func() { err = os.RemoveAll(templateFile.Name()) }()

	// Check if we're on a tag and adapt variables if necessary
	bundleVersion := head[:9]
	shortHead := head[:7]
	endRev := head
	if output, err := command.New(
		"git", "describe", "--exact-match",
	).RunSilentSuccessOutput(); err == nil {
		foundTag := output.OutputTrimNL()
		bundleVersion = foundTag
		shortHead = foundTag
		endRev = foundTag
	}

	if _, err := templateFile.WriteString(fmt.Sprintf(`# CRI-O %s

The release notes have been generated for the commit range
[%s...%s](https://github.com/cri-o/cri-o/compare/%s...%s) on %s.

## Downloads

Download the static release bundle via our Google Cloud Bucket:
[crio-%s.tar.gz][0]

[0]: https://storage.googleapis.com/k8s-conform-cri-o/artifacts/crio-%s.tar.gz

## Changelog since %s

{{with .NotesWithActionRequired -}}
### Urgent Upgrade Notes

{{range .}} {{println "-" .}} {{end}}
{{end}}

{{- if .Notes -}}
### Changes by Kind
{{ range .Notes}}
#### {{.Kind | prettyKind}}
{{range $note := .NoteEntries }}{{println " -" $note}}{{end}}
{{- end -}}
{{- end -}}
`,
		endTag,
		startTag, shortHead,
		startTag, endRev,
		time.Now().Format(time.RFC1123),
		bundleVersion, bundleVersion,
		startTag,
	)); err != nil {
		return errors.Wrap(err, "writing tmplate to file")
	}

	logrus.Infof("Generating release notes")
	outputFile := endTag + ".md"
	outputFilePath := filepath.Join(outputPath, outputFile)
	os.RemoveAll(outputFilePath)
	if err := command.Execute(
		"./build/bin/release-notes",
		"--github-org=cri-o",
		"--github-repo=cri-o",
		"--branch="+targetBranch,
		"--repo-path=/tmp/cri-o-repo",
		"--required-author=",
		"--start-rev="+startTag,
		"--end-sha="+head,
		"--output="+outputFilePath,
		"--toc",
		"--go-template=go-template:"+templateFile.Name(),
	); err != nil {
		return errors.Wrap(err, "generate release notes")
	}

	// Update gh-pages branch if not a pull request and running in CircleCI
	if util.IsEnvSet("CIRCLECI") && !util.IsEnvSet("CIRCLE_PULL_REQUEST") {
		content, err := ioutil.ReadFile(outputFilePath)
		if err != nil {
			return errors.Wrap(err, "open generated release notes")
		}

		logrus.Infof("Checking out branch %s", branch)
		if err := repo.Checkout(branch); err != nil {
			return errors.Wrapf(err, "checkout %s branch", branch)
		}
		defer func() { err = repo.Checkout(currentBranch) }()

		// Write the target file
		if err := ioutil.WriteFile(outputFile, content, 0o644); err != nil {
			return errors.Wrap(err, "write content to file")
		}

		if err := repo.Add(outputFile); err != nil {
			return errors.Wrap(err, "add file to repo")
		}

		// Update the README
		readmeFile := "README.md"
		logrus.Infof("Updating %s", readmeFile)
		readmeSlice, err := readLines(readmeFile)
		if err != nil {
			return errors.Wrapf(err, "open %s file", readmeFile)
		}
		link := fmt.Sprintf("- [%s](%s)", endTag, outputFile)

		// Item not in list
		alreadyExistingIndex := indexOfPrefix(link, readmeSlice)
		if alreadyExistingIndex < 0 {
			firstListEntry := indexOfPrefix("- ", readmeSlice)

			if firstListEntry < 0 {
				// No list available, just append
				readmeSlice = append(readmeSlice, link)
			} else {
				// Insert into slice
				readmeSlice = append(
					readmeSlice[:firstListEntry],
					append([]string{link}, readmeSlice[firstListEntry:]...)...,
				)
			}
		} else {
			readmeSlice[alreadyExistingIndex] = link
		}
		if err := ioutil.WriteFile(
			readmeFile, []byte(strings.Join(readmeSlice, "\n")), 0o644,
		); err != nil {
			return errors.Wrap(err, "write content to file")
		}
		if err := repo.Add(readmeFile); err != nil {
			return errors.Wrap(err, "add file to repo")
		}

		// Publish the changes
		if err := repo.Commit("Update release notes"); err != nil {
			return errors.Wrap(err, "commit")
		}

		if err := repo.Push(branch); err != nil {
			return errors.Wrap(err, "push changes")
		}
	}

	return nil
}

func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}

func indexOfPrefix(prefix string, slice []string) int {
	for k, v := range slice {
		if strings.HasPrefix(v, prefix) {
			return k
		}
	}
	return -1
}

func decVersion(tag string) string {
	sv, err := util.TagStringToSemver(strings.TrimSpace(tag))
	if err != nil {
		panic(err)
	}

	// clear any RC
	sv.Pre = nil

	if sv.Patch > 0 { // nolint: gocritic
		sv.Patch-- // 1.17.2 -> 1.17.1
	} else if sv.Minor > 0 {
		sv.Minor-- // 1.18.0 -> 1.17.0
	} else if sv.Major > 0 {
		sv.Major-- // 1.19.0 -> 2.0.0 (should never happen)
	} else {
		panic(fmt.Sprintf("unable to decrement version %v", sv))
	}

	return sv.String()
}
<|endoftext|>


<commit_before>// Copyright 2018 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

import "unsafe"

// This is based on the former libgo/runtime/netpoll_select.c implementation
// except that it uses poll instead of select and is written in Go.
// It's also based on Solaris implementation for the arming mechanisms

//go:cgo_import_dynamic libc_poll poll "libc.a/shr_64.o"
//go:linkname libc_poll libc_poll

var libc_poll libFunc

//go:nosplit
func poll(pfds *pollfd, npfds uintptr, timeout uintptr) (int32, int32) {
	r, err := syscall3(&libc_poll, uintptr(unsafe.Pointer(pfds)), npfds, timeout)
	return int32(r), int32(err)
}

// pollfd represents the poll structure for AIX operating system.
type pollfd struct {
	fd      int32
	events  int16
	revents int16
}

const _POLLIN = 0x0001
const _POLLOUT = 0x0002
const _POLLHUP = 0x2000
const _POLLERR = 0x4000

var (
	pfds           []pollfd
	pds            []*pollDesc
	mtxpoll        mutex
	mtxset         mutex
	rdwake         int32
	wrwake         int32
	pendingUpdates int32
)

func netpollinit() {
	// Create the pipe we use to wakeup poll.
	r, w, errno := nonblockingPipe()
	if errno != 0 {
		throw("netpollinit: failed to create pipe")
	}
	rdwake = r
	wrwake = w

	// Pre-allocate array of pollfd structures for poll.
	pfds = make([]pollfd, 1, 128)

	// Poll the read side of the pipe.
	pfds[0].fd = rdwake
	pfds[0].events = _POLLIN

	pds = make([]*pollDesc, 1, 128)
	pds[0] = nil
}

func netpollIsPollDescriptor(fd uintptr) bool {
	return fd == uintptr(rdwake) || fd == uintptr(wrwake)
}

// netpollwakeup writes on wrwake to wakeup poll before any changes.
func netpollwakeup() {
	if pendingUpdates == 0 {
		pendingUpdates = 1
		b := [1]byte{0}
		write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1)
	}
}

func netpollopen(fd uintptr, pd *pollDesc) int32 {
	lock(&mtxpoll)
	netpollwakeup()

	lock(&mtxset)
	unlock(&mtxpoll)

	pd.user = uint32(len(pfds))
	pfds = append(pfds, pollfd{fd: int32(fd)})
	pds = append(pds, pd)
	unlock(&mtxset)
	return 0
}

func netpollclose(fd uintptr) int32 {
	lock(&mtxpoll)
	netpollwakeup()

	lock(&mtxset)
	unlock(&mtxpoll)

	for i := 0; i < len(pfds); i++ {
		if pfds[i].fd == int32(fd) {
			pfds[i] = pfds[len(pfds)-1]
			pfds = pfds[:len(pfds)-1]

			pds[i] = pds[len(pds)-1]
			pds[i].user = uint32(i)
			pds = pds[:len(pds)-1]
			break
		}
	}
	unlock(&mtxset)
	return 0
}

func netpollarm(pd *pollDesc, mode int) {
	lock(&mtxpoll)
	netpollwakeup()

	lock(&mtxset)
	unlock(&mtxpoll)

	switch mode {
	case 'r':
		pfds[pd.user].events |= _POLLIN
	case 'w':
		pfds[pd.user].events |= _POLLOUT
	}
	unlock(&mtxset)
}

// netpollBreak interrupts an epollwait.
func netpollBreak() {
	netpollwakeup()
}

// netpoll checks for ready network connections.
// Returns list of goroutines that become runnable.
// delay < 0: blocks indefinitely
// delay == 0: does not block, just polls
// delay > 0: block for up to that many nanoseconds
//go:nowritebarrierrec
func netpoll(delay int64) gList {
	var timeout uintptr
	if delay < 0 {
		timeout = ^uintptr(0)
	} else if delay == 0 {
		// TODO: call poll with timeout == 0
		return gList{}
	} else if delay < 1e6 {
		timeout = 1
	} else if delay < 1e15 {
		timeout = uintptr(delay / 1e6)
	} else {
		// An arbitrary cap on how long to wait for a timer.
		// 1e9 ms == ~11.5 days.
		timeout = 1e9
	}
retry:
	lock(&mtxpoll)
	lock(&mtxset)
	pendingUpdates = 0
	unlock(&mtxpoll)

	n, e := poll(&pfds[0], uintptr(len(pfds)), timeout)
	if n < 0 {
		if e != _EINTR {
			println("errno=", e, " len(pfds)=", len(pfds))
			throw("poll failed")
		}
		unlock(&mtxset)
		// If a timed sleep was interrupted, just return to
		// recalculate how long we should sleep now.
		if timeout > 0 {
			return gList{}
		}
		goto retry
	}
	// Check if some descriptors need to be changed
	if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
		if delay != 0 {
			// A netpollwakeup could be picked up by a
			// non-blocking poll. Only clear the wakeup
			// if blocking.
			var b [1]byte
			for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 {
			}
		}
		// Do not look at the other fds in this case as the mode may have changed
		// XXX only additions of flags are made, so maybe it is ok
		unlock(&mtxset)
		goto retry
	}
	var toRun gList
	for i := 0; i < len(pfds) && n > 0; i++ {
		pfd := &pfds[i]

		var mode int32
		if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
			mode += 'r'
			pfd.events &= ^_POLLIN
		}
		if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 {
			mode += 'w'
			pfd.events &= ^_POLLOUT
		}
		if mode != 0 {
			pds[i].everr = false
			if pfd.revents == _POLLERR {
				pds[i].everr = true
			}
			netpollready(&toRun, pds[i], mode)
			n--
		}
	}
	unlock(&mtxset)
	return toRun
}
<commit_msg>runtime: fix netpollBreak for AIX<commit_after>// Copyright 2018 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

import "unsafe"

// This is based on the former libgo/runtime/netpoll_select.c implementation
// except that it uses poll instead of select and is written in Go.
// It's also based on Solaris implementation for the arming mechanisms

//go:cgo_import_dynamic libc_poll poll "libc.a/shr_64.o"
//go:linkname libc_poll libc_poll

var libc_poll libFunc

//go:nosplit
func poll(pfds *pollfd, npfds uintptr, timeout uintptr) (int32, int32) {
	r, err := syscall3(&libc_poll, uintptr(unsafe.Pointer(pfds)), npfds, timeout)
	return int32(r), int32(err)
}

// pollfd represents the poll structure for AIX operating system.
type pollfd struct {
	fd      int32
	events  int16
	revents int16
}

const _POLLIN = 0x0001
const _POLLOUT = 0x0002
const _POLLHUP = 0x2000
const _POLLERR = 0x4000

var (
	pfds           []pollfd
	pds            []*pollDesc
	mtxpoll        mutex
	mtxset         mutex
	rdwake         int32
	wrwake         int32
	pendingUpdates int32
)

func netpollinit() {
	// Create the pipe we use to wakeup poll.
	r, w, errno := nonblockingPipe()
	if errno != 0 {
		throw("netpollinit: failed to create pipe")
	}
	rdwake = r
	wrwake = w

	// Pre-allocate array of pollfd structures for poll.
	pfds = make([]pollfd, 1, 128)

	// Poll the read side of the pipe.
	pfds[0].fd = rdwake
	pfds[0].events = _POLLIN

	pds = make([]*pollDesc, 1, 128)
	pds[0] = nil
}

func netpollIsPollDescriptor(fd uintptr) bool {
	return fd == uintptr(rdwake) || fd == uintptr(wrwake)
}

// netpollwakeup writes on wrwake to wakeup poll before any changes.
func netpollwakeup() {
	if pendingUpdates == 0 {
		pendingUpdates = 1
		b := [1]byte{0}
		write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1)
	}
}

func netpollopen(fd uintptr, pd *pollDesc) int32 {
	lock(&mtxpoll)
	netpollwakeup()

	lock(&mtxset)
	unlock(&mtxpoll)

	pd.user = uint32(len(pfds))
	pfds = append(pfds, pollfd{fd: int32(fd)})
	pds = append(pds, pd)
	unlock(&mtxset)
	return 0
}

func netpollclose(fd uintptr) int32 {
	lock(&mtxpoll)
	netpollwakeup()

	lock(&mtxset)
	unlock(&mtxpoll)

	for i := 0; i < len(pfds); i++ {
		if pfds[i].fd == int32(fd) {
			pfds[i] = pfds[len(pfds)-1]
			pfds = pfds[:len(pfds)-1]

			pds[i] = pds[len(pds)-1]
			pds[i].user = uint32(i)
			pds = pds[:len(pds)-1]
			break
		}
	}
	unlock(&mtxset)
	return 0
}

func netpollarm(pd *pollDesc, mode int) {
	lock(&mtxpoll)
	netpollwakeup()

	lock(&mtxset)
	unlock(&mtxpoll)

	switch mode {
	case 'r':
		pfds[pd.user].events |= _POLLIN
	case 'w':
		pfds[pd.user].events |= _POLLOUT
	}
	unlock(&mtxset)
}

// netpollBreak interrupts an epollwait.
func netpollBreak() {
	netpollwakeup()
}

// netpoll checks for ready network connections.
// Returns list of goroutines that become runnable.
// delay < 0: blocks indefinitely
// delay == 0: does not block, just polls
// delay > 0: block for up to that many nanoseconds
//go:nowritebarrierrec
func netpoll(delay int64) gList {
	var timeout uintptr
	if delay < 0 {
		timeout = ^uintptr(0)
	} else if delay == 0 {
		// TODO: call poll with timeout == 0
		return gList{}
	} else if delay < 1e6 {
		timeout = 1
	} else if delay < 1e15 {
		timeout = uintptr(delay / 1e6)
	} else {
		// An arbitrary cap on how long to wait for a timer.
		// 1e9 ms == ~11.5 days.
		timeout = 1e9
	}
retry:
	lock(&mtxpoll)
	lock(&mtxset)
	pendingUpdates = 0
	unlock(&mtxpoll)

	n, e := poll(&pfds[0], uintptr(len(pfds)), timeout)
	if n < 0 {
		if e != _EINTR {
			println("errno=", e, " len(pfds)=", len(pfds))
			throw("poll failed")
		}
		unlock(&mtxset)
		// If a timed sleep was interrupted, just return to
		// recalculate how long we should sleep now.
		if timeout > 0 {
			return gList{}
		}
		goto retry
	}
	// Check if some descriptors need to be changed
	if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
		if delay != 0 {
			// A netpollwakeup could be picked up by a
			// non-blocking poll. Only clear the wakeup
			// if blocking.
			var b [1]byte
			for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 {
			}
		}
		// Still look at the other fds even if the mode may have
		// changed, as netpollBreak might have been called.
		n--
	}
	var toRun gList
	for i := 1; i < len(pfds) && n > 0; i++ {
		pfd := &pfds[i]

		var mode int32
		if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
			mode += 'r'
			pfd.events &= ^_POLLIN
		}
		if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 {
			mode += 'w'
			pfd.events &= ^_POLLOUT
		}
		if mode != 0 {
			pds[i].everr = false
			if pfd.revents == _POLLERR {
				pds[i].everr = true
			}
			netpollready(&toRun, pds[i], mode)
			n--
		}
	}
	unlock(&mtxset)
	return toRun
}
<|endoftext|>