<commit_before>package jetpack

// FIXME: refactor references to Host.imagesDS

import "bytes"
import "crypto/sha512"
import "encoding/json"
import "fmt"
import "io"
import "io/ioutil"
import "os"
import "path"
import "path/filepath"
import "sort"
import "strings"
import "time"

import "code.google.com/p/go-uuid/uuid"
import "github.com/appc/spec/schema"
import "github.com/appc/spec/schema/types"
import "github.com/juju/errors"

import "../run"
import "../zfs"

type Image struct {
	Dataset  *zfs.Dataset         `json:"-"`
	Host     *Host                `json:"-"`
	Manifest schema.ImageManifest `json:"-"`

	UUID uuid.UUID `json:"-"`

	Hash      *types.Hash `json:",omitempty"`
	Origin    string
	Timestamp time.Time
}

func NewImage(ds *zfs.Dataset, h *Host) *Image {
	basename := path.Base(ds.Name)
	img := &Image{
		Dataset:  ds,
		Host:     h,
		UUID:     uuid.Parse(basename),
		Manifest: *schema.BlankImageManifest(),
	}
	if img.UUID == nil {
		panic(fmt.Sprintf("Invalid UUID: %#v", basename))
	}
	return img
}

func (img *Image) IsEmpty() bool {
	_, err := os.Stat(img.Dataset.Path("manifest"))
	return os.IsNotExist(err)
}

func (img *Image) Load() error {
	if img.IsEmpty() {
		return errors.New("Image is empty")
	}

	metadataJSON, err := ioutil.ReadFile(img.Dataset.Path("metadata"))
	if err != nil {
		return errors.Trace(err)
	}

	if err = json.Unmarshal(metadataJSON, img); err != nil {
		return errors.Trace(err)
	}

	if err := img.loadManifest(); err != nil {
		return errors.Trace(err)
	}

	return nil
}

func (img *Image) Seal() error {
	// Make sure that manifest exists and validates correctly
	if err := img.loadManifest(); err != nil {
		return errors.Trace(err)
	}

	if img.Hash == nil {
		amiPath := ""
		if img.Host.Properties.GetBool("images.ami.store", false) {
			amiPath = img.Dataset.Path("ami")
		}

		if hash, err := img.SaveAMI(amiPath, 0400); err != nil {
			return errors.Trace(err)
		} else {
			img.Hash = hash
		}
	}

	// Serialize metadata
	if metadataJSON, err := json.Marshal(img); err != nil {
		return errors.Trace(err)
	} else {
		if err := ioutil.WriteFile(img.Dataset.Path("metadata"), metadataJSON, 0400); err != nil {
			return errors.Trace(err)
		}
	}

	if err := os.Symlink(path.Base(img.Dataset.Name), img.Host.imagesDS.Path(img.Hash.String())); err != nil {
		return errors.Trace(err)
	}

	if _, err := img.Dataset.Snapshot("seal"); err != nil {
		return errors.Trace(err)
	}

	if err := img.Dataset.Zfs("set", "readonly=on"); err != nil {
		return errors.Trace(err)
	}

	return nil
}

// Save image to an AMI file, return its hash.
//
// If path is an empty string, don't save the image, just return the
// hash. If path is "-", print image to stdout.
func (img *Image) SaveAMI(path string, perm os.FileMode) (*types.Hash, error) {
	archiver := run.Command("tar", "-C", img.Dataset.Mountpoint, "-c", "-f", "-", "manifest", "rootfs")
	if archive, err := archiver.StdoutPipe(); err != nil {
		return nil, errors.Trace(err)
	} else {
		hash := sha512.New()
		faucet := io.TeeReader(archive, hash)
		sink := ioutil.Discard
		var compressor *run.Cmd = nil

		if path != "" {
			if path == "-" {
				sink = os.Stdout
			} else {
				if f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perm); err != nil {
					return nil, errors.Trace(err)
				} else {
					defer f.Close()
					sink = f
				}
			}

			if compression := img.Host.Properties.GetString("images.ami.compression", "no"); compression != "none" {
				switch compression {
				case "xz":
					compressor = run.Command("xz", "-z", "-c")
				case "bzip2":
					compressor = run.Command("bzip2", "-z", "-c")
				case "gz":
				case "gzip":
					compressor = run.Command("gzip", "-c")
				default:
					return nil, errors.Errorf("Invalid setting images.ami.compression=%#v (allowed values: xz, bzip2, gzip, none)", compression)
				}

				compressor.Cmd.Stdout = sink
				if cin, err := compressor.StdinPipe(); err != nil {
					return nil, errors.Trace(err)
				} else {
					sink = cin
				}
			}
		}

		if err := archiver.Start(); err != nil {
			return nil, errors.Trace(err)
		}

		if compressor != nil {
			if err := compressor.Start(); err != nil {
				archiver.Cmd.Process.Kill()
				return nil, errors.Trace(err)
			}
		}

		if _, err := io.Copy(sink, faucet); err != nil {
			return nil, errors.Trace(err)
		}

		if hash, err := types.NewHash(fmt.Sprintf("sha512-%x", hash.Sum(nil))); err != nil {
			// CAN'T HAPPEN, srsly
			return nil, errors.Trace(err)
		} else {
			return hash, nil
		}
	}
}

func (img *Image) loadManifest() error {
	manifestJSON, err := ioutil.ReadFile(img.Dataset.Path("manifest"))
	if err != nil {
		return errors.Trace(err)
	}

	if err = json.Unmarshal(manifestJSON, &img.Manifest); err != nil {
		return errors.Trace(err)
	}

	return nil
}

func (img *Image) Containers() (children ContainerSlice, _ error) {
	if containers, err := img.Host.Containers(); err != nil {
		return nil, errors.Trace(err)
	} else {
		snap := img.Dataset.SnapshotName("seal")
		for _, container := range containers {
			if container.Dataset.Origin == snap {
				children = append(children, container)
			}
		}
		return
	}
}

func (img *Image) Destroy() (err error) {
	err = errors.Trace(img.Dataset.Destroy("-r"))
	if img.Hash != nil {
		if err2 := os.Remove(img.Host.imagesDS.Path(img.Hash.String())); err2 != nil && err == nil {
			err = errors.Trace(err2)
		}
	}
	return
}

func (img *Image) Clone(snapshot, dest string) (*zfs.Dataset, error) {
	snap, err := img.Dataset.GetSnapshot(snapshot)
	if err != nil {
		return nil, errors.Trace(err)
	}

	ds, err := snap.Clone(dest)
	if err != nil {
		return nil, errors.Trace(err)
	}

	// FIXME: maybe not? (hint: multi-app containers)
	for _, filename := range []string{"manifest", "metadata"} {
		if err := os.Remove(ds.Path(filename)); err != nil && !os.IsNotExist(err) {
			return nil, errors.Trace(err)
		}
	}
	return ds, nil
}

func (img *Image) RuntimeApp() schema.RuntimeApp {
	app := schema.RuntimeApp{Name: img.Manifest.Name}
	app.Annotations.Set("jetpack/image-uuid", img.UUID.String())
	if img.Hash != nil {
		app.ImageID = *img.Hash
	} else {
		// TODO: do we really need to store ACI tarballs to have an image ID on built images?
		app.ImageID.Set(fmt.Sprintf(
			"sha512-000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000%032x",
			[]byte(img.UUID),
		))
	}
	return app
}

func (img *Image) GetApp() *types.App {
	if img.Manifest.App != nil {
		return img.Manifest.App
	} else {
		return ConsoleApp("root")
	}
}

func (img *Image) Build(buildDir string, addFiles []string, buildExec []string) (*Image, error) {
	var buildContainer *Container
	if c, err := img.Host.CloneContainer(img); err != nil {
		return nil, errors.Trace(err)
	} else {
		buildContainer = c
	}

	// This is needed by freebsd-update at least, should be okay to
	// allow this in builders.
	buildContainer.Manifest.Annotations.Set("jetpack/jail.conf/allow.chflags", "true")

	destroot := buildContainer.Dataset.Path("rootfs")

	workDir, err := ioutil.TempDir(destroot, ".jetpack.build.")
	if err != nil {
		return nil, errors.Trace(err)
	}

	if buildDir[len(buildDir)-1] != '/' {
		buildDir += "/"
	}

	cpArgs := []string{"-R", buildDir}
	if addFiles != nil {
		cpArgs = append(cpArgs, addFiles...)
	}
	cpArgs = append(cpArgs, workDir)

	if err := run.Command("cp", cpArgs...).Run(); err != nil {
		return nil, errors.Trace(err)
	}

	cWorkDir := filepath.Base(workDir)
	buildApp := &types.App{
		Exec: append([]string{
			"/bin/sh", "-c",
			fmt.Sprintf("cd '%s' && exec \"${@}\"", cWorkDir),
			"jetpack-build@" + cWorkDir,
		}, buildExec...),
	}

	if err := buildContainer.Run(buildApp); err != nil {
		return nil, errors.Trace(err)
	}

	if err := os.Rename(
		filepath.Join(workDir, "manifest.json"),
		buildContainer.Dataset.Path("build.manifest"),
	); err != nil {
		return nil, errors.Trace(err)
	}

	if err := os.RemoveAll(workDir); err != nil {
		return nil, errors.Trace(err)
	}

	if err := os.Remove(filepath.Join(destroot, "/etc/resolv.conf")); err != nil {
		return nil, errors.Trace(err)
	}

	// Pivot container into an image
	uuid := path.Base(buildContainer.Dataset.Name)
	if err := buildContainer.Dataset.Rename(img.Host.imagesDS.ChildName(uuid)); err != nil {
		return nil, errors.Trace(err)
	}

	ds, err := img.Host.imagesDS.GetDataset(uuid)
	if err != nil {
		return nil, errors.Trace(err)
	}

	// Clean up container's runtime stuff, leave only rootfs and new manifest
	if entries, err := ioutil.ReadDir(ds.Mountpoint); err != nil {
		return nil, errors.Trace(err)
	} else {
		for _, entry := range entries {
			filename := entry.Name()
			if filename == "rootfs" || filename == "build.manifest" {
				continue
			}
			if err := os.RemoveAll(ds.Path(filename)); err != nil {
				return nil, errors.Trace(err)
			}
		}
	}

	childImage := NewImage(ds, img.Host)

	// Construct the child image's manifest

	if manifestBytes, err := ioutil.ReadFile(childImage.Dataset.Path("build.manifest")); err != nil {
		return nil, errors.Trace(err)
	} else {
		if err := json.Unmarshal(manifestBytes, &childImage.Manifest); err != nil {
			return nil, errors.Trace(err)
		}
	}

	if _, ok := childImage.Manifest.Annotations.Get("timestamp"); !ok {
		if ts, err := time.Now().MarshalText(); err != nil {
			return nil, errors.Trace(err)
		} else {
			childImage.Manifest.Annotations.Set("timestamp", string(ts))
		}
	}

	for _, label := range []string{"os", "arch"} {
		if childValue, ok := childImage.Manifest.GetLabel(label); !ok {
			// if child has no os/arch, copy from parent
			if parentValue, ok := img.Manifest.GetLabel(label); ok {
				childImage.Manifest.Labels = append(childImage.Manifest.Labels,
					types.Label{Name: types.ACName(label), Value: parentValue})
			}
		} else if childValue == "" {
			// if child explicitly set to nil or empty string, remove the
			// label
			for i, l := range childImage.Manifest.Labels {
				if string(l.Name) == label {
					childImage.Manifest.Labels = append(
						childImage.Manifest.Labels[:i],
						childImage.Manifest.Labels[i+1:]...)
					break
				}
			}
		}
	}

	// TODO: should we merge app from parent image?

	childImage.Origin = img.UUID.String()

	if manifestBytes, err := json.Marshal(childImage.Manifest); err != nil {
		return nil, errors.Trace(err)
	} else {
		if err := ioutil.WriteFile(childImage.Dataset.Path("manifest"), manifestBytes, 0400); err != nil {
			return nil, errors.Trace(err)
		}
	}

	if err := os.Remove(childImage.Dataset.Path("build.manifest")); err != nil {
		return nil, errors.Trace(err)
	}

	childImage.Timestamp = time.Now()

	if err := childImage.Seal(); err != nil {
		return nil, errors.Trace(err)
	}

	return childImage, nil
}

// For sorting
type ImageSlice []*Image

func (ii ImageSlice) Len() int           { return len(ii) }
func (ii ImageSlice) Less(i, j int) bool { return bytes.Compare(ii[i].UUID, ii[j].UUID) < 0 }
func (ii ImageSlice) Swap(i, j int)      { ii[i], ii[j] = ii[j], ii[i] }

func (ii ImageSlice) Table() [][]string {
	rows := make([][]string, len(ii)+1)
	rows[0] = []string{"UUID", "NAME", "LABELS"}
	for i, img := range ii {
		labels := make([]string, len(img.Manifest.Labels))
		for j, l := range img.Manifest.Labels {
			labels[j] = fmt.Sprintf("%v=%#v", l.Name, l.Value)
		}
		sort.Strings(labels)
		rows[i+1] = append([]string{img.UUID.String(), string(img.Manifest.Name)}, strings.Join(labels, ","))
	}
	return rows
}
<commit_msg>:lipstick:<commit_after>package jetpack

// FIXME: refactor references to Host.imagesDS

import "bytes"
import "crypto/sha512"
import "encoding/json"
import "fmt"
import "io"
import "io/ioutil"
import "os"
import "path"
import "path/filepath"
import "sort"
import "strings"
import "time"

import "code.google.com/p/go-uuid/uuid"
import "github.com/appc/spec/schema"
import "github.com/appc/spec/schema/types"
import "github.com/juju/errors"

import "../run"
import "../zfs"

type Image struct {
	Dataset  *zfs.Dataset         `json:"-"`
	Host     *Host                `json:"-"`
	Manifest schema.ImageManifest `json:"-"`

	UUID uuid.UUID `json:"-"`

	Hash      *types.Hash `json:",omitempty"`
	Origin    string
	Timestamp time.Time
}

func NewImage(ds *zfs.Dataset, h *Host) *Image {
	basename := path.Base(ds.Name)
	img := &Image{
		Dataset:  ds,
		Host:     h,
		UUID:     uuid.Parse(basename),
		Manifest: *schema.BlankImageManifest(),
	}
	if img.UUID == nil {
		panic(fmt.Sprintf("Invalid UUID: %#v", basename))
	}
	return img
}

func (img *Image) IsEmpty() bool {
	_, err := os.Stat(img.Dataset.Path("manifest"))
	return os.IsNotExist(err)
}

func (img *Image) Load() error {
	if img.IsEmpty() {
		return errors.New("Image is empty")
	}

	metadataJSON, err := ioutil.ReadFile(img.Dataset.Path("metadata"))
	if err != nil {
		return errors.Trace(err)
	}

	if err = json.Unmarshal(metadataJSON, img); err != nil {
		return errors.Trace(err)
	}

	if err := img.loadManifest(); err != nil {
		return errors.Trace(err)
	}

	return nil
}

func (img *Image) Seal() error {
	// Make sure that manifest exists and validates correctly
	if err := img.loadManifest(); err != nil {
		return errors.Trace(err)
	}

	if img.Hash == nil {
		amiPath := ""
		if img.Host.Properties.GetBool("images.ami.store", false) {
			amiPath = img.Dataset.Path("ami")
		}

		if hash, err := img.SaveAMI(amiPath, 0400); err != nil {
			return errors.Trace(err)
		} else {
			img.Hash = hash
		}
	}

	// Serialize metadata
	if metadataJSON, err := json.Marshal(img); err != nil {
		return errors.Trace(err)
	} else {
		if err := ioutil.WriteFile(img.Dataset.Path("metadata"), metadataJSON, 0400); err != nil {
			return errors.Trace(err)
		}
	}

	if err := os.Symlink(path.Base(img.Dataset.Name), img.Host.imagesDS.Path(img.Hash.String())); err != nil {
		return errors.Trace(err)
	}

	if _, err := img.Dataset.Snapshot("seal"); err != nil {
		return errors.Trace(err)
	}

	if err := img.Dataset.Zfs("set", "readonly=on"); err != nil {
		return errors.Trace(err)
	}

	return nil
}

// Save image to an AMI file, return its hash.
//
// If path is an empty string, don't save the image, just return the
// hash. If path is "-", print image to stdout.
func (img *Image) SaveAMI(path string, perm os.FileMode) (*types.Hash, error) {
	archiver := run.Command("tar", "-C", img.Dataset.Mountpoint, "-c", "-f", "-", "manifest", "rootfs")
	if archive, err := archiver.StdoutPipe(); err != nil {
		return nil, errors.Trace(err)
	} else {
		hash := sha512.New()
		faucet := io.TeeReader(archive, hash)
		sink := ioutil.Discard
		var compressor *run.Cmd = nil

		if path != "" {
			if path == "-" {
				sink = os.Stdout
			} else {
				if f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perm); err != nil {
					return nil, errors.Trace(err)
				} else {
					defer f.Close()
					sink = f
				}
			}

			if compression := img.Host.Properties.GetString("images.ami.compression", "no"); compression != "none" {
				switch compression {
				case "xz":
					compressor = run.Command("xz", "-z", "-c")
				case "bzip2":
					compressor = run.Command("bzip2", "-z", "-c")
				case "gz":
				case "gzip":
					compressor = run.Command("gzip", "-c")
				default:
					return nil, errors.Errorf("Invalid setting images.ami.compression=%#v (allowed values: xz, bzip2, gzip, none)", compression)
				}

				compressor.Cmd.Stdout = sink
				if cin, err := compressor.StdinPipe(); err != nil {
					return nil, errors.Trace(err)
				} else {
					sink = cin
				}
			}
		}

		if err := archiver.Start(); err != nil {
			return nil, errors.Trace(err)
		}

		if compressor != nil {
			if err := compressor.Start(); err != nil {
				archiver.Cmd.Process.Kill()
				return nil, errors.Trace(err)
			}
		}

		if _, err := io.Copy(sink, faucet); err != nil {
			return nil, errors.Trace(err)
		}

		if hash, err := types.NewHash(fmt.Sprintf("sha512-%x", hash.Sum(nil))); err != nil {
			// CAN'T HAPPEN, srsly
			return nil, errors.Trace(err)
		} else {
			return hash, nil
		}
	}
}

func (img *Image) loadManifest() error {
	manifestJSON, err := ioutil.ReadFile(img.Dataset.Path("manifest"))
	if err != nil {
		return errors.Trace(err)
	}

	if err = json.Unmarshal(manifestJSON, &img.Manifest); err != nil {
		return errors.Trace(err)
	}

	return nil
}

func (img *Image) Containers() (children ContainerSlice, _ error) {
	if containers, err := img.Host.Containers(); err != nil {
		return nil, errors.Trace(err)
	} else {
		snap := img.Dataset.SnapshotName("seal")
		for _, container := range containers {
			if container.Dataset.Origin == snap {
				children = append(children, container)
			}
		}
		return
	}
}

func (img *Image) Destroy() (err error) {
	err = errors.Trace(img.Dataset.Destroy("-r"))
	if img.Hash != nil {
		if err2 := os.Remove(img.Host.imagesDS.Path(img.Hash.String())); err2 != nil && err == nil {
			err = errors.Trace(err2)
		}
	}
	return
}

func (img *Image) Clone(snapshot, dest string) (*zfs.Dataset, error) {
	snap, err := img.Dataset.GetSnapshot(snapshot)
	if err != nil {
		return nil, errors.Trace(err)
	}

	ds, err := snap.Clone(dest)
	if err != nil {
		return nil, errors.Trace(err)
	}

	// FIXME: maybe not? (hint: multi-app containers)
	for _, filename := range []string{"manifest", "metadata"} {
		if err := os.Remove(ds.Path(filename)); err != nil && !os.IsNotExist(err) {
			return nil, errors.Trace(err)
		}
	}
	return ds, nil
}

func (img *Image) RuntimeApp() schema.RuntimeApp {
	app := schema.RuntimeApp{Name: img.Manifest.Name}
	app.Annotations.Set("jetpack/image-uuid", img.UUID.String())
	if img.Hash != nil {
		app.ImageID = *img.Hash
	} else {
		// TODO: do we really need to store ACI tarballs to have an image ID on built images?
		app.ImageID.Set(fmt.Sprintf(
			"sha512-000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000%032x",
			[]byte(img.UUID),
		))
	}
	return app
}

func (img *Image) GetApp() *types.App {
	if img.Manifest.App != nil {
		return img.Manifest.App
	} else {
		return ConsoleApp("root")
	}
}

func (img *Image) Build(buildDir string, addFiles []string, buildExec []string) (*Image, error) {
	var buildContainer *Container
	if c, err := img.Host.CloneContainer(img); err != nil {
		return nil, errors.Trace(err)
	} else {
		buildContainer = c
	}

	// This is needed by freebsd-update at least, should be okay to
	// allow this in builders.
	buildContainer.Manifest.Annotations.Set("jetpack/jail.conf/allow.chflags", "true")

	destroot := buildContainer.Dataset.Path("rootfs")

	workDir, err := ioutil.TempDir(destroot, ".jetpack.build.")
	if err != nil {
		return nil, errors.Trace(err)
	}

	if buildDir[len(buildDir)-1] != '/' {
		buildDir += "/"
	}

	cpArgs := []string{"-R", buildDir}
	if addFiles != nil {
		cpArgs = append(cpArgs, addFiles...)
	}
	cpArgs = append(cpArgs, workDir)

	if err := run.Command("cp", cpArgs...).Run(); err != nil {
		return nil, errors.Trace(err)
	}

	cWorkDir := filepath.Base(workDir)
	buildApp := &types.App{
		Exec: append([]string{
			"/bin/sh", "-c",
			fmt.Sprintf("cd '%s' && exec \"${@}\"", cWorkDir),
			"jetpack-build@" + cWorkDir,
		}, buildExec...),
	}

	if err := buildContainer.Run(buildApp); err != nil {
		return nil, errors.Trace(err)
	}

	if err := os.Rename(
		filepath.Join(workDir, "manifest.json"),
		buildContainer.Dataset.Path("build.manifest"),
	); err != nil {
		return nil, errors.Trace(err)
	}

	if err := os.RemoveAll(workDir); err != nil {
		return nil, errors.Trace(err)
	}

	if err := os.Remove(filepath.Join(destroot, "/etc/resolv.conf")); err != nil {
		return nil, errors.Trace(err)
	}

	// Pivot container into an image
	uuid := path.Base(buildContainer.Dataset.Name)
	if err := buildContainer.Dataset.Rename(img.Host.imagesDS.ChildName(uuid)); err != nil {
		return nil, errors.Trace(err)
	}

	ds, err := img.Host.imagesDS.GetDataset(uuid)
	if err != nil {
		return nil, errors.Trace(err)
	}

	// Clean up container's runtime stuff, leave only rootfs and new manifest
	if entries, err := ioutil.ReadDir(ds.Mountpoint); err != nil {
		return nil, errors.Trace(err)
	} else {
		for _, entry := range entries {
			filename := entry.Name()
			if filename == "rootfs" || filename == "build.manifest" {
				continue
			}
			if err := os.RemoveAll(ds.Path(filename)); err != nil {
				return nil, errors.Trace(err)
			}
		}
	}

	childImage := NewImage(ds, img.Host)

	// Construct the child image's manifest

	if manifestBytes, err := ioutil.ReadFile(childImage.Dataset.Path("build.manifest")); err != nil {
		return nil, errors.Trace(err)
	} else {
		if err := json.Unmarshal(manifestBytes, &childImage.Manifest); err != nil {
			return nil, errors.Trace(err)
		}
	}

	if _, ok := childImage.Manifest.Annotations.Get("timestamp"); !ok {
		if ts, err := time.Now().MarshalText(); err != nil {
			return nil, errors.Trace(err)
		} else {
			childImage.Manifest.Annotations.Set("timestamp", string(ts))
		}
	}

	for _, label := range []string{"os", "arch"} {
		if childValue, ok := childImage.Manifest.GetLabel(label); !ok {
			// if child has no os/arch, copy from parent
			if parentValue, ok := img.Manifest.GetLabel(label); ok {
				childImage.Manifest.Labels = append(childImage.Manifest.Labels,
					types.Label{Name: types.ACName(label), Value: parentValue})
			}
		} else if childValue == "" {
			// if child explicitly set to nil or empty string, remove the
			// label
			for i, l := range childImage.Manifest.Labels {
				if string(l.Name) == label {
					childImage.Manifest.Labels = append(
						childImage.Manifest.Labels[:i],
						childImage.Manifest.Labels[i+1:]...)
					break
				}
			}
		}
	}

	// TODO: should we merge app from parent image?

	childImage.Origin = img.UUID.String()

	if manifestBytes, err := json.Marshal(childImage.Manifest); err != nil {
		return nil, errors.Trace(err)
	} else {
		if err := ioutil.WriteFile(childImage.Dataset.Path("manifest"), manifestBytes, 0400); err != nil {
			return nil, errors.Trace(err)
		}
	}

	if err := os.Remove(childImage.Dataset.Path("build.manifest")); err != nil {
		return nil, errors.Trace(err)
	}

	childImage.Timestamp = time.Now()

	if err := childImage.Seal(); err != nil {
		return nil, errors.Trace(err)
	}

	return childImage, nil
}

// For sorting
type ImageSlice []*Image

func (imgs ImageSlice) Len() int           { return len(imgs) }
func (imgs ImageSlice) Less(i, j int) bool { return bytes.Compare(imgs[i].UUID, imgs[j].UUID) < 0 }
func (imgs ImageSlice) Swap(i, j int)      { imgs[i], imgs[j] = imgs[j], imgs[i] }

func (imgs ImageSlice) Table() [][]string {
	rows := make([][]string, len(imgs)+1)
	rows[0] = []string{"UUID", "NAME", "LABELS"}
	for i, img := range imgs {
		labels := make([]string, len(img.Manifest.Labels))
		for j, l := range img.Manifest.Labels {
			labels[j] = fmt.Sprintf("%v=%#v", l.Name, l.Value)
		}
		sort.Strings(labels)
		rows[i+1] = append([]string{img.UUID.String(), string(img.Manifest.Name)}, strings.Join(labels, ","))
	}
	return rows
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Niklas Wolber
// This file is licensed under the MIT license.
// See the LICENSE file for more information.

package job

import (
	"context"
	"fmt"
	"io"
	"log"
	"runtime"
	"strings"
	"time"

	"github.com/fatih/color"
	"github.com/nwolber/xCUTEr/flunc"
)

// TelemetryInfo holds informations about an instrumented job
type TelemetryInfo struct {
	updateTrigger chan NodeEvent
	root          node
	events        []NodeEvent
}

// Events returns a list of event that occured during execution
func (t *TelemetryInfo) Events() NodeEvents {
	return t.events
}

// GetFlunc executes the instrumented job, the channel must be serviced, otherwise execution hangs.
func (t *TelemetryInfo) GetFlunc() (flunc.Flunc, <-chan string) {
	update := make(chan string)
	go func(n node, updateTrigger <-chan NodeEvent, update chan<- string) {
		for {
			event, ok := <-updateTrigger
			if !ok {
				close(update)
				return
			}

			t.events = append(t.events, event)

			update <- n.String(nil)
		}
	}(t.root, t.updateTrigger, update)

	f := func(ctx context.Context) (context.Context, error) {
		t.root.Status(NotStartedNode, "")
		t.events = make([]NodeEvent, 0, len(t.events))
		newCtx, err := t.root.Exec()(ctx)
		close(t.updateTrigger)
		return newCtx, err
	}

	return f, update
}

// Instrument the given job.
func Instrument(c *Config) (*TelemetryInfo, error) {
	t := newTelemtryBuilder()
	ret, err := VisitConfig(t, c)
	if err != nil {
		return nil, err
	}

	return &TelemetryInfo{
		updateTrigger: t.u,
		root:          ret.(node),
	}, nil
}

// NodeStatus describes the status of a node.
type NodeStatus int

const (
	// Execution didn't start yet.
	NotStartedNode NodeStatus = iota
	// Node is currently executed.
	RunningNode
	// Execution completed.
	CompletedNode
	// Execution failed.
	FailedNode
)

// NodeEvent describes a event during execution
type NodeEvent struct {
	Timestamp time.Time
	Status    NodeStatus
	Typ, Text string
}

// A EventFilter returns wether the e matches the filter.
type EventFilter func(e NodeEvent) bool

// NodeEvents is a list of events
type NodeEvents []NodeEvent

// Filter returns all NodeEvents matching f.
func (e *NodeEvents) Filter(f EventFilter) NodeEvents {
	var arr NodeEvents
	for _, event := range *e {
		if f(event) {
			arr = append(arr, event)
		}
	}
	return arr
}

// FilterType returns all NodeEvents with type typ.
func (e *NodeEvents) FilterType(typ string) NodeEvents {
	return e.Filter(func(e NodeEvent) bool {
		return e.Typ == typ
	})
}

// FilterStatus returns all NodeEvents with status status.
func (e *NodeEvents) FilterStatus(status NodeStatus) NodeEvents {
	return e.Filter(func(e NodeEvent) bool {
		return e.Status == status
	})
}

type node interface {
	Typ() string
	Status(status NodeStatus, text string)
	Exec() flunc.Flunc
	Log(t time.Time, file string, line int, message string)
	Stringer
}

// A LogMessage .
type LogMessage struct {
	Timestamp     time.Time
	Line          int
	File, Message string
}

type simpleNode struct {
	typ    string
	exec   flunc.Flunc
	str    Stringer
	status NodeStatus
	text   string
	log    []*LogMessage
	v      *Vars
}

func (n *simpleNode) Typ() string {
	return n.typ
}

func (n *simpleNode) Status(status NodeStatus, text string) {
	n.status = status
	n.text = text
}

func (n *simpleNode) Exec() flunc.Flunc {
	return n.exec
}

func (n *simpleNode) Log(t time.Time, file string, line int, message string) {
	n.log = append(n.log, &LogMessage{
		Timestamp: t,
		File:      file,
		Line:      line,
		Message:   message,
	})
}

var (
	yellow = color.New(color.FgYellow).SprintFunc()
	green  = color.New(color.FgGreen).SprintFunc()
	red    = color.New(color.FgRed).SprintFunc()
)

func formatString(str string, status NodeStatus, text string) string {
	var color func(a ...interface{}) string
	switch status {
	case RunningNode:
		str = "➤ " + str
		color = yellow
	case CompletedNode:
		str = "✔ " + str
		color = green
	case FailedNode:
		str = "✘ " + str
		color = red
	}

	if text != "" {
		str += ": " + text
	}

	if color != nil {
		index := strings.Index(str, "\n")
		if index >= 0 {
			str = color(str[:index]) + str[index:]
		} else {
			str = color(str)
		}
	}

	return str
}

func (n *simpleNode) String(v *Vars) string {
	str := ""
	if n.v != nil {
		str = n.str.String(n.v)
	} else {
		str = n.str.String(v)
	}

	text := n.text
	if text == "" && len(n.log) > 0 {
		text = "\n"
		for _, msg := range n.log {
			text += fmt.Sprintf("%s %s:%d %s\n", msg.Timestamp, msg.File, msg.Line, msg.Message)
		}
	}

	return formatString(str, n.status, text)
}

// Logger is an interface for loggers. It is satisfied by log.Logger.
type Logger interface {
	SetOutput(w io.Writer)
	Flags() int
	SetFlags(flag int)
	Prefix() string
	SetPrefix(prefix string)
	Print(v ...interface{})
	Printf(format string, v ...interface{})
	Println(v ...interface{})
	Fatal(v ...interface{})
	Fatalf(format string, v ...interface{})
	Fatalln(v ...interface{})
	Panic(v ...interface{})
	Panicf(format string, v ...interface{})
	Panicln(v ...interface{})
	Output(calldepth int, s string) error
}

type interceptLogger struct {
	l, orig Logger
	log     func(t time.Time, file string, line int, message string)
}

func (l *interceptLogger) Log(t time.Time, message string) {
	_, file, line, ok := runtime.Caller(2)
	if !ok {
		file = "???"
	}

	if l := len(message); message[l-1] == '\n' {
		message = message[:l-1]
	}

	l.log(t, file, line, message)
}

func (l *interceptLogger) SetOutput(w io.Writer) {
	if l.orig != nil {
		l.orig.SetOutput(w)
	}
}

func (l *interceptLogger) Flags() int {
	if l.orig == nil {
		return 0
	}
	return l.orig.Flags()
}

func (l *interceptLogger) SetFlags(flag int) {
	if l.orig != nil {
		l.orig.SetFlags(flag)
	}
}

func (l *interceptLogger) Prefix() string {
	if l.orig == nil {
		return ""
	}
	return l.orig.Prefix()
}

func (l *interceptLogger) SetPrefix(prefix string) {
	if l.orig != nil {
		l.orig.SetPrefix(prefix)
	}
}

func (l *interceptLogger) Print(v ...interface{}) {
	l.Log(time.Now(), fmt.Sprintln(v...))
	if l.orig != nil {
		l.orig.Print(v...)
	}
}

func (l *interceptLogger) Printf(format string, v ...interface{}) {
	l.Log(time.Now(), fmt.Sprintf(format, v...))
	if l.orig != nil {
		l.orig.Printf(format, v...)
	}
}

func (l *interceptLogger) Println(v ...interface{}) {
	l.Log(time.Now(), fmt.Sprintln(v...))
	if l.orig != nil {
		l.orig.Println(v...)
	}
}

func (l *interceptLogger) Fatal(v ...interface{}) {
	l.Log(time.Now(), fmt.Sprintln(v...))
	if l.orig != nil {
		l.orig.Fatal(v...)
	}
}

func (l *interceptLogger) Fatalf(format string, v ...interface{}) {
	l.Log(time.Now(), fmt.Sprintf(format, v...))
	if l.orig != nil {
		l.orig.Fatalf(format, v...)
	}
}

func (l *interceptLogger) Fatalln(v ...interface{}) {
	l.Log(time.Now(), fmt.Sprintln(v...))
	if l.orig != nil {
		l.orig.Fatalln(v...)
	}
}

func (l *interceptLogger) Panic(v ...interface{}) {
	l.Log(time.Now(), fmt.Sprintln(v...))
	if l.orig != nil {
		l.orig.Panic(v...)
	}
}

func (l *interceptLogger) Panicf(format string, v ...interface{}) {
	l.Log(time.Now(), fmt.Sprintf(format, v...))
	if l.orig != nil {
		l.orig.Panicf(format, v...)
	}
}

func (l *interceptLogger) Panicln(v ...interface{}) {
	l.Log(time.Now(), fmt.Sprintln(v...))
	if l.orig != nil {
		l.orig.Panicln(v...)
	}
}

func (l *interceptLogger) Output(calldepth int, s string) error {
	if l.orig == nil {
		return nil
	}
	return l.orig.Output(calldepth, s)
}

type interceptContext struct {
	context.Context
}

func (ctx *interceptContext) Deadline() (deadline time.Time, ok bool) { return ctx.Context.Deadline() }
func (ctx *interceptContext) Done() <-chan struct{}                   { return ctx.Context.Done() }
func (ctx *interceptContext) Err() error                              { return ctx.Context.Err() }
func (ctx *interceptContext) Value(key interface{}) interface{}       { return ctx.Context.Value(key) }

func instrument(update func(NodeStatus, string, string), n node, f flunc.Flunc) flunc.Flunc {
	return flunc.MakeFlunc(func(origCtx context.Context) (context.Context, error) {
		tt, _ := origCtx.Value(templatingKey).(*TemplatingEngine)
		if tt != nil {
			switch nn := n.(type) {
			case *simpleNode:
				nn.v = &Vars{tt: tt}
			case *nodeGroup:
				nn.v = &Vars{tt: tt}
			}
		}

		origLogger, ok := origCtx.Value(LoggerKey).(Logger)
		for ok {
			var l *interceptLogger
			l, ok = origLogger.(*interceptLogger)
			if !ok {
				break
			}

			origLogger = l.orig
		}

		ctx := context.WithValue(origCtx, LoggerKey, &interceptLogger{orig: origLogger, log: n.Log})
		interceptCtx := &interceptContext{Context: ctx}

		n.Status(RunningNode, "")
		update(RunningNode, n.Typ(), "")

		newCtx, err := f(interceptCtx)

		if err == nil {
			n.Status(CompletedNode, "")
			update(CompletedNode, n.Typ(), "")
		} else {
			n.Status(FailedNode, err.Error())
			update(FailedNode, n.Typ(), err.Error())
		}

		// The interceptContext is now the parent of all modifications to the context
		// made by f. By retargeting the incerceptContext at the original context,
		// we effectivly remove the interceptLogger from the context
		interceptCtx.Context = origCtx

		return newCtx, err
	})
}

type stringerGroup interface {
	Group
	Stringer
}

type nodeGroup struct {
	typ      string
	children []node
	status   NodeStatus
	text     string
	exec     *executionGroup
	str      stringerGroup
	update   func(NodeStatus, string, string)
	v        *Vars
}

func (g *nodeGroup) Append(children ...interface{}) {
	for _, child := range children {
		if child == nil {
			continue
		}

		n, ok := child.(node)
		if !ok {
			log.Panicf("not a node %T", child)
		}

		g.exec.Append(n.Exec())
		g.str.Append(n.(Stringer))
		g.children = append(g.children, n)
	}
}

func (g *nodeGroup) Wrap() interface{} {
	return g
}

func (g *nodeGroup) Typ() string {
	return g.typ
}

func (g *nodeGroup) Status(status NodeStatus, text string) {
	g.status = status
	g.text = text

	if status == NotStartedNode {
		for _, child := range g.children {
			child.Status(status, text)
		}
	}
}

func (*nodeGroup) Log(t time.Time, file string, line int, message string) {}

func (g *nodeGroup) Exec() flunc.Flunc {
	return instrument(g.update, g, g.exec.Wrap().(flunc.Flunc))
}

func (g *nodeGroup) String(v *Vars) string {
	return formatString(g.str.String(g.v), g.status, g.text)
}

type telemetryBuilder struct {
	u    chan NodeEvent
	str  *StringBuilder
	exec ExecutionTreeBuilder
}

func newTelemtryBuilder() *telemetryBuilder {
	return &telemetryBuilder{
		u: make(chan NodeEvent),
		str: &StringBuilder{
			Full: true,
		},
	}
}

func (t *telemetryBuilder) update(status NodeStatus, typ, text string) {
	t.u <- NodeEvent{
		Timestamp: time.Now(),
		Status:    status,
		Typ:       typ,
		Text:      text,
	}
}

func (t *telemetryBuilder) Sequential() Group {
	var g nodeGroup
	g.typ = "Sequential"
	g.update = t.update
	g.exec = t.exec.Sequential().(*executionGroup)
	g.str = t.str.Sequential().(*SimpleBranch)
	return &g
}

func (t *telemetryBuilder) Parallel() Group {
	var g nodeGroup
	g.typ = "Parallel"
	g.update = t.update
	g.exec = t.exec.Parallel().(*executionGroup)
	g.str = t.str.Parallel().(*SimpleBranch)
	return &g
}

func (t *telemetryBuilder) Job(name string) Group {
	var g nodeGroup
	g.update = t.update
	g.exec = t.exec.Job(name).(*executionGroup)
	str := t.str.Job(name).(*SimpleBranch)
	g.str = str
	g.typ = string(str.Root)
	return &g
}

func (t *telemetryBuilder) Output(o *Output) interface{} {
	if o == nil {
		return nil
	}

	var n simpleNode
	n.typ = "Output"
	n.str = t.str.Output(o).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.Output(o).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) JobLogger(jobName string) interface{} {
	var n simpleNode
	n.typ = fmt.Sprintf("Job %s", jobName)
	n.str = t.str.JobLogger(jobName).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.JobLogger(jobName).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) HostLogger(jobName string, h *Host) interface{} {
	var n simpleNode
	n.typ = "Host logger"
	n.str = t.str.HostLogger(jobName, h).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.HostLogger(jobName, h).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) Timeout(timeout time.Duration) interface{} {
	var n simpleNode
	n.typ = "Timeout"
	n.str = t.str.Timeout(timeout).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.Timeout(timeout).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) SCP(scp *ScpData) interface{} {
	var n simpleNode
	n.typ = "SCP"
	n.str = t.str.SCP(scp).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.SCP(scp).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) Hosts() Group {
	var g nodeGroup
	g.typ = "Target hosts"
	g.update = t.update
	g.exec = t.exec.Hosts().(*executionGroup)
	g.str = t.str.Hosts().(*SimpleBranch)
	return &g
}

func (t *telemetryBuilder) Host(c *Config, h *Host) Group {
	var g nodeGroup
	g.update = t.update
	g.exec = t.exec.Host(c, h).(*executionGroup)
	str := t.str.Host(c, h).(*partHost)
	g.str = str
	g.typ = string(str.Root)
	return &g
}

func (t *telemetryBuilder) ErrorSafeguard(child interface{}) interface{} {
	var n simpleNode
	n.typ = "Error Safeguard"
	n.str = t.str.ErrorSafeguard(child).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.ErrorSafeguard(child.(node).Exec()).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) ContextBounds(child interface{}) interface{} {
	var n simpleNode
	n.typ = "Context Bounds"
	n.str = t.str.ContextBounds(child).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.ContextBounds(child.(node).Exec()).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) Retry(child interface{}, retries uint) interface{} {
	var n simpleNode
	n.typ = "Retry"
	n.str = t.str.Retry(child, retries).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.Retry(child.(node).Exec(), retries).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) Templating(c *Config, h *Host) interface{} {
	var n simpleNode
	n.typ = "Templating"
	n.str = t.str.Templating(c, h).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.Templating(c, h).(flunc.Flunc))

	return &n
}

func (t *telemetryBuilder) SSHClient(host, user, keyFile, password string, keyboardInteractive map[string]string) interface{} {
	var n simpleNode
	n.typ = fmt.Sprintf("SSH client %s@%s", user, host)
	n.str = t.str.SSHClient(host, user, keyFile, password, keyboardInteractive).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.SSHClient(host, user, keyFile, password, keyboardInteractive).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) Forwarding(f *Forwarding) interface{} {
	var n simpleNode
	n.typ = fmt.Sprintf("Forward %s:%d to %s:%d", f.RemoteHost, f.RemotePort, f.LocalHost, f.LocalPort)
	n.str = t.str.Forwarding(f).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.Forwarding(f).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) Tunnel(f *Forwarding) interface{} {
	var n simpleNode
	n.typ = fmt.Sprintf("Tunnel %s:%d to %s:%d", f.LocalHost, f.LocalPort, f.RemoteHost, f.RemotePort)
	n.str = t.str.Tunnel(f).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.Tunnel(f).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) Commands(cmd *Command) Group {
	var g nodeGroup
	g.typ = "Command"
	g.update = t.update
	g.exec = t.exec.Commands(cmd).(*executionGroup)
	g.str = t.str.Commands(cmd).(*SimpleBranch)
	return &g
}

func (t *telemetryBuilder) Command(cmd *Command) interface{} {
	var n simpleNode
	n.typ = fmt.Sprintf("Command %q", cmd.Command)
	n.str = t.str.Command(cmd).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.Command(cmd).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) LocalCommand(cmd *Command) interface{} {
	var n simpleNode
	n.typ = fmt.Sprintf("Local Command %q", cmd.Command)
	n.str = t.str.LocalCommand(cmd).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.LocalCommand(cmd).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) Stdout(o *Output) interface{} {
	var n simpleNode
	n.typ = "Stdout"
	n.str = t.str.Stdout(o).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.Stdout(o).(flunc.Flunc))
	return &n
}

func (t *telemetryBuilder) Stderr(o *Output) interface{} {
	var n simpleNode
	n.typ = "Stderr"
	n.str = t.str.Stderr(o).(Stringer)
	n.exec = instrument(t.update, &n, t.exec.Stderr(o).(flunc.Flunc))
	return &n
}
<commit_msg>Remove old telemetry code<commit_after><|endoftext|>


<commit_before>package operator

import (
	"os"
	"strconv"

	"github.com/bahadley/esp/log"
)

const (
	defaultLength     = 4
	defaultTriggerQty = 2

	envWinLen  = "ESP_WINDOW_LENGTH"
	envWinTrig = "ESP_WINDOW_TRIGGER"
)

var (
	window    []*SensorTuple
	count     int
	trigger   int
	avgFactor float64
)

func WindowInsert(msg string) ([]byte, bool) {
	var rslt []byte
    var st, tmp *SensorTuple

    st = new(SensorTuple)
	err := Unmarshal(msg, st)
	if err != nil {
		return rslt, false
	}

	for idx, val := range window {
		if idx == 0 {
			window[idx] = st
		} else {
			window[idx] = tmp
		}
		tmp = val
	}

	count++
	if count == trigger {
		count = 0
		avg := (window[0].Data + window[1].Data) / avgFactor
		rslt, err = Marshal(st.Sensor, avg)
		return rslt, true
	}
	return rslt, false
}

func init() {
	var err error
	var winlen int

	envVal := os.Getenv(envWinLen)
	if len(envVal) == 0 {
		winlen = defaultLength
	} else {
		winlen, err = strconv.Atoi(envVal)
		if err != nil {
			log.Error.Fatalf("Invalid environment variable: %s", envWinLen)
		}
	}

	envVal = os.Getenv(envWinTrig)
	if len(envVal) == 0 {
		trigger = defaultTriggerQty
	} else {
		trigger, err = strconv.Atoi(envVal)
		if err != nil {
			log.Error.Fatalf("Invalid environment variable: %s", envWinTrig)
		}
	}

	window = make([]*SensorTuple, winlen)
	avgFactor = float64(trigger)
}
<commit_msg>Added average()<commit_after>package operator

import (
	"os"
	"strconv"

	"github.com/bahadley/esp/log"
)

const (
	defaultLength     = 4
	defaultTriggerQty = 2

	envWinLen  = "ESP_WINDOW_LENGTH"
	envWinTrig = "ESP_WINDOW_TRIGGER"
)

var (
	window  []*SensorTuple
	count   int
	trigger int
)

func WindowInsert(msg string) ([]byte, bool) {
	var rslt []byte
	var newTuple, tmp *SensorTuple

	newTuple = new(SensorTuple)
	err := Unmarshal(msg, newTuple)
	if err != nil {
		return rslt, false
	}

	for idx, st := range window {
		if idx == 0 {
			window[idx] = newTuple
		} else {
			window[idx] = tmp
		}
		tmp = st
	}

	count++
	if count == trigger {
		count = 0
		rslt, err = Marshal(newTuple.Sensor, average())
		return rslt, true
	}
	return rslt, false
}

func average() float64 {
	sum := 0.0
	for _, st := range window[0:trigger] {
		sum += st.Data
	}
	return sum / float64(trigger)
}

func init() {
	var err error
	var winlen int

	envVal := os.Getenv(envWinLen)
	if len(envVal) == 0 {
		winlen = defaultLength
	} else {
		winlen, err = strconv.Atoi(envVal)
		if err != nil {
			log.Error.Fatalf("Invalid environment variable: %s", envWinLen)
		}
	}

	envVal = os.Getenv(envWinTrig)
	if len(envVal) == 0 {
		trigger = defaultTriggerQty
	} else {
		trigger, err = strconv.Atoi(envVal)
		if err != nil {
			log.Error.Fatalf("Invalid environment variable: %s", envWinTrig)
		}
	}

	window = make([]*SensorTuple, winlen)
}
<|endoftext|>


<commit_before>package operator

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"
	"time"

	"github.com/bahadley/esp/log"
)

const (
	defaultAddr     = "localhost"
	defaultSinkAddr = "localhost"
	defaultSinkPort = "22220"

	envsaddr    = "ESP_ADDR"
	envsinkaddr = "ESP_SINK_ADDR"
	envsinkport = "ESP_SINK_PORT"

	chanbufsz = 10
)

var (
	dstAddr *net.UDPAddr
	srcAddr *net.UDPAddr
)

type (
	gMsg struct {
		Sensor    string    `json:"sensor"`
		Type      string    `json:"type"`
		Timestamp time.Time `json:"ts"`
		Data      float64   `json:"data"`
	}
)

func Window(ingest chan string) {
	conn, err := net.DialUDP("udp", srcAddr, dstAddr)
	if err != nil {
		log.Logoutput(log.ErrPrefix, err.Error())
	}
	defer conn.Close()

	for {
		msg := <-ingest
		var gm gMsg
		err := json.NewDecoder(strings.NewReader(msg)).Decode(&gm)
		if err != nil {
			log.Logoutput(log.ErrPrefix, err.Error())
			continue
		}

		log.Logoutput(log.InfoPrefix,
			fmt.Sprintf("Windowed: %s,%s,%s,%f", gm.Sensor,
				gm.Type, gm.Timestamp, gm.Data))

		buf := []byte(gm.Sensor)
		_, err = conn.Write(buf)
		if err != nil {
			log.Logoutput(log.ErrPrefix, err.Error())
			continue
		}
	}
}

func init() {
	addr := os.Getenv(envsaddr)
	if len(addr) == 0 {
		addr = defaultAddr
	}

	sinkAddr := os.Getenv(envsinkaddr)
	if len(sinkAddr) == 0 {
		sinkAddr = defaultSinkAddr
	}

	sinkPort := os.Getenv(envsinkport)
	if len(sinkPort) == 0 {
		sinkPort = defaultSinkPort
	}

	var err error
	dstAddr, err = net.ResolveUDPAddr("udp", sinkAddr+":"+sinkPort)
	if err != nil {
		log.Logfatalerror(err)
	}

	srcAddr, err = net.ResolveUDPAddr("udp", addr+":0")
	if err != nil {
		log.Logfatalerror(err)
	}
}
<commit_msg>Now using json.Unmarshal()<commit_after>package operator

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"time"

	"github.com/bahadley/esp/log"
)

const (
	defaultAddr     = "localhost"
	defaultSinkAddr = "localhost"
	defaultSinkPort = "22220"

	envsaddr    = "ESP_ADDR"
	envsinkaddr = "ESP_SINK_ADDR"
	envsinkport = "ESP_SINK_PORT"

	chanbufsz = 10
)

var (
	dstAddr *net.UDPAddr
	srcAddr *net.UDPAddr
)

type (
	gMsg struct {
		Sensor    string    `json:"sensor"`
		Type      string    `json:"type"`
		Timestamp time.Time `json:"ts"`
		Data      float64   `json:"data"`
	}
)

func Window(ingest chan string) {
	conn, err := net.DialUDP("udp", srcAddr, dstAddr)
	if err != nil {
		log.Logoutput(log.ErrPrefix, err.Error())
	}
	defer conn.Close()

	var gm gMsg
	for {
		msg := <-ingest
        err := json.Unmarshal([]byte(msg), &gm)
		if err != nil {
			log.Logoutput(log.ErrPrefix, err.Error())
			continue
		}

		log.Logoutput(log.InfoPrefix,
			fmt.Sprintf("Windowed: %s,%s,%s,%f", gm.Sensor,
				gm.Type, gm.Timestamp, gm.Data))

		buf := []byte(gm.Sensor)
		_, err = conn.Write(buf)
		if err != nil {
			log.Logoutput(log.ErrPrefix, err.Error())
			continue
		}
	}
}

func init() {
	addr := os.Getenv(envsaddr)
	if len(addr) == 0 {
		addr = defaultAddr
	}

	sinkAddr := os.Getenv(envsinkaddr)
	if len(sinkAddr) == 0 {
		sinkAddr = defaultSinkAddr
	}

	sinkPort := os.Getenv(envsinkport)
	if len(sinkPort) == 0 {
		sinkPort = defaultSinkPort
	}

	var err error
	dstAddr, err = net.ResolveUDPAddr("udp", sinkAddr+":"+sinkPort)
	if err != nil {
		log.Logfatalerror(err)
	}

	srcAddr, err = net.ResolveUDPAddr("udp", addr+":0")
	if err != nil {
		log.Logfatalerror(err)
	}
}
<|endoftext|>


<commit_before>package options

import (
	"bufio"
	"flag"
	"fmt"
	"os"
)

// Options parsed at the command line
type Options struct {
	list      *bool
	Filepaths []string
}

// Parse arguments and flags and returns options configuration struct
func Parse() (opts Options) {
	opts.list = flag.Bool("list", false, "only list URIs found in files (i.e. no status check)")

	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage of URIstat: uristat [options] files...")
		fmt.Fprintln(os.Stderr, "Options:")
		flag.PrintDefaults()
	}

	flag.Parse()

	opts.populateFilepaths()

	return opts
}

func (opts *Options) populateFilepaths() {
	if flag.Parsed() {
		opts.Filepaths = flag.Args()
	} else {
		opts.Filepaths = os.Args[1:]
	}

	if len(opts.Filepaths) == 0 {
		inStat, _ := os.Stdin.Stat()

		if (inStat.Mode() & os.ModeCharDevice) == 0 {
			stdinScanner := bufio.NewScanner(os.Stdin)

			for stdinScanner.Scan() {
				opts.Filepaths = append(opts.Filepaths, stdinScanner.Text())
			}
		}
	}
}

// IsValid returns whether Options is valid
func (opts Options) IsValid() bool {
	if len(opts.Filepaths) == 0 {
		return false
	}

	return true
}

// PrintError prints reason Options was invalid and usage info to stderr
func (opts Options) PrintError() {
	if len(opts.Filepaths) == 0 {
		fmt.Fprintf(os.Stderr, "No files to scan\n")
	}

	fmt.Fprintln(os.Stderr, "")
	flag.Usage()
}

// ListOnly returns bool indicating if the found URIs should be printed
func (opts Options) ListOnly() bool {
	return *opts.list
}
<commit_msg>Small options clean up<commit_after>package options

import (
	"bufio"
	"flag"
	"fmt"
	"os"
)

// Options parsed at the command line
type Options struct {
	list      *bool
	Filepaths []string
}

// Parse arguments and flags and returns options configuration struct
func Parse() (opts Options) {
	opts.list = flag.Bool("list", false, "only list URIs found in files (i.e. no status check)")

	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage of URIstat: uristat [options] files...")
		fmt.Fprintln(os.Stderr, "Options:")
		flag.PrintDefaults()
	}

	flag.Parse()

	opts.populateFilepaths()

	return opts
}

func (opts *Options) populateFilepaths() {
	if flag.Parsed() {
		opts.Filepaths = flag.Args()
	} else {
		opts.Filepaths = os.Args[1:]
	}

	if len(opts.Filepaths) == 0 {
		inStat, _ := os.Stdin.Stat()

		if (inStat.Mode() & os.ModeCharDevice) == 0 {
			stdinScanner := bufio.NewScanner(os.Stdin)

			for stdinScanner.Scan() {
				opts.Filepaths = append(opts.Filepaths, stdinScanner.Text())
			}
		}
	}
}

// IsValid returns whether Options is valid
func (opts Options) IsValid() bool {
	return len(opts.Filepaths) > 0
}

// PrintError prints reason Options was invalid and usage info to stderr
func (opts Options) PrintError() {
	if len(opts.Filepaths) == 0 {
		fmt.Fprintln(os.Stderr, "No files to scan")
	}

	fmt.Fprintln(os.Stderr, "")
	flag.Usage()
}

// ListOnly returns bool indicating if the found URIs should be printed
func (opts Options) ListOnly() bool {
	return *opts.list
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package objectmeta

import (
	"testing"

	structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
	"k8s.io/apimachinery/pkg/util/json"
	"k8s.io/apimachinery/pkg/util/validation/field"
)

func TestValidateEmbeddedResource(t *testing.T) {
	tests := []struct {
		name   string
		object map[string]interface{}
		errors []validationMatch
	}{
		{name: "empty", object: map[string]interface{}{}, errors: []validationMatch{
			required("apiVersion"),
			required("kind"),
		}},
		{name: "version and kind", object: map[string]interface{}{
			"apiVersion": "foo/v1",
			"kind":       "Foo",
		}},
		{name: "invalid kind", object: map[string]interface{}{
			"apiVersion": "foo/v1",
			"kind":       "foo.bar-com",
		}, errors: []validationMatch{
			invalid("kind"),
		}},
		{name: "no name", object: map[string]interface{}{
			"apiVersion": "foo/v1",
			"kind":       "Foo",
			"metadata": map[string]interface{}{
				"namespace": "kube-system",
			},
		}},
		{name: "no namespace", object: map[string]interface{}{
			"apiVersion": "foo/v1",
			"kind":       "Foo",
			"metadata": map[string]interface{}{
				"name": "foo",
			},
		}},
		{name: "invalid", object: map[string]interface{}{
			"apiVersion": "foo/v1",
			"kind":       "Foo",
			"metadata": map[string]interface{}{
				"name":      "..",
				"namespace": "$$$",
				"labels": map[string]interface{}{
					"#": "#",
				},
				"annotations": map[string]interface{}{
					"#": "#",
				},
			},
		}, errors: []validationMatch{
			invalid("metadata", "name"),
			invalid("metadata", "namespace"),
			invalid("metadata", "labels"),      // key
			invalid("metadata", "labels"),      // value
			invalid("metadata", "annotations"), // key
		}},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			schema := &structuralschema.Structural{Extensions: structuralschema.Extensions{XEmbeddedResource: true}}
			errs := validateEmbeddedResource(nil, tt.object, schema)
			seenErrs := make([]bool, len(errs))

			for _, expectedError := range tt.errors {
				found := false
				for i, err := range errs {
					if expectedError.matches(err) && !seenErrs[i] {
						found = true
						seenErrs[i] = true
						break
					}
				}

				if !found {
					t.Errorf("expected %v at %v, got %v", expectedError.errorType, expectedError.path.String(), errs)
				}
			}

			for i, seen := range seenErrs {
				if !seen {
					t.Errorf("unexpected error: %v", errs[i])
				}
			}
		})
	}
}

func TestValidate(t *testing.T) {
	tests := []struct {
		name        string
		object      string
		includeRoot bool
		errors      []validationMatch
	}{
		{name: "empty", object: `{}`, errors: []validationMatch{}},
		{name: "include root", object: `{}`, includeRoot: true, errors: []validationMatch{
			required("apiVersion"),
			required("kind"),
		}},
		{name: "embedded", object: `
{
  "embedded": {}
}`, errors: []validationMatch{
			required("embedded", "apiVersion"),
			required("embedded", "kind"),
		}},
		{name: "nested", object: `
{
  "nested": {
    "embedded": {}
  }
}`, errors: []validationMatch{
			required("nested", "apiVersion"),
			required("nested", "kind"),
			required("nested", "embedded", "apiVersion"),
			required("nested", "embedded", "kind"),
		}},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			schema := &structuralschema.Structural{
				Properties: map[string]structuralschema.Structural{
					"embedded": {Extensions: structuralschema.Extensions{XEmbeddedResource: true}},
					"nested": {
						Extensions: structuralschema.Extensions{XEmbeddedResource: true},
						Properties: map[string]structuralschema.Structural{
							"embedded": {Extensions: structuralschema.Extensions{XEmbeddedResource: true}},
						},
					},
				},
			}

			var obj map[string]interface{}
			if err := json.Unmarshal([]byte(tt.object), &obj); err != nil {
				t.Fatal(err)
			}

			errs := Validate(nil, obj, schema, tt.includeRoot)
			seenErrs := make([]bool, len(errs))

			for _, expectedError := range tt.errors {
				found := false
				for i, err := range errs {
					if expectedError.matches(err) && !seenErrs[i] {
						found = true
						seenErrs[i] = true
						break
					}
				}

				if !found {
					t.Errorf("expected %v at %v, got %v", expectedError.errorType, expectedError.path.String(), errs)
				}
			}

			for i, seen := range seenErrs {
				if !seen {
					t.Errorf("unexpected error: %v", errs[i])
				}
			}
		})
	}
}

type validationMatch struct {
	path      *field.Path
	errorType field.ErrorType
}

func required(path ...string) validationMatch {
	return validationMatch{path: field.NewPath(path[0], path[1:]...), errorType: field.ErrorTypeRequired}
}
func invalid(path ...string) validationMatch {
	return validationMatch{path: field.NewPath(path[0], path[1:]...), errorType: field.ErrorTypeInvalid}
}
func invalidIndex(index int, path ...string) validationMatch {
	return validationMatch{path: field.NewPath(path[0], path[1:]...).Index(index), errorType: field.ErrorTypeInvalid}
}
func unsupported(path ...string) validationMatch {
	return validationMatch{path: field.NewPath(path[0], path[1:]...), errorType: field.ErrorTypeNotSupported}
}
func immutable(path ...string) validationMatch {
	return validationMatch{path: field.NewPath(path[0], path[1:]...), errorType: field.ErrorTypeInvalid}
}
func forbidden(path ...string) validationMatch {
	return validationMatch{path: field.NewPath(path[0], path[1:]...), errorType: field.ErrorTypeForbidden}
}

func (v validationMatch) matches(err *field.Error) bool {
	return err.Type == v.errorType && err.Field == v.path.String()
}
<commit_msg>apiextensions: add items+additionalProperties test coverage for objectmeta.Validate<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package objectmeta

import (
	"testing"

	structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
	"k8s.io/apimachinery/pkg/util/json"
	"k8s.io/apimachinery/pkg/util/validation/field"
)

func TestValidateEmbeddedResource(t *testing.T) {
	tests := []struct {
		name   string
		object map[string]interface{}
		errors []validationMatch
	}{
		{name: "empty", object: map[string]interface{}{}, errors: []validationMatch{
			required("apiVersion"),
			required("kind"),
		}},
		{name: "version and kind", object: map[string]interface{}{
			"apiVersion": "foo/v1",
			"kind":       "Foo",
		}},
		{name: "invalid kind", object: map[string]interface{}{
			"apiVersion": "foo/v1",
			"kind":       "foo.bar-com",
		}, errors: []validationMatch{
			invalid("kind"),
		}},
		{name: "no name", object: map[string]interface{}{
			"apiVersion": "foo/v1",
			"kind":       "Foo",
			"metadata": map[string]interface{}{
				"namespace": "kube-system",
			},
		}},
		{name: "no namespace", object: map[string]interface{}{
			"apiVersion": "foo/v1",
			"kind":       "Foo",
			"metadata": map[string]interface{}{
				"name": "foo",
			},
		}},
		{name: "invalid", object: map[string]interface{}{
			"apiVersion": "foo/v1",
			"kind":       "Foo",
			"metadata": map[string]interface{}{
				"name":      "..",
				"namespace": "$$$",
				"labels": map[string]interface{}{
					"#": "#",
				},
				"annotations": map[string]interface{}{
					"#": "#",
				},
			},
		}, errors: []validationMatch{
			invalid("metadata", "name"),
			invalid("metadata", "namespace"),
			invalid("metadata", "labels"),      // key
			invalid("metadata", "labels"),      // value
			invalid("metadata", "annotations"), // key
		}},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			schema := &structuralschema.Structural{Extensions: structuralschema.Extensions{XEmbeddedResource: true}}
			errs := validateEmbeddedResource(nil, tt.object, schema)
			seenErrs := make([]bool, len(errs))

			for _, expectedError := range tt.errors {
				found := false
				for i, err := range errs {
					if expectedError.matches(err) && !seenErrs[i] {
						found = true
						seenErrs[i] = true
						break
					}
				}

				if !found {
					t.Errorf("expected %v at %v, got %v", expectedError.errorType, expectedError.path.String(), errs)
				}
			}

			for i, seen := range seenErrs {
				if !seen {
					t.Errorf("unexpected error: %v", errs[i])
				}
			}
		})
	}
}

func TestValidate(t *testing.T) {
	tests := []struct {
		name        string
		object      string
		includeRoot bool
		errors      []validationMatch
	}{
		{name: "empty", object: `{}`, errors: []validationMatch{}},
		{name: "include root", object: `{}`, includeRoot: true, errors: []validationMatch{
			required("apiVersion"),
			required("kind"),
		}},
		{name: "embedded", object: `
{
  "embedded": {}
}`, errors: []validationMatch{
			required("embedded", "apiVersion"),
			required("embedded", "kind"),
		}},
		{name: "nested", object: `
{
  "nested": {
    "embedded": {}
  }
}`, errors: []validationMatch{
			required("nested", "apiVersion"),
			required("nested", "kind"),
			required("nested", "embedded", "apiVersion"),
			required("nested", "embedded", "kind"),
		}},
		{name: "items", object: `
{
  "items": [{}]
}`, errors: []validationMatch{
			required("items[0]", "apiVersion"),
			required("items[0]", "kind"),
		}},
		{name: "additionalProperties", object: `
{
  "additionalProperties": {"foo":{}}
}`, errors: []validationMatch{
			required("additionalProperties[foo]", "apiVersion"),
			required("additionalProperties[foo]", "kind"),
		}},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			schema := &structuralschema.Structural{
				Properties: map[string]structuralschema.Structural{
					"embedded": {Extensions: structuralschema.Extensions{XEmbeddedResource: true}},
					"nested": {
						Extensions: structuralschema.Extensions{XEmbeddedResource: true},
						Properties: map[string]structuralschema.Structural{
							"embedded": {Extensions: structuralschema.Extensions{XEmbeddedResource: true}},
						},
					},
					"items": {
						Items: &structuralschema.Structural{
							Extensions: structuralschema.Extensions{XEmbeddedResource: true},
						},
					},
					"additionalProperties": {
						Generic: structuralschema.Generic{
							AdditionalProperties: &structuralschema.StructuralOrBool{
								Structural: &structuralschema.Structural{
									Extensions: structuralschema.Extensions{XEmbeddedResource: true},
								},
							},
						},
					},
				},
			}

			var obj map[string]interface{}
			if err := json.Unmarshal([]byte(tt.object), &obj); err != nil {
				t.Fatal(err)
			}

			errs := Validate(nil, obj, schema, tt.includeRoot)
			seenErrs := make([]bool, len(errs))

			for _, expectedError := range tt.errors {
				found := false
				for i, err := range errs {
					if expectedError.matches(err) && !seenErrs[i] {
						found = true
						seenErrs[i] = true
						break
					}
				}

				if !found {
					t.Errorf("expected %v at %v, got %v", expectedError.errorType, expectedError.path.String(), errs)
				}
			}

			for i, seen := range seenErrs {
				if !seen {
					t.Errorf("unexpected error: %v", errs[i])
				}
			}
		})
	}
}

type validationMatch struct {
	path      *field.Path
	errorType field.ErrorType
}

func required(path ...string) validationMatch {
	return validationMatch{path: field.NewPath(path[0], path[1:]...), errorType: field.ErrorTypeRequired}
}
func invalid(path ...string) validationMatch {
	return validationMatch{path: field.NewPath(path[0], path[1:]...), errorType: field.ErrorTypeInvalid}
}
func invalidIndex(index int, path ...string) validationMatch {
	return validationMatch{path: field.NewPath(path[0], path[1:]...).Index(index), errorType: field.ErrorTypeInvalid}
}
func unsupported(path ...string) validationMatch {
	return validationMatch{path: field.NewPath(path[0], path[1:]...), errorType: field.ErrorTypeNotSupported}
}
func immutable(path ...string) validationMatch {
	return validationMatch{path: field.NewPath(path[0], path[1:]...), errorType: field.ErrorTypeInvalid}
}
func forbidden(path ...string) validationMatch {
	return validationMatch{path: field.NewPath(path[0], path[1:]...), errorType: field.ErrorTypeForbidden}
}

func (v validationMatch) matches(err *field.Error) bool {
	return err.Type == v.errorType && err.Field == v.path.String()
}
<|endoftext|>


<commit_before>/* https://leetcode.com/problems/first-unique-character-in-a-string/#/description
Given a string, find the first non-repeating character in it and return it's index. If it doesn't exist, return -1.

Examples:

    s = "leetcode"
    return 0.

    s = "loveleetcode",
    return 2.

Note: You may assume the string contain only lowercase letters.
*/

package leetcode

func firstUniqChar(s string) int {
	return -1
}
<commit_msg>add firstUniqChar<commit_after>/* https://leetcode.com/problems/first-unique-character-in-a-string/#/description
Given a string, find the first non-repeating character in it and return it's index. If it doesn't exist, return -1.

Examples:

    s = "leetcode"
    return 0.

    s = "loveleetcode",
    return 2.

Note: You may assume the string contain only lowercase letters.
*/

package leetcode

func firstUniqChar(s string) int {
	maps := make([]int, 26)
	for _, r := range s {
		maps[r-'a'] += 1
	}

	for i, r := range s {
		if maps[r-'a'] == 1 {
			return i
		}
	}
	return -1
}
<|endoftext|>


<commit_before><commit_msg>edit/ui: Add thin wrappers around styled.MakeText and styled.MarkLines.<commit_after><|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package clientcmd

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

	"github.com/ghodss/yaml"
	"github.com/imdario/mergo"

	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
	clientcmdlatest "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
)

const (
	RecommendedConfigPathFlag   = "kubeconfig"
	RecommendedConfigPathEnvVar = "KUBECONFIG"

	DefaultEnvVarIndex     = 0
	DefaultCurrentDirIndex = 1
	DefaultHomeDirIndex    = 2
)

// ClientConfigLoadingRules is an ExplicitPath and string slice of specific locations that are used for merging together a Config
// Callers can put the chain together however they want, but we'd recommend:
// [0] = EnvVarPath
// [1] = CurrentDirectoryPath
// [2] = HomeDirectoryPath
// ExplicitPath is special, because if a user specifically requests a certain file be used and error is reported if thie file is not present
type ClientConfigLoadingRules struct {
	ExplicitPath string
	Precedence   []string

	// DoNotResolvePaths indicates whether or not to resolve paths with respect to the originating files.  This is phrased as a negative so
	// that a default object that doesn't set this will usually get the behavior it wants.
	DoNotResolvePaths bool
}

// NewDefaultClientConfigLoadingRules returns a ClientConfigLoadingRules object with default fields filled in.  You are not required to
// use this constructor
func NewDefaultClientConfigLoadingRules() *ClientConfigLoadingRules {
	return &ClientConfigLoadingRules{
		Precedence: []string{os.Getenv(RecommendedConfigPathEnvVar), ".kubeconfig", os.Getenv("HOME") + "/.kube/.kubeconfig"},
	}
}

// Load takes the loading rules and merges together a Config object based on following order.
//   1.  ExplicitPath
//   2.  Precedence slice
// A missing ExplicitPath file produces an error. Empty filenames or other missing files are ignored.
// Read errors or files with non-deserializable content produce errors.
// The first file to set a particular map key wins and map key's value is never changed.
// BUT, if you set a struct value that is NOT contained inside of map, the value WILL be changed.
// This results in some odd looking logic to merge in one direction, merge in the other, and then merge the two.
// It also means that if two files specify a "red-user", only values from the first file's red-user are used.  Even
// non-conflicting entries from the second file's "red-user" are discarded.
// Relative paths inside of the .kubeconfig files are resolved against the .kubeconfig file's parent folder
// and only absolute file paths are returned.
func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
	errlist := []error{}

	// Make sure a file we were explicitly told to use exists
	if len(rules.ExplicitPath) > 0 {
		if _, err := os.Stat(rules.ExplicitPath); os.IsNotExist(err) {
			errlist = append(errlist, fmt.Errorf("The config file %v does not exist", rules.ExplicitPath))
		}
	}

	kubeConfigFiles := []string{rules.ExplicitPath}
	kubeConfigFiles = append(kubeConfigFiles, rules.Precedence...)

	// first merge all of our maps
	mapConfig := clientcmdapi.NewConfig()
	for _, file := range kubeConfigFiles {
		if err := mergeConfigWithFile(mapConfig, file); err != nil {
			errlist = append(errlist, err)
		}
		if !rules.DoNotResolvePaths {
			if err := ResolveLocalPaths(file, mapConfig); err != nil {
				errlist = append(errlist, err)
			}
		}
	}

	// merge all of the struct values in the reverse order so that priority is given correctly
	// errors are not added to the list the second time
	nonMapConfig := clientcmdapi.NewConfig()
	for i := len(kubeConfigFiles) - 1; i >= 0; i-- {
		file := kubeConfigFiles[i]
		mergeConfigWithFile(nonMapConfig, file)
		if !rules.DoNotResolvePaths {
			ResolveLocalPaths(file, nonMapConfig)
		}
	}

	// since values are overwritten, but maps values are not, we can merge the non-map config on top of the map config and
	// get the values we expect.
	config := clientcmdapi.NewConfig()
	mergo.Merge(config, mapConfig)
	mergo.Merge(config, nonMapConfig)

	return config, errors.NewAggregate(errlist)
}

func mergeConfigWithFile(startingConfig *clientcmdapi.Config, filename string) error {
	if len(filename) == 0 {
		// no work to do
		return nil
	}

	config, err := LoadFromFile(filename)
	if os.IsNotExist(err) {
		return nil
	}
	if err != nil {
		return fmt.Errorf("Error loading config file \"%s\": %v", filename, err)
	}

	mergo.Merge(startingConfig, config)

	return nil
}

// ResolveLocalPaths resolves all relative paths in the config object with respect to the parent directory of the filename
// this cannot be done directly inside of LoadFromFile because doing so there would make it impossible to load a file without
// modification of its contents.
func ResolveLocalPaths(filename string, config *clientcmdapi.Config) error {
	if len(filename) == 0 {
		return nil
	}

	configDir, err := filepath.Abs(filepath.Dir(filename))
	if err != nil {
		return fmt.Errorf("Could not determine the absolute path of config file %s: %v", filename, err)
	}

	resolvedClusters := make(map[string]clientcmdapi.Cluster)
	for key, cluster := range config.Clusters {
		cluster.CertificateAuthority = resolveLocalPath(configDir, cluster.CertificateAuthority)
		resolvedClusters[key] = cluster
	}
	config.Clusters = resolvedClusters

	resolvedAuthInfos := make(map[string]clientcmdapi.AuthInfo)
	for key, authInfo := range config.AuthInfos {
		authInfo.AuthPath = resolveLocalPath(configDir, authInfo.AuthPath)
		authInfo.ClientCertificate = resolveLocalPath(configDir, authInfo.ClientCertificate)
		authInfo.ClientKey = resolveLocalPath(configDir, authInfo.ClientKey)
		resolvedAuthInfos[key] = authInfo
	}
	config.AuthInfos = resolvedAuthInfos

	return nil
}

// resolveLocalPath makes the path absolute with respect to the startingDir
func resolveLocalPath(startingDir, path string) string {
	if len(path) == 0 {
		return path
	}
	if filepath.IsAbs(path) {
		return path
	}

	return filepath.Join(startingDir, path)
}

// LoadFromFile takes a filename and deserializes the contents into Config object
func LoadFromFile(filename string) (*clientcmdapi.Config, error) {
	kubeconfigBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	config, err := Load(kubeconfigBytes)
	if err != nil {
		return nil, err
	}

	// set LocationOfOrigin on every Cluster, User, and Context
	for key, obj := range config.AuthInfos {
		obj.LocationOfOrigin = filename
		config.AuthInfos[key] = obj
	}
	for key, obj := range config.Clusters {
		obj.LocationOfOrigin = filename
		config.Clusters[key] = obj
	}
	for key, obj := range config.Contexts {
		obj.LocationOfOrigin = filename
		config.Contexts[key] = obj
	}

	return config, nil
}

// Load takes a byte slice and deserializes the contents into Config object.
// Encapsulates deserialization without assuming the source is a file.
func Load(data []byte) (*clientcmdapi.Config, error) {
	config := clientcmdapi.NewConfig()
	// if there's no data in a file, return the default object instead of failing (DecodeInto reject empty input)
	if len(data) == 0 {
		return config, nil
	}

	if err := clientcmdlatest.Codec.DecodeInto(data, config); err != nil {
		return nil, err
	}
	return config, nil
}

// WriteToFile serializes the config to yaml and writes it out to a file.  If not present, it creates the file with the mode 0600.  If it is present
// it stomps the contents
func WriteToFile(config clientcmdapi.Config, filename string) error {
	content, err := Write(config)
	if err != nil {
		return err
	}
	if err := ioutil.WriteFile(filename, content, 0600); err != nil {
		return err
	}
	return nil
}

// Write serializes the config to yaml.
// Encapsulates serialization without assuming the destination is a file.
func Write(config clientcmdapi.Config) ([]byte, error) {
	json, err := clientcmdlatest.Codec.Encode(&config)
	if err != nil {
		return nil, err
	}
	content, err := yaml.JSONToYAML(json)
	if err != nil {
		return nil, err
	}
	return content, nil
}
<commit_msg>UPSTREAM: create parent dir structure when saving config to file<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package clientcmd

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

	"github.com/ghodss/yaml"
	"github.com/imdario/mergo"

	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
	clientcmdlatest "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
)

const (
	RecommendedConfigPathFlag   = "kubeconfig"
	RecommendedConfigPathEnvVar = "KUBECONFIG"

	DefaultEnvVarIndex     = 0
	DefaultCurrentDirIndex = 1
	DefaultHomeDirIndex    = 2
)

// ClientConfigLoadingRules is an ExplicitPath and string slice of specific locations that are used for merging together a Config
// Callers can put the chain together however they want, but we'd recommend:
// [0] = EnvVarPath
// [1] = CurrentDirectoryPath
// [2] = HomeDirectoryPath
// ExplicitPath is special, because if a user specifically requests a certain file be used and error is reported if thie file is not present
type ClientConfigLoadingRules struct {
	ExplicitPath string
	Precedence   []string

	// DoNotResolvePaths indicates whether or not to resolve paths with respect to the originating files.  This is phrased as a negative so
	// that a default object that doesn't set this will usually get the behavior it wants.
	DoNotResolvePaths bool
}

// NewDefaultClientConfigLoadingRules returns a ClientConfigLoadingRules object with default fields filled in.  You are not required to
// use this constructor
func NewDefaultClientConfigLoadingRules() *ClientConfigLoadingRules {
	return &ClientConfigLoadingRules{
		Precedence: []string{os.Getenv(RecommendedConfigPathEnvVar), ".kubeconfig", os.Getenv("HOME") + "/.kube/.kubeconfig"},
	}
}

// Load takes the loading rules and merges together a Config object based on following order.
//   1.  ExplicitPath
//   2.  Precedence slice
// A missing ExplicitPath file produces an error. Empty filenames or other missing files are ignored.
// Read errors or files with non-deserializable content produce errors.
// The first file to set a particular map key wins and map key's value is never changed.
// BUT, if you set a struct value that is NOT contained inside of map, the value WILL be changed.
// This results in some odd looking logic to merge in one direction, merge in the other, and then merge the two.
// It also means that if two files specify a "red-user", only values from the first file's red-user are used.  Even
// non-conflicting entries from the second file's "red-user" are discarded.
// Relative paths inside of the .kubeconfig files are resolved against the .kubeconfig file's parent folder
// and only absolute file paths are returned.
func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
	errlist := []error{}

	// Make sure a file we were explicitly told to use exists
	if len(rules.ExplicitPath) > 0 {
		if _, err := os.Stat(rules.ExplicitPath); os.IsNotExist(err) {
			errlist = append(errlist, fmt.Errorf("The config file %v does not exist", rules.ExplicitPath))
		}
	}

	kubeConfigFiles := []string{rules.ExplicitPath}
	kubeConfigFiles = append(kubeConfigFiles, rules.Precedence...)

	// first merge all of our maps
	mapConfig := clientcmdapi.NewConfig()
	for _, file := range kubeConfigFiles {
		if err := mergeConfigWithFile(mapConfig, file); err != nil {
			errlist = append(errlist, err)
		}
		if !rules.DoNotResolvePaths {
			if err := ResolveLocalPaths(file, mapConfig); err != nil {
				errlist = append(errlist, err)
			}
		}
	}

	// merge all of the struct values in the reverse order so that priority is given correctly
	// errors are not added to the list the second time
	nonMapConfig := clientcmdapi.NewConfig()
	for i := len(kubeConfigFiles) - 1; i >= 0; i-- {
		file := kubeConfigFiles[i]
		mergeConfigWithFile(nonMapConfig, file)
		if !rules.DoNotResolvePaths {
			ResolveLocalPaths(file, nonMapConfig)
		}
	}

	// since values are overwritten, but maps values are not, we can merge the non-map config on top of the map config and
	// get the values we expect.
	config := clientcmdapi.NewConfig()
	mergo.Merge(config, mapConfig)
	mergo.Merge(config, nonMapConfig)

	return config, errors.NewAggregate(errlist)
}

func mergeConfigWithFile(startingConfig *clientcmdapi.Config, filename string) error {
	if len(filename) == 0 {
		// no work to do
		return nil
	}

	config, err := LoadFromFile(filename)
	if os.IsNotExist(err) {
		return nil
	}
	if err != nil {
		return fmt.Errorf("Error loading config file \"%s\": %v", filename, err)
	}

	mergo.Merge(startingConfig, config)

	return nil
}

// ResolveLocalPaths resolves all relative paths in the config object with respect to the parent directory of the filename
// this cannot be done directly inside of LoadFromFile because doing so there would make it impossible to load a file without
// modification of its contents.
func ResolveLocalPaths(filename string, config *clientcmdapi.Config) error {
	if len(filename) == 0 {
		return nil
	}

	configDir, err := filepath.Abs(filepath.Dir(filename))
	if err != nil {
		return fmt.Errorf("Could not determine the absolute path of config file %s: %v", filename, err)
	}

	resolvedClusters := make(map[string]clientcmdapi.Cluster)
	for key, cluster := range config.Clusters {
		cluster.CertificateAuthority = resolveLocalPath(configDir, cluster.CertificateAuthority)
		resolvedClusters[key] = cluster
	}
	config.Clusters = resolvedClusters

	resolvedAuthInfos := make(map[string]clientcmdapi.AuthInfo)
	for key, authInfo := range config.AuthInfos {
		authInfo.AuthPath = resolveLocalPath(configDir, authInfo.AuthPath)
		authInfo.ClientCertificate = resolveLocalPath(configDir, authInfo.ClientCertificate)
		authInfo.ClientKey = resolveLocalPath(configDir, authInfo.ClientKey)
		resolvedAuthInfos[key] = authInfo
	}
	config.AuthInfos = resolvedAuthInfos

	return nil
}

// resolveLocalPath makes the path absolute with respect to the startingDir
func resolveLocalPath(startingDir, path string) string {
	if len(path) == 0 {
		return path
	}
	if filepath.IsAbs(path) {
		return path
	}

	return filepath.Join(startingDir, path)
}

// LoadFromFile takes a filename and deserializes the contents into Config object
func LoadFromFile(filename string) (*clientcmdapi.Config, error) {
	kubeconfigBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	config, err := Load(kubeconfigBytes)
	if err != nil {
		return nil, err
	}

	// set LocationOfOrigin on every Cluster, User, and Context
	for key, obj := range config.AuthInfos {
		obj.LocationOfOrigin = filename
		config.AuthInfos[key] = obj
	}
	for key, obj := range config.Clusters {
		obj.LocationOfOrigin = filename
		config.Clusters[key] = obj
	}
	for key, obj := range config.Contexts {
		obj.LocationOfOrigin = filename
		config.Contexts[key] = obj
	}

	return config, nil
}

// Load takes a byte slice and deserializes the contents into Config object.
// Encapsulates deserialization without assuming the source is a file.
func Load(data []byte) (*clientcmdapi.Config, error) {
	config := clientcmdapi.NewConfig()
	// if there's no data in a file, return the default object instead of failing (DecodeInto reject empty input)
	if len(data) == 0 {
		return config, nil
	}

	if err := clientcmdlatest.Codec.DecodeInto(data, config); err != nil {
		return nil, err
	}
	return config, nil
}

// WriteToFile serializes the config to yaml and writes it out to a file.  If not present, it creates the file with the mode 0600.  If it is present
// it stomps the contents
func WriteToFile(config clientcmdapi.Config, filename string) error {
	content, err := Write(config)
	if err != nil {
		return err
	}
	dir := filepath.Dir(filename)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err = os.MkdirAll(dir, 0755); err != nil {
			return err
		}
	}
	if err := ioutil.WriteFile(filename, content, 0600); err != nil {
		return err
	}
	return nil
}

// Write serializes the config to yaml.
// Encapsulates serialization without assuming the destination is a file.
func Write(config clientcmdapi.Config) ([]byte, error) {
	json, err := clientcmdlatest.Codec.Encode(&config)
	if err != nil {
		return nil, err
	}
	content, err := yaml.JSONToYAML(json)
	if err != nil {
		return nil, err
	}
	return content, nil
}
<|endoftext|>


<commit_before>package main
import (
    "fmt"
    "math"
    "strconv"
    "strings"
)

func (s *NumberStats) init() {
    s.Max = math.Inf(-1)
    s.Min = math.Inf(1)
}

func (s *NumberStats) update(value string) {
    cleanVal := strings.Replace(value, ",", "", -1)
    f, err := strconv.ParseFloat(cleanVal, 64)
    if err != nil {
        return
    }
    if math.IsInf(f,0) || math.IsNaN(f) {
        return
    }
    _, err = strconv.Atoi(cleanVal)
    if err != nil {
        // Value is not an integer
        s.IsFloat = true
    }
    s.NumValidNums++
    s.Min = math.Min(s.Min, f)
    s.Max = math.Max(s.Max, f)
    s.Sum += f
    s.SumSquares += f*f
}

func (s *NumberStats) String() string {
    var ret string
    if s.IsFloat {
        ret = "real numbers, "
    } else {
        ret = "integers, "
    }

    if s.NumValidNums == 0 {
        ret = "not numeric"
    } else if s.Min == s.Max {
        if s.IsFloat {
            ret += fmt.Sprintf("always %f", s.Min)
        } else {
            ret += fmt.Sprintf("integers, always %d", int64(s.Min))
        }
    } else {
        avg := s.Sum / float64(s.NumValidNums)
        stdev := math.Sqrt((s.SumSquares - avg*avg) / float64(s.NumValidNums))

        if s.IsFloat {
            ret += fmt.Sprintf("min %f max %f avg %f stdev %f",
                s.Min, s.Max, avg, stdev)
        } else {
            ret += fmt.Sprintf("min %d max %d avg %f stdev %f",
                int64(s.Min), int64(s.Max), avg, stdev)
        }
    }
    return ret
}

<commit_msg>Fixed bug in standard deviation calculation<commit_after>package main
import (
    "fmt"
    "math"
    "strconv"
    "strings"
)

func (s *NumberStats) init() {
    s.Max = math.Inf(-1)
    s.Min = math.Inf(1)
}

func (s *NumberStats) update(value string) {
    cleanVal := strings.Replace(value, ",", "", -1)
    f, err := strconv.ParseFloat(cleanVal, 64)
    if err != nil {
        return
    }
    if math.IsInf(f,0) || math.IsNaN(f) {
        return
    }
    _, err = strconv.Atoi(cleanVal)
    if err != nil {
        // Value is not an integer
        s.IsFloat = true
    }
    s.NumValidNums++
    s.Min = math.Min(s.Min, f)
    s.Max = math.Max(s.Max, f)
    s.Sum += f
    s.SumSquares += f*f
}

func (s *NumberStats) String() string {
    var ret string
    if s.IsFloat {
        ret = "real numbers, "
    } else {
        ret = "integers, "
    }

    if s.NumValidNums == 0 {
        ret = "not numeric"
    } else if s.Min == s.Max {
        if s.IsFloat {
            ret += fmt.Sprintf("always %f", s.Min)
        } else {
            ret += fmt.Sprintf("integers, always %d", int64(s.Min))
        }
    } else {
        avg := s.Sum / float64(s.NumValidNums)
        stdev := math.Sqrt((s.SumSquares / float64(s.NumValidNums)) - avg*avg)

        if s.IsFloat {
            ret += fmt.Sprintf("min %f max %f avg %f stdev %f",
                s.Min, s.Max, avg, stdev)
        } else {
            ret += fmt.Sprintf("min %d max %d avg %f stdev %f",
                int64(s.Min), int64(s.Max), avg, stdev)
        }
    }
    return ret
}

<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/codecommit"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsCodeCommitRepository() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsCodeCommitRepositoryCreate,
		Update: resourceAwsCodeCommitRepositoryUpdate,
		Read:   resourceAwsCodeCommitRepositoryRead,
		Delete: resourceAwsCodeCommitRepositoryDelete,

		Schema: map[string]*schema.Schema{
			"repository_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					value := v.(string)
					if len(value) > 100 {
						errors = append(errors, fmt.Errorf(
							"%q cannot be longer than 100 characters", k))
					}
					return
				},
			},

			"description": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					value := v.(string)
					if len(value) > 1000 {
						errors = append(errors, fmt.Errorf(
							"%q cannot be longer than 1000 characters", k))
					}
					return
				},
			},

			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"repository_id": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"clone_url_http": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"clone_url_ssh": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"default_branch": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

func resourceAwsCodeCommitRepositoryCreate(d *schema.ResourceData, meta interface{}) error {
	codecommitconn := meta.(*AWSClient).codecommitconn
	region := meta.(*AWSClient).region

	//	This is a temporary thing - we need to ensure that CodeCommit is only being run against us-east-1
	//	As this is the only place that AWS currently supports it
	if region != "us-east-1" {
		return fmt.Errorf("CodeCommit can only be used with us-east-1. You are trying to use it on %s", region)
	}

	input := &codecommit.CreateRepositoryInput{
		RepositoryName:        aws.String(d.Get("repository_name").(string)),
		RepositoryDescription: aws.String(d.Get("description").(string)),
	}

	out, err := codecommitconn.CreateRepository(input)
	if err != nil {
		return fmt.Errorf("Error creating CodeCommit Repository: %s", err)
	}

	d.SetId(d.Get("repository_name").(string))
	d.Set("repository_id", *out.RepositoryMetadata.RepositoryId)
	d.Set("arn", *out.RepositoryMetadata.Arn)
	d.Set("clone_url_http", *out.RepositoryMetadata.CloneUrlHttp)
	d.Set("clone_url_ssh", *out.RepositoryMetadata.CloneUrlSsh)

	return resourceAwsCodeCommitRepositoryUpdate(d, meta)
}

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

	if d.HasChange("default_branch") {
		if err := resourceAwsCodeCommitUpdateDefaultBranch(codecommitconn, d); err != nil {
			return err
		}
	}

	if d.HasChange("description") {
		if err := resourceAwsCodeCommitUpdateDescription(codecommitconn, d); err != nil {
			return err
		}
	}

	return resourceAwsCodeCommitRepositoryRead(d, meta)
}

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

	input := &codecommit.GetRepositoryInput{
		RepositoryName: aws.String(d.Id()),
	}

	out, err := codecommitconn.GetRepository(input)
	if err != nil {
		return fmt.Errorf("Error reading CodeCommit Repository: %s", err.Error())
	}

	d.Set("repository_id", *out.RepositoryMetadata.RepositoryId)
	d.Set("arn", *out.RepositoryMetadata.Arn)
	d.Set("clone_url_http", *out.RepositoryMetadata.CloneUrlHttp)
	d.Set("clone_url_ssh", *out.RepositoryMetadata.CloneUrlSsh)
	if out.RepositoryMetadata.DefaultBranch != nil {
		d.Set("default_branch", *out.RepositoryMetadata.DefaultBranch)
	}

	return nil
}

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

	log.Printf("[DEBUG] CodeCommit Delete Repository: %s", d.Id())
	_, err := codecommitconn.DeleteRepository(&codecommit.DeleteRepositoryInput{
		RepositoryName: aws.String(d.Id()),
	})
	if err != nil {
		return fmt.Errorf("Error deleting CodeCommit Repository: %s", err.Error())
	}

	return nil
}

func resourceAwsCodeCommitUpdateDescription(codecommitconn *codecommit.CodeCommit, d *schema.ResourceData) error {
	branchInput := &codecommit.UpdateRepositoryDescriptionInput{
		RepositoryName:        aws.String(d.Id()),
		RepositoryDescription: aws.String(d.Get("description").(string)),
	}

	_, err := codecommitconn.UpdateRepositoryDescription(branchInput)
	if err != nil {
		return fmt.Errorf("Error Updating Repository Description for CodeCommit Repository: %s", err.Error())
	}

	return nil
}

func resourceAwsCodeCommitUpdateDefaultBranch(codecommitconn *codecommit.CodeCommit, d *schema.ResourceData) error {
	branchInput := &codecommit.UpdateDefaultBranchInput{
		RepositoryName:    aws.String(d.Id()),
		DefaultBranchName: aws.String(d.Get("default_branch").(string)),
	}

	_, err := codecommitconn.UpdateDefaultBranch(branchInput)
	if err != nil {
		return fmt.Errorf("Error Updating Default Branch for CodeCommit Repository: %s", err.Error())
	}

	return nil
}
<commit_msg>provider/aws: var name tweak<commit_after>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/codecommit"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsCodeCommitRepository() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsCodeCommitRepositoryCreate,
		Update: resourceAwsCodeCommitRepositoryUpdate,
		Read:   resourceAwsCodeCommitRepositoryRead,
		Delete: resourceAwsCodeCommitRepositoryDelete,

		Schema: map[string]*schema.Schema{
			"repository_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					value := v.(string)
					if len(value) > 100 {
						errors = append(errors, fmt.Errorf(
							"%q cannot be longer than 100 characters", k))
					}
					return
				},
			},

			"description": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					value := v.(string)
					if len(value) > 1000 {
						errors = append(errors, fmt.Errorf(
							"%q cannot be longer than 1000 characters", k))
					}
					return
				},
			},

			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"repository_id": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"clone_url_http": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"clone_url_ssh": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"default_branch": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

func resourceAwsCodeCommitRepositoryCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).codecommitconn
	region := meta.(*AWSClient).region

	//	This is a temporary thing - we need to ensure that CodeCommit is only being run against us-east-1
	//	As this is the only place that AWS currently supports it
	if region != "us-east-1" {
		return fmt.Errorf("CodeCommit can only be used with us-east-1. You are trying to use it on %s", region)
	}

	input := &codecommit.CreateRepositoryInput{
		RepositoryName:        aws.String(d.Get("repository_name").(string)),
		RepositoryDescription: aws.String(d.Get("description").(string)),
	}

	out, err := conn.CreateRepository(input)
	if err != nil {
		return fmt.Errorf("Error creating CodeCommit Repository: %s", err)
	}

	d.SetId(d.Get("repository_name").(string))
	d.Set("repository_id", *out.RepositoryMetadata.RepositoryId)
	d.Set("arn", *out.RepositoryMetadata.Arn)
	d.Set("clone_url_http", *out.RepositoryMetadata.CloneUrlHttp)
	d.Set("clone_url_ssh", *out.RepositoryMetadata.CloneUrlSsh)

	return resourceAwsCodeCommitRepositoryUpdate(d, meta)
}

func resourceAwsCodeCommitRepositoryUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).codecommitconn

	if d.HasChange("default_branch") {
		if err := resourceAwsCodeCommitUpdateDefaultBranch(conn, d); err != nil {
			return err
		}
	}

	if d.HasChange("description") {
		if err := resourceAwsCodeCommitUpdateDescription(conn, d); err != nil {
			return err
		}
	}

	return resourceAwsCodeCommitRepositoryRead(d, meta)
}

func resourceAwsCodeCommitRepositoryRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).codecommitconn

	input := &codecommit.GetRepositoryInput{
		RepositoryName: aws.String(d.Id()),
	}

	out, err := conn.GetRepository(input)
	if err != nil {
		return fmt.Errorf("Error reading CodeCommit Repository: %s", err.Error())
	}

	d.Set("repository_id", *out.RepositoryMetadata.RepositoryId)
	d.Set("arn", *out.RepositoryMetadata.Arn)
	d.Set("clone_url_http", *out.RepositoryMetadata.CloneUrlHttp)
	d.Set("clone_url_ssh", *out.RepositoryMetadata.CloneUrlSsh)
	if out.RepositoryMetadata.DefaultBranch != nil {
		d.Set("default_branch", *out.RepositoryMetadata.DefaultBranch)
	}

	return nil
}

func resourceAwsCodeCommitRepositoryDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).codecommitconn

	log.Printf("[DEBUG] CodeCommit Delete Repository: %s", d.Id())
	_, err := conn.DeleteRepository(&codecommit.DeleteRepositoryInput{
		RepositoryName: aws.String(d.Id()),
	})
	if err != nil {
		return fmt.Errorf("Error deleting CodeCommit Repository: %s", err.Error())
	}

	return nil
}

func resourceAwsCodeCommitUpdateDescription(conn *codecommit.CodeCommit, d *schema.ResourceData) error {
	branchInput := &codecommit.UpdateRepositoryDescriptionInput{
		RepositoryName:        aws.String(d.Id()),
		RepositoryDescription: aws.String(d.Get("description").(string)),
	}

	_, err := conn.UpdateRepositoryDescription(branchInput)
	if err != nil {
		return fmt.Errorf("Error Updating Repository Description for CodeCommit Repository: %s", err.Error())
	}

	return nil
}

func resourceAwsCodeCommitUpdateDefaultBranch(conn *codecommit.CodeCommit, d *schema.ResourceData) error {
	branchInput := &codecommit.UpdateDefaultBranchInput{
		RepositoryName:    aws.String(d.Id()),
		DefaultBranchName: aws.String(d.Get("default_branch").(string)),
	}

	_, err := conn.UpdateDefaultBranch(branchInput)
	if err != nil {
		return fmt.Errorf("Error Updating Default Branch for CodeCommit Repository: %s", err.Error())
	}

	return nil
}
<|endoftext|>