<commit_before>package object

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strings"

	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/plumbing/storer"
	"gopkg.in/src-d/go-git.v4/utils/ioutil"
)

// Hash represents the hash of an object
type Hash plumbing.Hash

// Commit points to a single tree, marking it as what the project looked like
// at a certain point in time. It contains meta-information about that point
// in time, such as a timestamp, the author of the changes since the last
// commit, a pointer to the previous commit(s), etc.
// http://schacon.github.io/gitbook/1_the_git_object_model.html
type Commit struct {
	// Hash of the commit object.
	Hash plumbing.Hash
	// Author is the original author of the commit.
	Author Signature
	// Committer is the one performing the commit, might be different from
	// Author.
	Committer Signature
	// Message is the commit message, contains arbitrary text.
	Message string
	// TreeHash is the hash of the root tree of the commit.
	TreeHash plumbing.Hash
	// ParentHashes are the hashes of the parent commits of the commit.
	ParentHashes []plumbing.Hash

	s storer.EncodedObjectStorer
}

// GetCommit gets a commit from an object storer and decodes it.
func GetCommit(s storer.EncodedObjectStorer, h plumbing.Hash) (*Commit, error) {
	o, err := s.EncodedObject(plumbing.CommitObject, h)
	if err != nil {
		return nil, err
	}

	return DecodeCommit(s, o)
}

// DecodeCommit decodes an encoded object into a *Commit and associates it to
// the given object storer.
func DecodeCommit(s storer.EncodedObjectStorer, o plumbing.EncodedObject) (*Commit, error) {
	c := &Commit{s: s}
	if err := c.Decode(o); err != nil {
		return nil, err
	}

	return c, nil
}

// Tree returns the Tree from the commit.
func (c *Commit) Tree() (*Tree, error) {
	return GetTree(c.s, c.TreeHash)
}

// Patch returns the Patch between the actual commit and the provided one.
func (c *Commit) Patch(to *Commit) (*Patch, error) {
	fromTree, err := c.Tree()
	if err != nil {
		return nil, err
	}

	toTree, err := to.Tree()
	if err != nil {
		return nil, err
	}

	return fromTree.Patch(toTree)
}

// Parents return a CommitIter to the parent Commits.
func (c *Commit) Parents() CommitIter {
	return NewCommitIter(c.s,
		storer.NewEncodedObjectLookupIter(c.s, plumbing.CommitObject, c.ParentHashes),
	)
}

// NumParents returns the number of parents in a commit.
func (c *Commit) NumParents() int {
	return len(c.ParentHashes)
}

// File returns the file with the specified "path" in the commit and a
// nil error if the file exists. If the file does not exist, it returns
// a nil file and the ErrFileNotFound error.
func (c *Commit) File(path string) (*File, error) {
	tree, err := c.Tree()
	if err != nil {
		return nil, err
	}

	return tree.File(path)
}

// Files returns a FileIter allowing to iterate over the Tree
func (c *Commit) Files() (*FileIter, error) {
	tree, err := c.Tree()
	if err != nil {
		return nil, err
	}

	return tree.Files(), nil
}

// ID returns the object ID of the commit. The returned value will always match
// the current value of Commit.Hash.
//
// ID is present to fulfill the Object interface.
func (c *Commit) ID() plumbing.Hash {
	return c.Hash
}

// Type returns the type of object. It always returns plumbing.CommitObject.
//
// Type is present to fulfill the Object interface.
func (c *Commit) Type() plumbing.ObjectType {
	return plumbing.CommitObject
}

// Decode transforms a plumbing.EncodedObject into a Commit struct.
func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
	if o.Type() != plumbing.CommitObject {
		return ErrUnsupportedObject
	}

	c.Hash = o.Hash()

	reader, err := o.Reader()
	if err != nil {
		return err
	}
	defer ioutil.CheckClose(reader, &err)

	r := bufio.NewReader(reader)

	var message bool
	for {
		line, err := r.ReadBytes('\n')
		if err != nil && err != io.EOF {
			return err
		}

		if !message {
			line = bytes.TrimSpace(line)
			if len(line) == 0 {
				message = true
				continue
			}

			split := bytes.SplitN(line, []byte{' '}, 2)
			switch string(split[0]) {
			case "tree":
				c.TreeHash = plumbing.NewHash(string(split[1]))
			case "parent":
				c.ParentHashes = append(c.ParentHashes, plumbing.NewHash(string(split[1])))
			case "author":
				c.Author.Decode(split[1])
			case "committer":
				c.Committer.Decode(split[1])
			}
		} else {
			c.Message += string(line)
		}

		if err == io.EOF {
			return nil
		}
	}
}

// Encode transforms a Commit into a plumbing.EncodedObject.
func (b *Commit) Encode(o plumbing.EncodedObject) error {
	o.SetType(plumbing.CommitObject)
	w, err := o.Writer()
	if err != nil {
		return err
	}

	defer ioutil.CheckClose(w, &err)

	if _, err = fmt.Fprintf(w, "tree %s\n", b.TreeHash.String()); err != nil {
		return err
	}

	for _, parent := range b.ParentHashes {
		if _, err = fmt.Fprintf(w, "parent %s\n", parent.String()); err != nil {
			return err
		}
	}

	if _, err = fmt.Fprint(w, "author "); err != nil {
		return err
	}

	if err = b.Author.Encode(w); err != nil {
		return err
	}

	if _, err = fmt.Fprint(w, "\ncommitter "); err != nil {
		return err
	}

	if err = b.Committer.Encode(w); err != nil {
		return err
	}

	if _, err = fmt.Fprintf(w, "\n\n%s", b.Message); err != nil {
		return err
	}

	return err
}

func (c *Commit) String() string {
	return fmt.Sprintf(
		"%s %s\nAuthor: %s\nDate:   %s\n\n%s\n",
		plumbing.CommitObject, c.Hash, c.Author.String(),
		c.Author.When.Format(DateFormat), indent(c.Message),
	)
}

func indent(t string) string {
	var output []string
	for _, line := range strings.Split(t, "\n") {
		if len(line) != 0 {
			line = "    " + line
		}

		output = append(output, line)
	}

	return strings.Join(output, "\n")
}

// CommitIter is a generic closable interface for iterating over commits.
type CommitIter interface {
	Next() (*Commit, error)
	ForEach(func(*Commit) error) error
	Close()
}

// storerCommitIter provides an iterator from commits in an EncodedObjectStorer.
type storerCommitIter struct {
	storer.EncodedObjectIter
	s storer.EncodedObjectStorer
}

// NewCommitIter takes a storer.EncodedObjectStorer and a
// storer.EncodedObjectIter and returns a CommitIter that iterates over all
// commits contained in the storer.EncodedObjectIter.
//
// Any non-commit object returned by the storer.EncodedObjectIter is skipped.
func NewCommitIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) CommitIter {
	return &storerCommitIter{iter, s}
}

// Next moves the iterator to the next commit and returns a pointer to it. If
// there are no more commits, it returns io.EOF.
func (iter *storerCommitIter) Next() (*Commit, error) {
	obj, err := iter.EncodedObjectIter.Next()
	if err != nil {
		return nil, err
	}

	return DecodeCommit(iter.s, obj)
}

// ForEach call the cb function for each commit contained on this iter until
// an error appends or the end of the iter is reached. If ErrStop is sent
// the iteration is stop but no error is returned. The iterator is closed.
func (iter *storerCommitIter) ForEach(cb func(*Commit) error) error {
	return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error {
		c, err := DecodeCommit(iter.s, obj)
		if err != nil {
			return err
		}

		return cb(c)
	})
}

func (iter *storerCommitIter) Close() {
	iter.EncodedObjectIter.Close()
}
<commit_msg>plumbing/object: add Commit.FirstParent<commit_after>package object

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"

	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/plumbing/storer"
	"gopkg.in/src-d/go-git.v4/utils/ioutil"
)

// Hash represents the hash of an object
type Hash plumbing.Hash

// Commit points to a single tree, marking it as what the project looked like
// at a certain point in time. It contains meta-information about that point
// in time, such as a timestamp, the author of the changes since the last
// commit, a pointer to the previous commit(s), etc.
// http://schacon.github.io/gitbook/1_the_git_object_model.html
type Commit struct {
	// Hash of the commit object.
	Hash plumbing.Hash
	// Author is the original author of the commit.
	Author Signature
	// Committer is the one performing the commit, might be different from
	// Author.
	Committer Signature
	// Message is the commit message, contains arbitrary text.
	Message string
	// TreeHash is the hash of the root tree of the commit.
	TreeHash plumbing.Hash
	// ParentHashes are the hashes of the parent commits of the commit.
	ParentHashes []plumbing.Hash

	s storer.EncodedObjectStorer
}

// GetCommit gets a commit from an object storer and decodes it.
func GetCommit(s storer.EncodedObjectStorer, h plumbing.Hash) (*Commit, error) {
	o, err := s.EncodedObject(plumbing.CommitObject, h)
	if err != nil {
		return nil, err
	}

	return DecodeCommit(s, o)
}

// DecodeCommit decodes an encoded object into a *Commit and associates it to
// the given object storer.
func DecodeCommit(s storer.EncodedObjectStorer, o plumbing.EncodedObject) (*Commit, error) {
	c := &Commit{s: s}
	if err := c.Decode(o); err != nil {
		return nil, err
	}

	return c, nil
}

// Tree returns the Tree from the commit.
func (c *Commit) Tree() (*Tree, error) {
	return GetTree(c.s, c.TreeHash)
}

// Patch returns the Patch between the actual commit and the provided one.
func (c *Commit) Patch(to *Commit) (*Patch, error) {
	fromTree, err := c.Tree()
	if err != nil {
		return nil, err
	}

	toTree, err := to.Tree()
	if err != nil {
		return nil, err
	}

	return fromTree.Patch(toTree)
}

// Parents return a CommitIter to the parent Commits.
func (c *Commit) Parents() CommitIter {
	return NewCommitIter(c.s,
		storer.NewEncodedObjectLookupIter(c.s, plumbing.CommitObject, c.ParentHashes),
	)
}

// NumParents returns the number of parents in a commit.
func (c *Commit) NumParents() int {
	return len(c.ParentHashes)
}

var ErrNoParents = errors.New("commit has no parents")

// FirstParent returns the first parent of c.
func (c *Commit) FirstParent() (*Commit, error) {
	if len(c.ParentHashes) == 0 {
		return nil, ErrNoParents
	}
	return GetCommit(c.s, c.ParentHashes[0])
}

// File returns the file with the specified "path" in the commit and a
// nil error if the file exists. If the file does not exist, it returns
// a nil file and the ErrFileNotFound error.
func (c *Commit) File(path string) (*File, error) {
	tree, err := c.Tree()
	if err != nil {
		return nil, err
	}

	return tree.File(path)
}

// Files returns a FileIter allowing to iterate over the Tree
func (c *Commit) Files() (*FileIter, error) {
	tree, err := c.Tree()
	if err != nil {
		return nil, err
	}

	return tree.Files(), nil
}

// ID returns the object ID of the commit. The returned value will always match
// the current value of Commit.Hash.
//
// ID is present to fulfill the Object interface.
func (c *Commit) ID() plumbing.Hash {
	return c.Hash
}

// Type returns the type of object. It always returns plumbing.CommitObject.
//
// Type is present to fulfill the Object interface.
func (c *Commit) Type() plumbing.ObjectType {
	return plumbing.CommitObject
}

// Decode transforms a plumbing.EncodedObject into a Commit struct.
func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
	if o.Type() != plumbing.CommitObject {
		return ErrUnsupportedObject
	}

	c.Hash = o.Hash()

	reader, err := o.Reader()
	if err != nil {
		return err
	}
	defer ioutil.CheckClose(reader, &err)

	r := bufio.NewReader(reader)

	var message bool
	for {
		line, err := r.ReadBytes('\n')
		if err != nil && err != io.EOF {
			return err
		}

		if !message {
			line = bytes.TrimSpace(line)
			if len(line) == 0 {
				message = true
				continue
			}

			split := bytes.SplitN(line, []byte{' '}, 2)
			switch string(split[0]) {
			case "tree":
				c.TreeHash = plumbing.NewHash(string(split[1]))
			case "parent":
				c.ParentHashes = append(c.ParentHashes, plumbing.NewHash(string(split[1])))
			case "author":
				c.Author.Decode(split[1])
			case "committer":
				c.Committer.Decode(split[1])
			}
		} else {
			c.Message += string(line)
		}

		if err == io.EOF {
			return nil
		}
	}
}

// Encode transforms a Commit into a plumbing.EncodedObject.
func (b *Commit) Encode(o plumbing.EncodedObject) error {
	o.SetType(plumbing.CommitObject)
	w, err := o.Writer()
	if err != nil {
		return err
	}

	defer ioutil.CheckClose(w, &err)

	if _, err = fmt.Fprintf(w, "tree %s\n", b.TreeHash.String()); err != nil {
		return err
	}

	for _, parent := range b.ParentHashes {
		if _, err = fmt.Fprintf(w, "parent %s\n", parent.String()); err != nil {
			return err
		}
	}

	if _, err = fmt.Fprint(w, "author "); err != nil {
		return err
	}

	if err = b.Author.Encode(w); err != nil {
		return err
	}

	if _, err = fmt.Fprint(w, "\ncommitter "); err != nil {
		return err
	}

	if err = b.Committer.Encode(w); err != nil {
		return err
	}

	if _, err = fmt.Fprintf(w, "\n\n%s", b.Message); err != nil {
		return err
	}

	return err
}

func (c *Commit) String() string {
	return fmt.Sprintf(
		"%s %s\nAuthor: %s\nDate:   %s\n\n%s\n",
		plumbing.CommitObject, c.Hash, c.Author.String(),
		c.Author.When.Format(DateFormat), indent(c.Message),
	)
}

func indent(t string) string {
	var output []string
	for _, line := range strings.Split(t, "\n") {
		if len(line) != 0 {
			line = "    " + line
		}

		output = append(output, line)
	}

	return strings.Join(output, "\n")
}

// CommitIter is a generic closable interface for iterating over commits.
type CommitIter interface {
	Next() (*Commit, error)
	ForEach(func(*Commit) error) error
	Close()
}

// storerCommitIter provides an iterator from commits in an EncodedObjectStorer.
type storerCommitIter struct {
	storer.EncodedObjectIter
	s storer.EncodedObjectStorer
}

// NewCommitIter takes a storer.EncodedObjectStorer and a
// storer.EncodedObjectIter and returns a CommitIter that iterates over all
// commits contained in the storer.EncodedObjectIter.
//
// Any non-commit object returned by the storer.EncodedObjectIter is skipped.
func NewCommitIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) CommitIter {
	return &storerCommitIter{iter, s}
}

// Next moves the iterator to the next commit and returns a pointer to it. If
// there are no more commits, it returns io.EOF.
func (iter *storerCommitIter) Next() (*Commit, error) {
	obj, err := iter.EncodedObjectIter.Next()
	if err != nil {
		return nil, err
	}

	return DecodeCommit(iter.s, obj)
}

// ForEach call the cb function for each commit contained on this iter until
// an error appends or the end of the iter is reached. If ErrStop is sent
// the iteration is stop but no error is returned. The iterator is closed.
func (iter *storerCommitIter) ForEach(cb func(*Commit) error) error {
	return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error {
		c, err := DecodeCommit(iter.s, obj)
		if err != nil {
			return err
		}

		return cb(c)
	})
}

func (iter *storerCommitIter) Close() {
	iter.EncodedObjectIter.Close()
}
<|endoftext|>


<commit_before>package convert

import (
	"crypto/md5"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"github.com/suapapa/go_hangul/encoding/cp949"
	"strconv"
	"strings"
	"time"
)

func Int(val interface{}) int64 {
	if val != nil {
		switch v := val.(type) {
		case float64:
			return int64(v)
		case int64:
			return v
		default:
			ret, err := strconv.ParseInt(String(v), 10, 64)
			if err != nil {
				return 0
			} else {
				return ret
			}
		}
	}
	return 0
}

func IntWith(val interface{}, defaultValue int64) int64 {
	if val != nil {
		switch v := val.(type) {
		case float64:
			return int64(v)
		case int64:
			return v
		default:
			ret, err := strconv.ParseInt(String(v), 10, 64)
			if err != nil {
				return defaultValue
			} else {
				return ret
			}
		}
	}
	return defaultValue
}

func Float(val interface{}) float64 {
	if val != nil {
		switch v := val.(type) {
		case float64:
			return v
		case int64:
			return float64(v)
		default:
			ret, err := strconv.ParseFloat(String(v), 64)
			if err != nil {
				return 0
			} else {
				return float64(ret)
			}
		}
	}
	return 0
}

func FloatWith(val interface{}, defaultValue float64) float64 {
	if val != nil {
		switch v := val.(type) {
		case float64:
			return v
		case int64:
			return float64(v)
		default:
			ret, err := strconv.ParseFloat(String(v), 64)
			if err != nil {
				return defaultValue
			} else {
				return float64(ret)
			}
		}
	}
	return defaultValue
}

func String(val interface{}) string {
	switch v := val.(type) {
	case nil:
		return ""
	case string:
		return v
	case []byte:
		return string(v)
	case *time.Time:
		if v == nil {
			return ""
		}
		return fmt.Sprintf("%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", v.Year(), v.Month(), v.Day(), v.Hour(), v.Minute(), v.Second())
	case time.Time:
		return fmt.Sprintf("%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", v.Year(), v.Month(), v.Day(), v.Hour(), v.Minute(), v.Second())
	case float32:
		if int64(v*1000000) == int64(v)*1000000 {
			return fmt.Sprintf("%v", int64(v))
		} else {
			return fmt.Sprintf("%v", v)
		}
	case float64:
		if int64(v*1000000) == int64(v)*1000000 {
			return fmt.Sprintf("%v", int64(v))
		} else {
			return fmt.Sprintf("%v", v)
		}
	default:
		if v == nil {
			return ""
		} else {
			return fmt.Sprintf("%v", v)
		}
	}
}

func QueryString(val string) string {
	return strings.Join(strings.Split(val, "'"), "''")
}

func MD5(src string) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(src)))
}

func SHA256(src string) string {
	hasher := sha256.New()
	hasher.Write([]byte(src))
	return hex.EncodeToString(hasher.Sum(nil))
}

func Time(val interface{}) *time.Time {
	if val != nil {
		v := String(val)
		if len(v) > 0 {
			if t, err := time.Parse(time.RFC3339Nano, v); err == nil && t.Year() > 0 {
				return &t
			} else if t, err := time.Parse(time.RFC3339, v); err == nil && t.Year() > 0 {
				return &t
			} else if t, err := time.Parse("2006-01-02 15:04:05.000", v); err == nil && t.Year() > 0 {
				return &t
			} else if t, err := time.Parse("2006-01-02 15:04:05", v); err == nil && t.Year() > 0 {
				return &t
			} else if t, err := time.Parse("2006-01-02", v); err == nil && t.Year() > 0 {
				return &t
			} else if t, err := time.Parse("20060102150405", v); err == nil && t.Year() > 0 {
				return &t
			} else if t, err := time.Parse("20060102", v); err == nil && t.Year() > 0 {
				return &t
			} else if t, err := time.Parse("01-02-06", v); err == nil && t.Year() > 0 {
				return &t
			} else if t, err := time.Parse("01-02-2006", v); err == nil && t.Year() > 0 {
				return &t
			} else if t, err := time.Parse("15:04:05.000", v); err == nil {
				return &t
			} else if t, err := time.Parse("15:04:05", v); err == nil {
				return &t
			}
		}
	}
	return nil
}

func UTF8(ustr string) (str string) {
	defer func() {
		if r := recover(); r != nil {
			ustr = str
			return
		}
	}()

	bytes, err := cp949.From([]byte(ustr))
	if err != nil {
		str = ustr
	} else {
		str = string(bytes)
	}
	return
}

func EUCKR(str string) (ustr string) {
	defer func() {
		if r := recover(); r != nil {
			ustr = str
			return
		}
	}()

	ubytes, err := cp949.To([]byte(str))
	if err != nil {
		ustr = str
	} else {
		ustr = string(ubytes)
	}
	return
}
<commit_msg>change timezone if not described<commit_after>package convert

import (
	"crypto/md5"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"github.com/suapapa/go_hangul/encoding/cp949"
	"strconv"
	"strings"
	"time"
)

func Int(val interface{}) int64 {
	if val != nil {
		switch v := val.(type) {
		case float64:
			return int64(v)
		case int64:
			return v
		default:
			ret, err := strconv.ParseInt(String(v), 10, 64)
			if err != nil {
				return 0
			} else {
				return ret
			}
		}
	}
	return 0
}

func IntWith(val interface{}, defaultValue int64) int64 {
	if val != nil {
		switch v := val.(type) {
		case float64:
			return int64(v)
		case int64:
			return v
		default:
			ret, err := strconv.ParseInt(String(v), 10, 64)
			if err != nil {
				return defaultValue
			} else {
				return ret
			}
		}
	}
	return defaultValue
}

func Float(val interface{}) float64 {
	if val != nil {
		switch v := val.(type) {
		case float64:
			return v
		case int64:
			return float64(v)
		default:
			ret, err := strconv.ParseFloat(String(v), 64)
			if err != nil {
				return 0
			} else {
				return float64(ret)
			}
		}
	}
	return 0
}

func FloatWith(val interface{}, defaultValue float64) float64 {
	if val != nil {
		switch v := val.(type) {
		case float64:
			return v
		case int64:
			return float64(v)
		default:
			ret, err := strconv.ParseFloat(String(v), 64)
			if err != nil {
				return defaultValue
			} else {
				return float64(ret)
			}
		}
	}
	return defaultValue
}

func String(val interface{}) string {
	switch v := val.(type) {
	case nil:
		return ""
	case string:
		return v
	case []byte:
		return string(v)
	case *time.Time:
		if v == nil {
			return ""
		}
		return fmt.Sprintf("%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", v.Year(), v.Month(), v.Day(), v.Hour(), v.Minute(), v.Second())
	case time.Time:
		return fmt.Sprintf("%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", v.Year(), v.Month(), v.Day(), v.Hour(), v.Minute(), v.Second())
	case float32:
		if int64(v*1000000) == int64(v)*1000000 {
			return fmt.Sprintf("%v", int64(v))
		} else {
			return fmt.Sprintf("%v", v)
		}
	case float64:
		if int64(v*1000000) == int64(v)*1000000 {
			return fmt.Sprintf("%v", int64(v))
		} else {
			return fmt.Sprintf("%v", v)
		}
	default:
		if v == nil {
			return ""
		} else {
			return fmt.Sprintf("%v", v)
		}
	}
}

func QueryString(val string) string {
	return strings.Join(strings.Split(val, "'"), "''")
}

func MD5(src string) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(src)))
}

func SHA256(src string) string {
	hasher := sha256.New()
	hasher.Write([]byte(src))
	return hex.EncodeToString(hasher.Sum(nil))
}

func Time(val interface{}) *time.Time {
	if val != nil {
		v := String(val)
		if len(v) > 0 {
			if t, err := time.Parse(time.RFC3339Nano, v); err == nil && t.Year() > 0 {
				return &t
			} else if t, err := time.Parse(time.RFC3339, v); err == nil && t.Year() > 0 {
				return &t
			} else if t, err := time.Parse("2006-01-02 15:04:05.000", v); err == nil && t.Year() > 0 {
				t = t.In(time.Local)
				return &t
			} else if t, err := time.Parse("2006-01-02 15:04:05", v); err == nil && t.Year() > 0 {
				t = t.In(time.Local)
				return &t
			} else if t, err := time.Parse("2006-01-02", v); err == nil && t.Year() > 0 {
				t = t.In(time.Local)
				return &t
			} else if t, err := time.Parse("20060102150405", v); err == nil && t.Year() > 0 {
				t = t.In(time.Local)
				return &t
			} else if t, err := time.Parse("20060102", v); err == nil && t.Year() > 0 {
				t = t.In(time.Local)
				return &t
			} else if t, err := time.Parse("01-02-06", v); err == nil && t.Year() > 0 {
				t = t.In(time.Local)
				return &t
			} else if t, err := time.Parse("01-02-2006", v); err == nil && t.Year() > 0 {
				t = t.In(time.Local)
				return &t
			} else if t, err := time.Parse("15:04:05.000", v); err == nil {
				t = t.In(time.Local)
				return &t
			} else if t, err := time.Parse("15:04:05", v); err == nil {
				t = t.In(time.Local)
				return &t
			}
		}
	}
	return nil
}

func UTF8(ustr string) (str string) {
	defer func() {
		if r := recover(); r != nil {
			ustr = str
			return
		}
	}()

	bytes, err := cp949.From([]byte(ustr))
	if err != nil {
		str = ustr
	} else {
		str = string(bytes)
	}
	return
}

func EUCKR(str string) (ustr string) {
	defer func() {
		if r := recover(); r != nil {
			ustr = str
			return
		}
	}()

	ubytes, err := cp949.To([]byte(str))
	if err != nil {
		ustr = str
	} else {
		ustr = string(ubytes)
	}
	return
}
<|endoftext|>


<commit_before>package gintersect

// NonEmpty is true if the intersection of lhs and rhs matches a non-empty set of non-empty strings.
func NonEmpty(lhs string, rhs string) (bool, error) {
	g1, err := NewGlob(lhs)
	if err != nil {
		return false, err
	}

	g2, err := NewGlob(rhs)
	if err != nil {
		return false, err
	}
	// TODO(yash): Go over flow of error messages to see if they make sense.

	return intersectNormal(g1, g2), nil
}

func intersectNormal(g1, g2 Glob) bool {
	var i, j int
	for i, j = 0, 0; i < len(g1) && j < len(g2); i, j = i+1, j+1 {
		if g1[i].Flag() == FlagNone && g2[j].Flag() == FlagNone {
			if !Match(g1[i], g2[j]) {
				return false
			}
		} else {
			return intersectSpecial(g1[i:], g2[j:])
		}
	}

	if i == len(g1) && j == len(g2) {
		return true
	}

	return false
}

func intersectSpecial(g1, g2 Glob) bool {
	if g1[0].Flag() != FlagNone { // If g1 starts with a Token having a Flag.
		switch g1[0].Flag() {
		case FlagPlus:
			return intersectPlus(g1, g2)
		case FlagStar:
			return intersectStar(g1, g2)
		}
	} else { // If g2 starts with a Token having a Flag.
		switch g2[0].Flag() {
		case FlagPlus:
			return intersectPlus(g2, g1)
		case FlagStar:
			return intersectStar(g2, g1)
		}
	}

	return false
}

func intersectPlus(plussed, other Glob) bool {
	if !Match(plussed[0], other[0]) {
		return false
	}
	return intersectStar(plussed, other[1:])
}

func intersectStar(starred, other Glob) bool {
	// starToken, nextToken are the token having FlagStar and the one that follows immediately after, respectively.
	var starToken, nextToken Token

	starToken = starred[0]
	if len(starred) > 1 {
		nextToken = starred[1]
	}

	for i, t := range other {
		// Start gobbling up tokens in other while they match starToken.
		if nextToken != nil && Match(t, nextToken) {
			// When a token in other matches the token after starToken, stop gobbling and try to match the two all the way.
			allTheWay := intersectNormal(starred[1:], other[i:])
			// If they match all the way, the Globs intersect.
			if allTheWay {
				return true
			} else {
				// If they don't match all the way, then the current token from other should still match starToken.
				if !Match(t, starToken) {
					return false
				}
			}
		} else {
			// Only move forward if this token can be gobbled up by starToken.
			if !Match(t, starToken) {
				return false
			}
		}
	}

	// If there was no token following starToken, and everything from other was gobbled, the Globs intersect.
	if nextToken == nil {
		return true
	}

	// If everything from other was gobbles but there was a nextToken to match, they don't intersect.
	return false
}
<commit_msg>Trim matching prefixes and suffixes of input Globs until flags are encountered.<commit_after>package gintersect

// NonEmpty is true if the intersection of lhs and rhs matches a non-empty set of non-empty str1ngs.
func NonEmpty(lhs string, rhs string) (bool, error) {
	g1, err := NewGlob(lhs)
	if err != nil {
		return false, err
	}

	g2, err := NewGlob(rhs)
	if err != nil {
		return false, err
	}

	var match bool
	g1, g2, match = trimGlobs(g1, g2)
	if !match {
		return false, nil
	}

	return intersectNormal(g1, g2), nil
}

func trimGlobs(g1, g2 Glob) (Glob, Glob, bool) {
	var l1, r1, l2, r2 int

	for l1, l2 = 0, 0; l1 < len(g1) && l2 < len(g2) && g1[l1].Flag() == FlagNone && g2[l2].Flag() == FlagNone; l1, l2 = l1+1, l2+1 {
		if !Match(g1[l1], g2[l2]) {
			return nil, nil, false
		}
	}

	for r1, r2 = len(g1)-1, len(g2)-1; r1 >= 0 && r1 >= l1 && r2 >= 0 && r2 >= l2 && g1[r1].Flag() == FlagNone && g2[r2].Flag() == FlagNone; r1, r2 = r1-1, r2-1 {
		if !Match(g1[r1], g2[r2]) {
			return nil, nil, false
		}
	}

	return g1[l1 : r1+1], g2[l2 : r2+1], true
}

func intersectNormal(g1, g2 Glob) bool {
	var i, j int
	for i, j = 0, 0; i < len(g1) && j < len(g2); i, j = i+1, j+1 {
		if g1[i].Flag() == FlagNone && g2[j].Flag() == FlagNone {
			if !Match(g1[i], g2[j]) {
				return false
			}
		} else {
			return intersectSpecial(g1[i:], g2[j:])
		}
	}

	if i == len(g1) && j == len(g2) {
		return true
	}

	return false
}

func intersectSpecial(g1, g2 Glob) bool {
	if g1[0].Flag() != FlagNone { // If g1 starts with a Token having a Flag.
		switch g1[0].Flag() {
		case FlagPlus:
			return intersectPlus(g1, g2)
		case FlagStar:
			return intersectStar(g1, g2)
		}
	} else { // If g2 starts with a Token having a Flag.
		switch g2[0].Flag() {
		case FlagPlus:
			return intersectPlus(g2, g1)
		case FlagStar:
			return intersectStar(g2, g1)
		}
	}

	return false
}

func intersectPlus(plussed, other Glob) bool {
	if !Match(plussed[0], other[0]) {
		return false
	}
	return intersectStar(plussed, other[1:])
}

func intersectStar(starred, other Glob) bool {
	// starToken, nextToken are the token having FlagStar and the one that follows immediately after, respectively.
	var starToken, nextToken Token

	starToken = starred[0]
	if len(starred) > 1 {
		nextToken = starred[1]
	}

	for i, t := range other {
		// Start gobbl1ng up tokens in other while they match starToken.
		if nextToken != nil && Match(t, nextToken) {
			// When a token in other matches the token after starToken, stop gobbl1ng and try to match the two all the way.
			allTheWay := intersectNormal(starred[1:], other[i:])
			// If they match all the way, the Globs intersect.
			if allTheWay {
				return true
			} else {
				// If they don't match all the way, then the current token from other should still match starToken.
				if !Match(t, starToken) {
					return false
				}
			}
		} else {
			// Only move forward if this token can be gobbled up by starToken.
			if !Match(t, starToken) {
				return false
			}
		}
	}

	// If there was no token following starToken, and everything from other was gobbled, the Globs intersect.
	if nextToken == nil {
		return true
	}

	// If everything from other was gobbles but there was a nextToken to match, they don't intersect.
	return false
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"io"
	"sort"
	"strings"

	"github.com/spf13/cobra"
	kapi "k8s.io/kubernetes/pkg/api"
	kerrors "k8s.io/kubernetes/pkg/api/errors"
	kclient "k8s.io/kubernetes/pkg/client/unversioned"
	kubectl "k8s.io/kubernetes/pkg/kubectl"
	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/resource"
	"k8s.io/kubernetes/pkg/runtime"

	latest "github.com/openshift/origin/pkg/api/latest"
	"github.com/openshift/origin/pkg/client"
	describe "github.com/openshift/origin/pkg/cmd/cli/describe"
	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
	deployapi "github.com/openshift/origin/pkg/deploy/api"
	deployutil "github.com/openshift/origin/pkg/deploy/util"
)

const (
	rollbackLong = `
Revert an application back to a previous deployment

When you run this command your deployment configuration will be updated to
match a previous deployment. By default only the pod and container
configuration will be changed and scaling or trigger settings will be left as-
is. Note that environment variables and volumes are included in rollbacks, so
if you've recently updated security credentials in your environment your
previous deployment may not have the correct values.

Any image triggers present in the rolled back configuration will be disabled
with a warning. This is to help prevent your rolled back deployment from being
replaced by a triggered deployment soon after your rollback. To re-enable the
triggers, use the 'deploy' command.

If you would like to review the outcome of the rollback, pass '--dry-run' to print
a human-readable representation of the updated deployment configuration instead of
executing the rollback. This is useful if you're not quite sure what the outcome
will be.`

	rollbackExample = `  # Perform a rollback to the last successfully completed deployment for a deploymentconfig
  %[1]s rollback frontend

  # See what a rollback to version 3 will look like, but don't perform the rollback
  %[1]s rollback frontend --to-version=3 --dry-run

  # Perform a rollback to a specific deployment
  %[1]s rollback frontend-2

  # Perform the rollback manually by piping the JSON of the new config back to %[1]s
  %[1]s rollback frontend -o json | %[1]s replace dc/frontend -f -`
)

// NewCmdRollback creates a CLI rollback command.
func NewCmdRollback(fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
	opts := &RollbackOptions{}
	cmd := &cobra.Command{
		Use:     "rollback (DEPLOYMENTCONFIG | DEPLOYMENT)",
		Short:   "Revert part of an application back to a previous deployment",
		Long:    rollbackLong,
		Example: fmt.Sprintf(rollbackExample, fullName),
		Run: func(cmd *cobra.Command, args []string) {
			if err := opts.Complete(f, args, out); err != nil {
				kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error()))
			}

			if err := opts.Validate(); err != nil {
				kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error()))
			}

			if err := opts.Run(); err != nil {
				kcmdutil.CheckErr(err)
			}
		},
	}

	cmd.Flags().BoolVar(&opts.IncludeTriggers, "change-triggers", false, "Include the previous deployment's triggers in the rollback")
	cmd.Flags().BoolVar(&opts.IncludeStrategy, "change-strategy", false, "Include the previous deployment's strategy in the rollback")
	cmd.Flags().BoolVar(&opts.IncludeScalingSettings, "change-scaling-settings", false, "Include the previous deployment's replicationController replica count and selector in the rollback")
	cmd.Flags().BoolVarP(&opts.DryRun, "dry-run", "d", false, "Instead of performing the rollback, describe what the rollback will look like in human-readable form")
	cmd.Flags().StringVarP(&opts.Format, "output", "o", "", "Instead of performing the rollback, print the updated deployment configuration in the specified format (json|yaml|name|template|templatefile)")
	cmd.Flags().StringVarP(&opts.Template, "template", "t", "", "Template string or path to template file to use when -o=template or -o=templatefile.")
	cmd.MarkFlagFilename("template")
	cmd.Flags().Int64Var(&opts.DesiredVersion, "to-version", 0, "A config version to rollback to. Specifying version 0 is the same as omitting a version (the version will be auto-detected). This option is ignored when specifying a deployment.")

	return cmd
}

// RollbackOptions contains all the necessary state to perform a rollback.
type RollbackOptions struct {
	Namespace              string
	TargetName             string
	DesiredVersion         int64
	Format                 string
	Template               string
	DryRun                 bool
	IncludeTriggers        bool
	IncludeStrategy        bool
	IncludeScalingSettings bool

	// out is a place to write user-facing output.
	out io.Writer
	// oc is an openshift client.
	oc client.Interface
	// kc is a kube client.
	kc kclient.Interface
	// getBuilder returns a new builder each time it is called. A
	// resource.Builder is stateful and isn't safe to reuse (e.g. across
	// resource types).
	getBuilder func() *resource.Builder
}

// Complete turns a partially defined RollbackActions into a solvent structure
// which can be validated and used for a rollback.
func (o *RollbackOptions) Complete(f *clientcmd.Factory, args []string, out io.Writer) error {
	// Extract basic flags.
	if len(args) == 1 {
		o.TargetName = args[0]
	}
	namespace, _, err := f.DefaultNamespace()
	if err != nil {
		return err
	}
	o.Namespace = namespace

	// Set up client based support.
	mapper, typer := f.Object(false)
	o.getBuilder = func() *resource.Builder {
		return resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), kapi.Codecs.UniversalDecoder())
	}

	oClient, kClient, err := f.Clients()
	if err != nil {
		return err
	}
	o.oc = oClient
	o.kc = kClient

	o.out = out
	return nil
}

// Validate ensures that a RollbackOptions is valid and can be used to execute
// a rollback.
func (o *RollbackOptions) Validate() error {
	if len(o.TargetName) == 0 {
		return fmt.Errorf("a deployment or deployment config name is required")
	}
	if o.DesiredVersion < 0 {
		return fmt.Errorf("the to version must be >= 0")
	}
	if o.out == nil {
		return fmt.Errorf("out must not be nil")
	}
	if o.oc == nil {
		return fmt.Errorf("oc must not be nil")
	}
	if o.kc == nil {
		return fmt.Errorf("kc must not be nil")
	}
	if o.getBuilder == nil {
		return fmt.Errorf("getBuilder must not be nil")
	} else {
		b := o.getBuilder()
		if b == nil {
			return fmt.Errorf("getBuilder must return a resource.Builder")
		}
	}
	return nil
}

// Run performs a rollback.
func (o *RollbackOptions) Run() error {
	// Get the resource referenced in the command args.
	obj, err := o.findResource(o.TargetName)
	if err != nil {
		return err
	}

	// Interpret the resource to resolve a target for rollback.
	var target *kapi.ReplicationController
	switch r := obj.(type) {
	case *kapi.ReplicationController:
		dcName := deployutil.DeploymentConfigNameFor(r)
		dc, err := o.oc.DeploymentConfigs(r.Namespace).Get(dcName)
		if err != nil {
			return err
		}
		if dc.Spec.Paused {
			return fmt.Errorf("cannot rollback a paused deployment config")
		}

		// A specific deployment was used.
		target = r
	case *deployapi.DeploymentConfig:
		if r.Spec.Paused {
			return fmt.Errorf("cannot rollback a paused deployment config")
		}
		// A deploymentconfig was used. Find the target deployment by the
		// specified version, or by a lookup of the last completed deployment if
		// no version was supplied.
		deployment, err := o.findTargetDeployment(r, o.DesiredVersion)
		if err != nil {
			return err
		}
		target = deployment
	}
	if target == nil {
		return fmt.Errorf("%s is not a valid deployment or deployment config", o.TargetName)
	}

	// Set up the rollback and generate a new rolled back config.
	rollback := &deployapi.DeploymentConfigRollback{
		Spec: deployapi.DeploymentConfigRollbackSpec{
			From: kapi.ObjectReference{
				Name: target.Name,
			},
			IncludeTemplate:        true,
			IncludeTriggers:        o.IncludeTriggers,
			IncludeStrategy:        o.IncludeStrategy,
			IncludeReplicationMeta: o.IncludeScalingSettings,
		},
	}
	newConfig, err := o.oc.DeploymentConfigs(o.Namespace).RollbackDeprecated(rollback)
	if err != nil {
		return err
	}

	// If this is a dry run, print and exit.
	if o.DryRun {
		describer := describe.NewDeploymentConfigDescriber(o.oc, o.kc, newConfig)
		description, err := describer.Describe(newConfig.Namespace, newConfig.Name, kubectl.DescriberSettings{})
		if err != nil {
			return err
		}
		o.out.Write([]byte(description))
		return nil
	}

	// If an output format is specified, print and exit.
	if len(o.Format) > 0 {
		printer, _, err := kubectl.GetPrinter(o.Format, o.Template)
		if err != nil {
			return err
		}
		versionedPrinter := kubectl.NewVersionedPrinter(printer, kapi.Scheme, latest.Version)
		versionedPrinter.PrintObj(newConfig, o.out)
		return nil
	}

	// Perform a real rollback.
	rolledback, err := o.oc.DeploymentConfigs(newConfig.Namespace).Update(newConfig)
	if err != nil {
		return err
	}

	// Print warnings about any image triggers disabled during the rollback.
	fmt.Fprintf(o.out, "#%d rolled back to %s\n", rolledback.Status.LatestVersion, rollback.Spec.From.Name)
	for _, trigger := range rolledback.Spec.Triggers {
		disabled := []string{}
		if trigger.Type == deployapi.DeploymentTriggerOnImageChange && !trigger.ImageChangeParams.Automatic {
			disabled = append(disabled, trigger.ImageChangeParams.From.Name)
		}
		if len(disabled) > 0 {
			reenable := fmt.Sprintf("oc deploy %s --enable-triggers -n %s", rolledback.Name, o.Namespace)
			fmt.Fprintf(o.out, "Warning: the following images triggers were disabled: %s\n  You can re-enable them with: %s\n", strings.Join(disabled, ","), reenable)
		}
	}

	return nil
}

// findResource tries to find a deployment or deploymentconfig named
// targetName using a resource.Builder. For compatibility, if the resource
// name is unprefixed, treat it as an rc first and a dc second.
func (o *RollbackOptions) findResource(targetName string) (runtime.Object, error) {
	candidates := []string{}
	if strings.Index(targetName, "/") == -1 {
		candidates = append(candidates, "rc/"+targetName)
		candidates = append(candidates, "dc/"+targetName)
	} else {
		candidates = append(candidates, targetName)
	}
	var obj runtime.Object
	for _, name := range candidates {
		r := o.getBuilder().
			NamespaceParam(o.Namespace).
			ResourceTypeOrNameArgs(false, name).
			SingleResourceType().
			Do()
		if r.Err() != nil {
			return nil, r.Err()
		}
		resultObj, err := r.Object()
		if err != nil {
			// If the resource wasn't found, try another candidate.
			if kerrors.IsNotFound(err) {
				continue
			}
			return nil, err
		}
		obj = resultObj
		break
	}
	if obj == nil {
		return nil, fmt.Errorf("%s is not a valid deployment or deployment config", targetName)
	}
	return obj, nil
}

// findTargetDeployment finds the deployment which is the rollback target by
// searching for deployments associated with config. If desiredVersion is >0,
// the deployment matching desiredVersion will be returned. If desiredVersion
// is <=0, the last completed deployment which is older than the config's
// version will be returned.
func (o *RollbackOptions) findTargetDeployment(config *deployapi.DeploymentConfig, desiredVersion int64) (*kapi.ReplicationController, error) {
	// Find deployments for the config sorted by version descending.
	deployments, err := o.kc.ReplicationControllers(config.Namespace).List(kapi.ListOptions{LabelSelector: deployutil.ConfigSelector(config.Name)})
	if err != nil {
		return nil, err
	}
	sort.Sort(deployutil.ByLatestVersionDesc(deployments.Items))

	// Find the target deployment for rollback. If a version was specified,
	// use the version for a search. Otherwise, use the last completed
	// deployment.
	var target *kapi.ReplicationController
	for _, deployment := range deployments.Items {
		version := deployutil.DeploymentVersionFor(&deployment)
		if desiredVersion > 0 {
			if version == desiredVersion {
				target = &deployment
				break
			}
		} else {
			if version < config.Status.LatestVersion && deployutil.DeploymentStatusFor(&deployment) == deployapi.DeploymentStatusComplete {
				target = &deployment
				break
			}
		}
	}
	if target == nil {
		return nil, fmt.Errorf("couldn't find deployment for rollback")
	}
	return target, nil
}
<commit_msg>oc: make rollback use both paths for rolling back<commit_after>package cmd

import (
	"fmt"
	"io"
	"sort"
	"strings"

	"github.com/spf13/cobra"
	kapi "k8s.io/kubernetes/pkg/api"
	kerrors "k8s.io/kubernetes/pkg/api/errors"
	kclient "k8s.io/kubernetes/pkg/client/unversioned"
	kubectl "k8s.io/kubernetes/pkg/kubectl"
	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/resource"
	"k8s.io/kubernetes/pkg/runtime"

	latest "github.com/openshift/origin/pkg/api/latest"
	"github.com/openshift/origin/pkg/client"
	describe "github.com/openshift/origin/pkg/cmd/cli/describe"
	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
	deployapi "github.com/openshift/origin/pkg/deploy/api"
	deployutil "github.com/openshift/origin/pkg/deploy/util"
)

const (
	rollbackLong = `
Revert an application back to a previous deployment

When you run this command your deployment configuration will be updated to
match a previous deployment. By default only the pod and container
configuration will be changed and scaling or trigger settings will be left as-
is. Note that environment variables and volumes are included in rollbacks, so
if you've recently updated security credentials in your environment your
previous deployment may not have the correct values.

Any image triggers present in the rolled back configuration will be disabled
with a warning. This is to help prevent your rolled back deployment from being
replaced by a triggered deployment soon after your rollback. To re-enable the
triggers, use the 'deploy' command.

If you would like to review the outcome of the rollback, pass '--dry-run' to print
a human-readable representation of the updated deployment configuration instead of
executing the rollback. This is useful if you're not quite sure what the outcome
will be.`

	rollbackExample = `  # Perform a rollback to the last successfully completed deployment for a deploymentconfig
  %[1]s rollback frontend

  # See what a rollback to version 3 will look like, but don't perform the rollback
  %[1]s rollback frontend --to-version=3 --dry-run

  # Perform a rollback to a specific deployment
  %[1]s rollback frontend-2

  # Perform the rollback manually by piping the JSON of the new config back to %[1]s
  %[1]s rollback frontend -o json | %[1]s replace dc/frontend -f -`
)

// NewCmdRollback creates a CLI rollback command.
func NewCmdRollback(fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
	opts := &RollbackOptions{}
	cmd := &cobra.Command{
		Use:     "rollback (DEPLOYMENTCONFIG | DEPLOYMENT)",
		Short:   "Revert part of an application back to a previous deployment",
		Long:    rollbackLong,
		Example: fmt.Sprintf(rollbackExample, fullName),
		Run: func(cmd *cobra.Command, args []string) {
			if err := opts.Complete(f, args, out); err != nil {
				kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error()))
			}

			if err := opts.Validate(); err != nil {
				kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error()))
			}

			if err := opts.Run(); err != nil {
				kcmdutil.CheckErr(err)
			}
		},
	}

	cmd.Flags().BoolVar(&opts.IncludeTriggers, "change-triggers", false, "Include the previous deployment's triggers in the rollback")
	cmd.Flags().BoolVar(&opts.IncludeStrategy, "change-strategy", false, "Include the previous deployment's strategy in the rollback")
	cmd.Flags().BoolVar(&opts.IncludeScalingSettings, "change-scaling-settings", false, "Include the previous deployment's replicationController replica count and selector in the rollback")
	cmd.Flags().BoolVarP(&opts.DryRun, "dry-run", "d", false, "Instead of performing the rollback, describe what the rollback will look like in human-readable form")
	cmd.Flags().StringVarP(&opts.Format, "output", "o", "", "Instead of performing the rollback, print the updated deployment configuration in the specified format (json|yaml|name|template|templatefile)")
	cmd.Flags().StringVarP(&opts.Template, "template", "t", "", "Template string or path to template file to use when -o=template or -o=templatefile.")
	cmd.MarkFlagFilename("template")
	cmd.Flags().Int64Var(&opts.DesiredVersion, "to-version", 0, "A config version to rollback to. Specifying version 0 is the same as omitting a version (the version will be auto-detected). This option is ignored when specifying a deployment.")

	return cmd
}

// RollbackOptions contains all the necessary state to perform a rollback.
type RollbackOptions struct {
	Namespace              string
	TargetName             string
	DesiredVersion         int64
	Format                 string
	Template               string
	DryRun                 bool
	IncludeTriggers        bool
	IncludeStrategy        bool
	IncludeScalingSettings bool

	// out is a place to write user-facing output.
	out io.Writer
	// oc is an openshift client.
	oc client.Interface
	// kc is a kube client.
	kc kclient.Interface
	// getBuilder returns a new builder each time it is called. A
	// resource.Builder is stateful and isn't safe to reuse (e.g. across
	// resource types).
	getBuilder func() *resource.Builder
}

// Complete turns a partially defined RollbackActions into a solvent structure
// which can be validated and used for a rollback.
func (o *RollbackOptions) Complete(f *clientcmd.Factory, args []string, out io.Writer) error {
	// Extract basic flags.
	if len(args) == 1 {
		o.TargetName = args[0]
	}
	namespace, _, err := f.DefaultNamespace()
	if err != nil {
		return err
	}
	o.Namespace = namespace

	// Set up client based support.
	mapper, typer := f.Object(false)
	o.getBuilder = func() *resource.Builder {
		return resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), kapi.Codecs.UniversalDecoder())
	}

	oClient, kClient, err := f.Clients()
	if err != nil {
		return err
	}
	o.oc = oClient
	o.kc = kClient

	o.out = out
	return nil
}

// Validate ensures that a RollbackOptions is valid and can be used to execute
// a rollback.
func (o *RollbackOptions) Validate() error {
	if len(o.TargetName) == 0 {
		return fmt.Errorf("a deployment or deployment config name is required")
	}
	if o.DesiredVersion < 0 {
		return fmt.Errorf("the to version must be >= 0")
	}
	if o.out == nil {
		return fmt.Errorf("out must not be nil")
	}
	if o.oc == nil {
		return fmt.Errorf("oc must not be nil")
	}
	if o.kc == nil {
		return fmt.Errorf("kc must not be nil")
	}
	if o.getBuilder == nil {
		return fmt.Errorf("getBuilder must not be nil")
	} else {
		b := o.getBuilder()
		if b == nil {
			return fmt.Errorf("getBuilder must return a resource.Builder")
		}
	}
	return nil
}

// Run performs a rollback.
func (o *RollbackOptions) Run() error {
	// Get the resource referenced in the command args.
	obj, err := o.findResource(o.TargetName)
	if err != nil {
		return err
	}

	configName := ""

	// Interpret the resource to resolve a target for rollback.
	var target *kapi.ReplicationController
	switch r := obj.(type) {
	case *kapi.ReplicationController:
		dcName := deployutil.DeploymentConfigNameFor(r)
		dc, err := o.oc.DeploymentConfigs(r.Namespace).Get(dcName)
		if err != nil {
			return err
		}
		if dc.Spec.Paused {
			return fmt.Errorf("cannot rollback a paused deployment config")
		}

		// A specific deployment was used.
		target = r
		configName = deployutil.DeploymentConfigNameFor(obj)
	case *deployapi.DeploymentConfig:
		if r.Spec.Paused {
			return fmt.Errorf("cannot rollback a paused deployment config")
		}
		// A deploymentconfig was used. Find the target deployment by the
		// specified version, or by a lookup of the last completed deployment if
		// no version was supplied.
		deployment, err := o.findTargetDeployment(r, o.DesiredVersion)
		if err != nil {
			return err
		}
		target = deployment
		configName = r.Name
	}
	if target == nil {
		return fmt.Errorf("%s is not a valid deployment or deployment config", o.TargetName)
	}

	// Set up the rollback and generate a new rolled back config.
	rollback := &deployapi.DeploymentConfigRollback{
		Name: configName,
		Spec: deployapi.DeploymentConfigRollbackSpec{
			From: kapi.ObjectReference{
				Name: target.Name,
			},
			Revision:               int64(o.DesiredVersion),
			IncludeTemplate:        true,
			IncludeTriggers:        o.IncludeTriggers,
			IncludeStrategy:        o.IncludeStrategy,
			IncludeReplicationMeta: o.IncludeScalingSettings,
		},
	}
	newConfig, err := o.oc.DeploymentConfigs(o.Namespace).Rollback(rollback)
	if kerrors.IsNotFound(err) {
		// Fallback to the old path for new clients talking to old servers.
		newConfig, err = o.oc.DeploymentConfigs(o.Namespace).RollbackDeprecated(rollback)
	}
	if err != nil {
		return err
	}

	// If this is a dry run, print and exit.
	if o.DryRun {
		describer := describe.NewDeploymentConfigDescriber(o.oc, o.kc, newConfig)
		description, err := describer.Describe(newConfig.Namespace, newConfig.Name, kubectl.DescriberSettings{})
		if err != nil {
			return err
		}
		o.out.Write([]byte(description))
		return nil
	}

	// If an output format is specified, print and exit.
	if len(o.Format) > 0 {
		printer, _, err := kubectl.GetPrinter(o.Format, o.Template)
		if err != nil {
			return err
		}
		versionedPrinter := kubectl.NewVersionedPrinter(printer, kapi.Scheme, latest.Version)
		versionedPrinter.PrintObj(newConfig, o.out)
		return nil
	}

	// Perform a real rollback.
	rolledback, err := o.oc.DeploymentConfigs(newConfig.Namespace).Update(newConfig)
	if err != nil {
		return err
	}

	// Print warnings about any image triggers disabled during the rollback.
	fmt.Fprintf(o.out, "#%d rolled back to %s\n", rolledback.Status.LatestVersion, rollback.Spec.From.Name)
	for _, trigger := range rolledback.Spec.Triggers {
		disabled := []string{}
		if trigger.Type == deployapi.DeploymentTriggerOnImageChange && !trigger.ImageChangeParams.Automatic {
			disabled = append(disabled, trigger.ImageChangeParams.From.Name)
		}
		if len(disabled) > 0 {
			reenable := fmt.Sprintf("oc deploy %s --enable-triggers -n %s", rolledback.Name, o.Namespace)
			fmt.Fprintf(o.out, "Warning: the following images triggers were disabled: %s\n  You can re-enable them with: %s\n", strings.Join(disabled, ","), reenable)
		}
	}

	return nil
}

// findResource tries to find a deployment or deploymentconfig named
// targetName using a resource.Builder. For compatibility, if the resource
// name is unprefixed, treat it as an rc first and a dc second.
func (o *RollbackOptions) findResource(targetName string) (runtime.Object, error) {
	candidates := []string{}
	if strings.Index(targetName, "/") == -1 {
		candidates = append(candidates, "rc/"+targetName)
		candidates = append(candidates, "dc/"+targetName)
	} else {
		candidates = append(candidates, targetName)
	}
	var obj runtime.Object
	for _, name := range candidates {
		r := o.getBuilder().
			NamespaceParam(o.Namespace).
			ResourceTypeOrNameArgs(false, name).
			SingleResourceType().
			Do()
		if r.Err() != nil {
			return nil, r.Err()
		}
		resultObj, err := r.Object()
		if err != nil {
			// If the resource wasn't found, try another candidate.
			if kerrors.IsNotFound(err) {
				continue
			}
			return nil, err
		}
		obj = resultObj
		break
	}
	if obj == nil {
		return nil, fmt.Errorf("%s is not a valid deployment or deployment config", targetName)
	}
	return obj, nil
}

// findTargetDeployment finds the deployment which is the rollback target by
// searching for deployments associated with config. If desiredVersion is >0,
// the deployment matching desiredVersion will be returned. If desiredVersion
// is <=0, the last completed deployment which is older than the config's
// version will be returned.
func (o *RollbackOptions) findTargetDeployment(config *deployapi.DeploymentConfig, desiredVersion int64) (*kapi.ReplicationController, error) {
	// Find deployments for the config sorted by version descending.
	deployments, err := o.kc.ReplicationControllers(config.Namespace).List(kapi.ListOptions{LabelSelector: deployutil.ConfigSelector(config.Name)})
	if err != nil {
		return nil, err
	}
	sort.Sort(deployutil.ByLatestVersionDesc(deployments.Items))

	// Find the target deployment for rollback. If a version was specified,
	// use the version for a search. Otherwise, use the last completed
	// deployment.
	var target *kapi.ReplicationController
	for _, deployment := range deployments.Items {
		version := deployutil.DeploymentVersionFor(&deployment)
		if desiredVersion > 0 {
			if version == desiredVersion {
				target = &deployment
				break
			}
		} else {
			if version < config.Status.LatestVersion && deployutil.DeploymentStatusFor(&deployment) == deployapi.DeploymentStatusComplete {
				target = &deployment
				break
			}
		}
	}
	if target == nil {
		return nil, fmt.Errorf("couldn't find deployment for rollback")
	}
	return target, nil
}
<|endoftext|>


<commit_before>package config

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

	"github.com/containers/image/types"
	helperclient "github.com/docker/docker-credential-helpers/client"
	"github.com/docker/docker-credential-helpers/credentials"
	"github.com/docker/docker/pkg/homedir"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type dockerAuthConfig struct {
	Auth string `json:"auth,omitempty"`
}

type dockerConfigFile struct {
	AuthConfigs map[string]dockerAuthConfig `json:"auths"`
	CredHelpers map[string]string           `json:"credHelpers,omitempty"`
}

var (
	defaultPerUIDPathFormat = filepath.FromSlash("/run/containers/%d/auth.json")
	xdgRuntimeDirPath       = filepath.FromSlash("containers/auth.json")
	dockerHomePath          = filepath.FromSlash(".docker/config.json")
	dockerLegacyHomePath    = ".dockercfg"

	// ErrNotLoggedIn is returned for users not logged into a registry
	// that they are trying to logout of
	ErrNotLoggedIn = errors.New("not logged in")
)

// SetAuthentication stores the username and password in the auth.json file
func SetAuthentication(sys *types.SystemContext, registry, username, password string) error {
	return modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) {
		if ch, exists := auths.CredHelpers[registry]; exists {
			return false, setAuthToCredHelper(ch, registry, username, password)
		}

		creds := base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
		newCreds := dockerAuthConfig{Auth: creds}
		auths.AuthConfigs[registry] = newCreds
		return true, nil
	})
}

// GetAuthentication returns the registry credentials stored in
// either auth.json file or .docker/config.json
// If an entry is not found empty strings are returned for the username and password
func GetAuthentication(sys *types.SystemContext, registry string) (string, string, error) {
	if sys != nil && sys.DockerAuthConfig != nil {
		return sys.DockerAuthConfig.Username, sys.DockerAuthConfig.Password, nil
	}

	dockerLegacyPath := filepath.Join(homedir.Get(), dockerLegacyHomePath)
	var paths []string
	pathToAuth, err := getPathToAuth(sys)
	if err == nil {
		paths = append(paths, pathToAuth)
	} else {
		// Error means that the path set for XDG_RUNTIME_DIR does not exist
		// but we don't want to completely fail in the case that the user is pulling a public image
		// Logging the error as a warning instead and moving on to pulling the image
		logrus.Warnf("%v: Trying to pull image in the event that it is a public image.", err)
	}
	paths = append(paths, filepath.Join(homedir.Get(), dockerHomePath), dockerLegacyPath)

	for _, path := range paths {
		legacyFormat := path == dockerLegacyPath
		username, password, err := findAuthentication(registry, path, legacyFormat)
		if err != nil {
			return "", "", err
		}
		if username != "" && password != "" {
			return username, password, nil
		}
	}
	return "", "", nil
}

// GetUserLoggedIn returns the username logged in to registry from either
// auth.json or XDG_RUNTIME_DIR
// Used to tell the user if someone is logged in to the registry when logging in
func GetUserLoggedIn(sys *types.SystemContext, registry string) (string, error) {
	path, err := getPathToAuth(sys)
	if err != nil {
		return "", err
	}
	username, _, _ := findAuthentication(registry, path, false)
	if username != "" {
		return username, nil
	}
	return "", nil
}

// RemoveAuthentication deletes the credentials stored in auth.json
func RemoveAuthentication(sys *types.SystemContext, registry string) error {
	return modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) {
		// First try cred helpers.
		if ch, exists := auths.CredHelpers[registry]; exists {
			return false, deleteAuthFromCredHelper(ch, registry)
		}

		if _, ok := auths.AuthConfigs[registry]; ok {
			delete(auths.AuthConfigs, registry)
		} else if _, ok := auths.AuthConfigs[normalizeRegistry(registry)]; ok {
			delete(auths.AuthConfigs, normalizeRegistry(registry))
		} else {
			return false, ErrNotLoggedIn
		}
		return true, nil
	})
}

// RemoveAllAuthentication deletes all the credentials stored in auth.json
func RemoveAllAuthentication(sys *types.SystemContext) error {
	return modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) {
		auths.CredHelpers = make(map[string]string)
		auths.AuthConfigs = make(map[string]dockerAuthConfig)
		return true, nil
	})
}

// getPath gets the path of the auth.json file
// The path can be overriden by the user if the overwrite-path flag is set
// If the flag is not set and XDG_RUNTIME_DIR is set, the auth.json file is saved in XDG_RUNTIME_DIR/containers
// Otherwise, the auth.json file is stored in /run/containers/UID
func getPathToAuth(sys *types.SystemContext) (string, error) {
	if sys != nil {
		if sys.AuthFilePath != "" {
			return sys.AuthFilePath, nil
		}
		if sys.RootForImplicitAbsolutePaths != "" {
			return filepath.Join(sys.RootForImplicitAbsolutePaths, fmt.Sprintf(defaultPerUIDPathFormat, os.Getuid())), nil
		}
	}

	runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
	if runtimeDir != "" {
		// This function does not in general need to separately check that the returned path exists; that’s racy, and callers will fail accessing the file anyway.
		// We are checking for os.IsNotExist here only to give the user better guidance what to do in this special case.
		_, err := os.Stat(runtimeDir)
		if os.IsNotExist(err) {
			// This means the user set the XDG_RUNTIME_DIR variable and either forgot to create the directory
			// or made a typo while setting the environment variable,
			// so return an error referring to $XDG_RUNTIME_DIR instead of xdgRuntimeDirPath inside.
			return "", errors.Wrapf(err, "%q directory set by $XDG_RUNTIME_DIR does not exist. Either create the directory or unset $XDG_RUNTIME_DIR.", runtimeDir)
		} // else ignore err and let the caller fail accessing xdgRuntimeDirPath.
		return filepath.Join(runtimeDir, xdgRuntimeDirPath), nil
	}
	return fmt.Sprintf(defaultPerUIDPathFormat, os.Getuid()), nil
}

// readJSONFile unmarshals the authentications stored in the auth.json file and returns it
// or returns an empty dockerConfigFile data structure if auth.json does not exist
// if the file exists and is empty, readJSONFile returns an error
func readJSONFile(path string, legacyFormat bool) (dockerConfigFile, error) {
	var auths dockerConfigFile

	raw, err := ioutil.ReadFile(path)
	if os.IsNotExist(err) {
		auths.AuthConfigs = map[string]dockerAuthConfig{}
		return auths, nil
	}

	if legacyFormat {
		if err = json.Unmarshal(raw, &auths.AuthConfigs); err != nil {
			return dockerConfigFile{}, errors.Wrapf(err, "error unmarshaling JSON at %q", path)
		}
		return auths, nil
	}

	if err = json.Unmarshal(raw, &auths); err != nil {
		return dockerConfigFile{}, errors.Wrapf(err, "error unmarshaling JSON at %q", path)
	}

	return auths, nil
}

// modifyJSON writes to auth.json if the dockerConfigFile has been updated
func modifyJSON(sys *types.SystemContext, editor func(auths *dockerConfigFile) (bool, error)) error {
	path, err := getPathToAuth(sys)
	if err != nil {
		return err
	}

	dir := filepath.Dir(path)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err = os.MkdirAll(dir, 0700); err != nil {
			return errors.Wrapf(err, "error creating directory %q", dir)
		}
	}

	auths, err := readJSONFile(path, false)
	if err != nil {
		return errors.Wrapf(err, "error reading JSON file %q", path)
	}

	updated, err := editor(&auths)
	if err != nil {
		return errors.Wrapf(err, "error updating %q", path)
	}
	if updated {
		newData, err := json.MarshalIndent(auths, "", "\t")
		if err != nil {
			return errors.Wrapf(err, "error marshaling JSON %q", path)
		}

		if err = ioutil.WriteFile(path, newData, 0755); err != nil {
			return errors.Wrapf(err, "error writing to file %q", path)
		}
	}

	return nil
}

func getAuthFromCredHelper(credHelper, registry string) (string, string, error) {
	helperName := fmt.Sprintf("docker-credential-%s", credHelper)
	p := helperclient.NewShellProgramFunc(helperName)
	creds, err := helperclient.Get(p, registry)
	if err != nil {
		return "", "", err
	}
	return creds.Username, creds.Secret, nil
}

func setAuthToCredHelper(credHelper, registry, username, password string) error {
	helperName := fmt.Sprintf("docker-credential-%s", credHelper)
	p := helperclient.NewShellProgramFunc(helperName)
	creds := &credentials.Credentials{
		ServerURL: registry,
		Username:  username,
		Secret:    password,
	}
	return helperclient.Store(p, creds)
}

func deleteAuthFromCredHelper(credHelper, registry string) error {
	helperName := fmt.Sprintf("docker-credential-%s", credHelper)
	p := helperclient.NewShellProgramFunc(helperName)
	return helperclient.Erase(p, registry)
}

// findAuthentication looks for auth of registry in path
func findAuthentication(registry, path string, legacyFormat bool) (string, string, error) {
	auths, err := readJSONFile(path, legacyFormat)
	if err != nil {
		return "", "", errors.Wrapf(err, "error reading JSON file %q", path)
	}

	// First try cred helpers. They should always be normalized.
	if ch, exists := auths.CredHelpers[registry]; exists {
		return getAuthFromCredHelper(ch, registry)
	}

	// I'm feeling lucky
	if val, exists := auths.AuthConfigs[registry]; exists {
		return decodeDockerAuth(val.Auth)
	}

	// bad luck; let's normalize the entries first
	registry = normalizeRegistry(registry)
	normalizedAuths := map[string]dockerAuthConfig{}
	for k, v := range auths.AuthConfigs {
		normalizedAuths[normalizeRegistry(k)] = v
	}
	if val, exists := normalizedAuths[registry]; exists {
		return decodeDockerAuth(val.Auth)
	}
	return "", "", nil
}

func decodeDockerAuth(s string) (string, string, error) {
	decoded, err := base64.StdEncoding.DecodeString(s)
	if err != nil {
		return "", "", err
	}
	parts := strings.SplitN(string(decoded), ":", 2)
	if len(parts) != 2 {
		// if it's invalid just skip, as docker does
		return "", "", nil
	}
	user := parts[0]
	password := strings.Trim(parts[1], "\x00")
	return user, password, nil
}

// convertToHostname converts a registry url which has http|https prepended
// to just an hostname.
// Copied from github.com/docker/docker/registry/auth.go
func convertToHostname(url string) string {
	stripped := url
	if strings.HasPrefix(url, "http://") {
		stripped = strings.TrimPrefix(url, "http://")
	} else if strings.HasPrefix(url, "https://") {
		stripped = strings.TrimPrefix(url, "https://")
	}

	nameParts := strings.SplitN(stripped, "/", 2)

	return nameParts[0]
}

func normalizeRegistry(registry string) string {
	normalized := convertToHostname(registry)
	switch normalized {
	case "registry-1.docker.io", "docker.io":
		return "index.docker.io"
	}
	return normalized
}
<commit_msg>Don't ignore errors reading pkg/docker/config config files<commit_after>package config

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

	"github.com/containers/image/types"
	helperclient "github.com/docker/docker-credential-helpers/client"
	"github.com/docker/docker-credential-helpers/credentials"
	"github.com/docker/docker/pkg/homedir"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type dockerAuthConfig struct {
	Auth string `json:"auth,omitempty"`
}

type dockerConfigFile struct {
	AuthConfigs map[string]dockerAuthConfig `json:"auths"`
	CredHelpers map[string]string           `json:"credHelpers,omitempty"`
}

var (
	defaultPerUIDPathFormat = filepath.FromSlash("/run/containers/%d/auth.json")
	xdgRuntimeDirPath       = filepath.FromSlash("containers/auth.json")
	dockerHomePath          = filepath.FromSlash(".docker/config.json")
	dockerLegacyHomePath    = ".dockercfg"

	// ErrNotLoggedIn is returned for users not logged into a registry
	// that they are trying to logout of
	ErrNotLoggedIn = errors.New("not logged in")
)

// SetAuthentication stores the username and password in the auth.json file
func SetAuthentication(sys *types.SystemContext, registry, username, password string) error {
	return modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) {
		if ch, exists := auths.CredHelpers[registry]; exists {
			return false, setAuthToCredHelper(ch, registry, username, password)
		}

		creds := base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
		newCreds := dockerAuthConfig{Auth: creds}
		auths.AuthConfigs[registry] = newCreds
		return true, nil
	})
}

// GetAuthentication returns the registry credentials stored in
// either auth.json file or .docker/config.json
// If an entry is not found empty strings are returned for the username and password
func GetAuthentication(sys *types.SystemContext, registry string) (string, string, error) {
	if sys != nil && sys.DockerAuthConfig != nil {
		return sys.DockerAuthConfig.Username, sys.DockerAuthConfig.Password, nil
	}

	dockerLegacyPath := filepath.Join(homedir.Get(), dockerLegacyHomePath)
	var paths []string
	pathToAuth, err := getPathToAuth(sys)
	if err == nil {
		paths = append(paths, pathToAuth)
	} else {
		// Error means that the path set for XDG_RUNTIME_DIR does not exist
		// but we don't want to completely fail in the case that the user is pulling a public image
		// Logging the error as a warning instead and moving on to pulling the image
		logrus.Warnf("%v: Trying to pull image in the event that it is a public image.", err)
	}
	paths = append(paths, filepath.Join(homedir.Get(), dockerHomePath), dockerLegacyPath)

	for _, path := range paths {
		legacyFormat := path == dockerLegacyPath
		username, password, err := findAuthentication(registry, path, legacyFormat)
		if err != nil {
			return "", "", err
		}
		if username != "" && password != "" {
			return username, password, nil
		}
	}
	return "", "", nil
}

// GetUserLoggedIn returns the username logged in to registry from either
// auth.json or XDG_RUNTIME_DIR
// Used to tell the user if someone is logged in to the registry when logging in
func GetUserLoggedIn(sys *types.SystemContext, registry string) (string, error) {
	path, err := getPathToAuth(sys)
	if err != nil {
		return "", err
	}
	username, _, _ := findAuthentication(registry, path, false)
	if username != "" {
		return username, nil
	}
	return "", nil
}

// RemoveAuthentication deletes the credentials stored in auth.json
func RemoveAuthentication(sys *types.SystemContext, registry string) error {
	return modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) {
		// First try cred helpers.
		if ch, exists := auths.CredHelpers[registry]; exists {
			return false, deleteAuthFromCredHelper(ch, registry)
		}

		if _, ok := auths.AuthConfigs[registry]; ok {
			delete(auths.AuthConfigs, registry)
		} else if _, ok := auths.AuthConfigs[normalizeRegistry(registry)]; ok {
			delete(auths.AuthConfigs, normalizeRegistry(registry))
		} else {
			return false, ErrNotLoggedIn
		}
		return true, nil
	})
}

// RemoveAllAuthentication deletes all the credentials stored in auth.json
func RemoveAllAuthentication(sys *types.SystemContext) error {
	return modifyJSON(sys, func(auths *dockerConfigFile) (bool, error) {
		auths.CredHelpers = make(map[string]string)
		auths.AuthConfigs = make(map[string]dockerAuthConfig)
		return true, nil
	})
}

// getPath gets the path of the auth.json file
// The path can be overriden by the user if the overwrite-path flag is set
// If the flag is not set and XDG_RUNTIME_DIR is set, the auth.json file is saved in XDG_RUNTIME_DIR/containers
// Otherwise, the auth.json file is stored in /run/containers/UID
func getPathToAuth(sys *types.SystemContext) (string, error) {
	if sys != nil {
		if sys.AuthFilePath != "" {
			return sys.AuthFilePath, nil
		}
		if sys.RootForImplicitAbsolutePaths != "" {
			return filepath.Join(sys.RootForImplicitAbsolutePaths, fmt.Sprintf(defaultPerUIDPathFormat, os.Getuid())), nil
		}
	}

	runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
	if runtimeDir != "" {
		// This function does not in general need to separately check that the returned path exists; that’s racy, and callers will fail accessing the file anyway.
		// We are checking for os.IsNotExist here only to give the user better guidance what to do in this special case.
		_, err := os.Stat(runtimeDir)
		if os.IsNotExist(err) {
			// This means the user set the XDG_RUNTIME_DIR variable and either forgot to create the directory
			// or made a typo while setting the environment variable,
			// so return an error referring to $XDG_RUNTIME_DIR instead of xdgRuntimeDirPath inside.
			return "", errors.Wrapf(err, "%q directory set by $XDG_RUNTIME_DIR does not exist. Either create the directory or unset $XDG_RUNTIME_DIR.", runtimeDir)
		} // else ignore err and let the caller fail accessing xdgRuntimeDirPath.
		return filepath.Join(runtimeDir, xdgRuntimeDirPath), nil
	}
	return fmt.Sprintf(defaultPerUIDPathFormat, os.Getuid()), nil
}

// readJSONFile unmarshals the authentications stored in the auth.json file and returns it
// or returns an empty dockerConfigFile data structure if auth.json does not exist
// if the file exists and is empty, readJSONFile returns an error
func readJSONFile(path string, legacyFormat bool) (dockerConfigFile, error) {
	var auths dockerConfigFile

	raw, err := ioutil.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			auths.AuthConfigs = map[string]dockerAuthConfig{}
			return auths, nil
		}
		return dockerConfigFile{}, err
	}

	if legacyFormat {
		if err = json.Unmarshal(raw, &auths.AuthConfigs); err != nil {
			return dockerConfigFile{}, errors.Wrapf(err, "error unmarshaling JSON at %q", path)
		}
		return auths, nil
	}

	if err = json.Unmarshal(raw, &auths); err != nil {
		return dockerConfigFile{}, errors.Wrapf(err, "error unmarshaling JSON at %q", path)
	}

	return auths, nil
}

// modifyJSON writes to auth.json if the dockerConfigFile has been updated
func modifyJSON(sys *types.SystemContext, editor func(auths *dockerConfigFile) (bool, error)) error {
	path, err := getPathToAuth(sys)
	if err != nil {
		return err
	}

	dir := filepath.Dir(path)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err = os.MkdirAll(dir, 0700); err != nil {
			return errors.Wrapf(err, "error creating directory %q", dir)
		}
	}

	auths, err := readJSONFile(path, false)
	if err != nil {
		return errors.Wrapf(err, "error reading JSON file %q", path)
	}

	updated, err := editor(&auths)
	if err != nil {
		return errors.Wrapf(err, "error updating %q", path)
	}
	if updated {
		newData, err := json.MarshalIndent(auths, "", "\t")
		if err != nil {
			return errors.Wrapf(err, "error marshaling JSON %q", path)
		}

		if err = ioutil.WriteFile(path, newData, 0755); err != nil {
			return errors.Wrapf(err, "error writing to file %q", path)
		}
	}

	return nil
}

func getAuthFromCredHelper(credHelper, registry string) (string, string, error) {
	helperName := fmt.Sprintf("docker-credential-%s", credHelper)
	p := helperclient.NewShellProgramFunc(helperName)
	creds, err := helperclient.Get(p, registry)
	if err != nil {
		return "", "", err
	}
	return creds.Username, creds.Secret, nil
}

func setAuthToCredHelper(credHelper, registry, username, password string) error {
	helperName := fmt.Sprintf("docker-credential-%s", credHelper)
	p := helperclient.NewShellProgramFunc(helperName)
	creds := &credentials.Credentials{
		ServerURL: registry,
		Username:  username,
		Secret:    password,
	}
	return helperclient.Store(p, creds)
}

func deleteAuthFromCredHelper(credHelper, registry string) error {
	helperName := fmt.Sprintf("docker-credential-%s", credHelper)
	p := helperclient.NewShellProgramFunc(helperName)
	return helperclient.Erase(p, registry)
}

// findAuthentication looks for auth of registry in path
func findAuthentication(registry, path string, legacyFormat bool) (string, string, error) {
	auths, err := readJSONFile(path, legacyFormat)
	if err != nil {
		return "", "", errors.Wrapf(err, "error reading JSON file %q", path)
	}

	// First try cred helpers. They should always be normalized.
	if ch, exists := auths.CredHelpers[registry]; exists {
		return getAuthFromCredHelper(ch, registry)
	}

	// I'm feeling lucky
	if val, exists := auths.AuthConfigs[registry]; exists {
		return decodeDockerAuth(val.Auth)
	}

	// bad luck; let's normalize the entries first
	registry = normalizeRegistry(registry)
	normalizedAuths := map[string]dockerAuthConfig{}
	for k, v := range auths.AuthConfigs {
		normalizedAuths[normalizeRegistry(k)] = v
	}
	if val, exists := normalizedAuths[registry]; exists {
		return decodeDockerAuth(val.Auth)
	}
	return "", "", nil
}

func decodeDockerAuth(s string) (string, string, error) {
	decoded, err := base64.StdEncoding.DecodeString(s)
	if err != nil {
		return "", "", err
	}
	parts := strings.SplitN(string(decoded), ":", 2)
	if len(parts) != 2 {
		// if it's invalid just skip, as docker does
		return "", "", nil
	}
	user := parts[0]
	password := strings.Trim(parts[1], "\x00")
	return user, password, nil
}

// convertToHostname converts a registry url which has http|https prepended
// to just an hostname.
// Copied from github.com/docker/docker/registry/auth.go
func convertToHostname(url string) string {
	stripped := url
	if strings.HasPrefix(url, "http://") {
		stripped = strings.TrimPrefix(url, "http://")
	} else if strings.HasPrefix(url, "https://") {
		stripped = strings.TrimPrefix(url, "https://")
	}

	nameParts := strings.SplitN(stripped, "/", 2)

	return nameParts[0]
}

func normalizeRegistry(registry string) string {
	normalized := convertToHostname(registry)
	switch normalized {
	case "registry-1.docker.io", "docker.io":
		return "index.docker.io"
	}
	return normalized
}
<|endoftext|>


<commit_before>package server

import (
    "fmt"

    "golang.org/x/net/context"

    pb "github.com/jaypipes/procession/proto"
)

// Bootstrap checks a bootstrap key and creates a user with SUPER privileges
func (s *Server) Bootstrap(
    ctx context.Context,
    req *pb.BootstrapRequest,
) (*pb.BootstrapResponse, error) {
    // The default value of the bootstrap key CLI/env option is "" which means
    // the Procession service has to be explcitly started with a
    // --bootstrap-key value in order for bootstrap operations, which don't
    // check for any authentication/authorization, to be taken.
    if s.cfg.BootstrapKey == "" {
        return nil, fmt.Errorf("Invalid bootstrap key")
    }

    key := req.Key
    if key == "" {
        return nil, fmt.Errorf("Invalid bootstrap key")
    }
    if key != s.cfg.BootstrapKey {
        return nil, fmt.Errorf("Invalid bootstrap key")
    }

    defer s.log.WithSection("iam/server")()

    // Create a role with the SUPER privilege if one with the requested name
    // does not exist
    role, err := s.storage.RoleGet(req.SuperRoleName)
    if err != nil {
        return nil, err
    }
    if role.Uuid == "" {
        rsFields := &pb.RoleSetFields{
            DisplayName: &pb.StringValue{
                Value: req.SuperRoleName,
            },
            Add: []pb.Permission{pb.Permission_SUPER},
        }
        _, err := s.storage.RoleCreate(nil, rsFields)
        if err != nil {
            return nil, err
        }
        s.log.L1("Created role %s with SUPER privilege", req.SuperRoleName)
    }

    // Add user records for each email in the collection of super user emails
    for _, email := range req.SuperUserEmails {
        user, err := s.storage.UserGet(email)
        if err != nil {
            return nil, err
        }
        if user.Uuid == "" {
            newFields := &pb.UserSetFields{
                DisplayName: &pb.StringValue{
                    Value: email,
                },
                Email: &pb.StringValue{
                    Value: email,
                },
            }
            user, err = s.storage.UserCreate(newFields)
            if err != nil {
                return nil, err
            }
            // Add the new super user to the super role
            ursReq := &pb.UserRolesSetRequest{
                User: email,
                Add: []string{req.SuperRoleName},
            }
            _, _, err := s.storage.UserRolesSet(ursReq)
            if err != nil {
                return nil, err
            }
            s.log.L1("Created new super user %s (%s)", email, user.Uuid)
        }
    }

    s.log.L1("Successful bootstrap operation.")

    // Clear the bootstrap key to effectively make this a one-time operation
    // TODO(jaypipes): Determine whether the affect of this reset in a
    // multi-server environment is something we should care about?
    s.cfg.BootstrapKey = ""
    return &pb.BootstrapResponse{KeyReset: true}, nil
}
<commit_msg>Properly handle NOT_FOUND in bootstrap command<commit_after>package server

import (
    "fmt"

    "golang.org/x/net/context"

    "github.com/jaypipes/procession/pkg/errors"
    pb "github.com/jaypipes/procession/proto"
)

// Bootstrap checks a bootstrap key and creates a user with SUPER privileges
func (s *Server) Bootstrap(
    ctx context.Context,
    req *pb.BootstrapRequest,
) (*pb.BootstrapResponse, error) {
    // The default value of the bootstrap key CLI/env option is "" which means
    // the Procession service has to be explcitly started with a
    // --bootstrap-key value in order for bootstrap operations, which don't
    // check for any authentication/authorization, to be taken.
    if s.cfg.BootstrapKey == "" {
        return nil, fmt.Errorf("Invalid bootstrap key")
    }

    key := req.Key
    if key == "" {
        return nil, fmt.Errorf("Invalid bootstrap key")
    }
    if key != s.cfg.BootstrapKey {
        return nil, fmt.Errorf("Invalid bootstrap key")
    }

    defer s.log.WithSection("iam/server")()

    // Create a role with the SUPER privilege if one with the requested name
    // does not exist
    role, err := s.storage.RoleGet(req.SuperRoleName)
    if err != nil {
        return nil, err
    }
    if role.Uuid == "" {
        rsFields := &pb.RoleSetFields{
            DisplayName: &pb.StringValue{
                Value: req.SuperRoleName,
            },
            Add: []pb.Permission{pb.Permission_SUPER},
        }
        _, err := s.storage.RoleCreate(nil, rsFields)
        if err != nil {
            return nil, err
        }
        s.log.L1("Created role %s with SUPER privilege", req.SuperRoleName)
    }

    // Add user records for each email in the collection of super user emails
    for _, email := range req.SuperUserEmails {
        user, err := s.storage.UserGet(email)
        if err != nil && ! errors.IsNotFound(err) {
            return nil, err
        }
        if errors.IsNotFound(err) {
            newFields := &pb.UserSetFields{
                DisplayName: &pb.StringValue{
                    Value: email,
                },
                Email: &pb.StringValue{
                    Value: email,
                },
            }
            user, err = s.storage.UserCreate(newFields)
            if err != nil {
                return nil, err
            }
            // Add the new super user to the super role
            ursReq := &pb.UserRolesSetRequest{
                User: email,
                Add: []string{req.SuperRoleName},
            }
            _, _, err := s.storage.UserRolesSet(ursReq)
            if err != nil {
                return nil, err
            }
            s.log.L1("Created new super user %s (%s)", email, user.Uuid)
        }
    }

    s.log.L1("Successful bootstrap operation.")

    // Clear the bootstrap key to effectively make this a one-time operation
    // TODO(jaypipes): Determine whether the affect of this reset in a
    // multi-server environment is something we should care about?
    s.cfg.BootstrapKey = ""
    return &pb.BootstrapResponse{KeyReset: true}, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 Google LLC

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 review

import (
	"fmt"

	"github.com/golang/glog"
	"github.com/grafeas/kritis/pkg/kritis/apis/kritis/v1beta1"
	"github.com/grafeas/kritis/pkg/kritis/container"
	"github.com/grafeas/kritis/pkg/kritis/crd/authority"
	"github.com/grafeas/kritis/pkg/kritis/crd/securitypolicy"
	"github.com/grafeas/kritis/pkg/kritis/metadata"
	"github.com/grafeas/kritis/pkg/kritis/secrets"
	"github.com/grafeas/kritis/pkg/kritis/util"
	"github.com/grafeas/kritis/pkg/kritis/violation"
	"k8s.io/api/core/v1"
)

type Reviewer struct {
	config *Config
	client metadata.Fetcher
}

type Config struct {
	Validate  securitypolicy.ValidateFunc
	Secret    secrets.Fetcher
	Strategy  violation.Strategy
	IsWebhook bool
}

func New(client metadata.Fetcher, c *Config) Reviewer {
	return Reviewer{
		client: client,
		config: c,
	}
}

// For testing
var (
	authFetcher = authority.Authorities
)

// Review reviews a set of images against a set of policies
// Returns error if violations are found and handles them as per violation strategy
func (r Reviewer) Review(images []string, isps []v1beta1.ImageSecurityPolicy, pod *v1.Pod) error {
	images = util.RemoveGloballyWhitelistedImages(images)
	if len(images) == 0 {
		glog.Info("images are all globally whitelisted, returning successful status", images)
		return nil
	}
	for _, isp := range isps {
		glog.Infof("Validating against ImageSecurityPolicy %s", isp.Name)
		for _, image := range images {
			glog.Infof("Check if %s as valid Attestations.", image)
			isAttested, attestations := r.fetchAndVerifyAttestations(image, isp.Namespace, pod)
			// Skip vulnerability check for Webhook if attestations found.
			if isAttested && r.config.IsWebhook {
				continue
			}

			glog.Infof("Getting vulnz for %s", image)
			violations, err := r.config.Validate(isp, image, r.client)
			if err != nil {
				return fmt.Errorf("error validating image security policy %v", err)
			}
			if len(violations) != 0 {
				return r.handleViolations(image, pod, violations)
			}
			if r.config.IsWebhook {
				if err := r.addAttestations(image, attestations, isp.Namespace); err != nil {
					glog.Errorf("error adding attestations %s", err)
				}
			}
			glog.Infof("Found no violations in %s", image)
		}
	}
	return nil
}

func (r Reviewer) fetchAndVerifyAttestations(image string, ns string, pod *v1.Pod) (bool, []metadata.PGPAttestation) {
	attestations, err := r.client.GetAttestations(image)
	if err != nil {
		glog.Errorf("Error while fetching attestations %s", err)
		return false, attestations
	}
	isAttested := r.hasValidImageAttestations(image, attestations, ns)
	if err := r.config.Strategy.HandleAttestation(image, pod, isAttested); err != nil {
		glog.Errorf("error handling attestations %v", err)
	}
	return isAttested, attestations
}

// hasValidImageAttestations return true if any one image attestation is verified.
func (r Reviewer) hasValidImageAttestations(image string, attestations []metadata.PGPAttestation, ns string) bool {
	if len(attestations) == 0 {
		glog.Infof(`No attestations found for image %s.
This normally happens when you deploy a pod before kritis or no attestation authority is deployed.
Please see instructions `, image)
	}
	host, err := container.NewAtomicContainerSig(image, map[string]string{})
	if err != nil {
		glog.Error(err)
		return false
	}
	for _, a := range attestations {
		// Get Secret from key id.
		secret, err := r.config.Secret(ns, a.KeyID)
		if err != nil {
			glog.Errorf("Could not find secret %s in namespace %s for attestation verification", a.KeyID, ns)
			continue
		}
		if err = host.VerifyAttestationSignature(secret.PublicKey, a.Signature); err != nil {
			glog.Errorf("Could not find verify attestation for attestation authority %s", a.KeyID)
		} else {
			return true
		}
	}
	return false
}

func (r Reviewer) handleViolations(image string, pod *v1.Pod, violations []securitypolicy.Violation) error {
	errMsg := fmt.Sprintf("found violations in %s", image)
	// Check if one of the violations is that the image is not fully qualified
	for _, v := range violations {
		if v.Violation == securitypolicy.UnqualifiedImageViolation {
			errMsg = fmt.Sprintf(`%s is not a fully qualified image.
			  You can run 'kubectl plugin resolve-tags' to qualify all images with a digest.
			  Instructions for installing the plugin can be found at https://github.com/grafeas/kritis/blob/master/cmd/kritis/kubectl/plugins/resolve`, image)
		}
	}
	if err := r.config.Strategy.HandleViolation(image, pod, violations); err != nil {
		return fmt.Errorf("%s. error handling violation %v", errMsg, err)
	}
	return fmt.Errorf(errMsg)
}

func (r Reviewer) addAttestations(image string, atts []metadata.PGPAttestation, ns string) error {
	// Get all AttestationAuthorities in this namespace.
	auths, err := authFetcher(ns)
	if err != nil {
		return err
	}
	if len(auths) == 0 {
		return fmt.Errorf("no attestation quthorities configured for namespace %s", ns)
	}
	// Get all AttestationAuthorities which have not attested the image.
	errMsgs := []string{}
	u := getUnAttested(auths, atts)
	if len(u) == 0 {
		glog.Info("Attestation exists for all authorities")
		return nil
	}
	for _, a := range u {
		// Get or Create Note for this this Authority
		n, err := util.GetOrCreateAttestationNote(r.client, &a)
		if err != nil {
			errMsgs = append(errMsgs, err.Error())
		}
		// Get secret for this Authority
		s, err := r.config.Secret(ns, a.Spec.PrivateKeySecretName)
		if err != nil {
			errMsgs = append(errMsgs, err.Error())
		}
		// Create Attestation Signature
		if _, err := r.client.CreateAttestationOccurence(n, image, s); err != nil {
			errMsgs = append(errMsgs, err.Error())
		}

	}
	if len(errMsgs) == 0 {
		return nil
	}
	return fmt.Errorf("one or more errors adding attestations: %s", errMsgs)
}

func getUnAttested(auths []v1beta1.AttestationAuthority, atts []metadata.PGPAttestation) []v1beta1.AttestationAuthority {
	l := []v1beta1.AttestationAuthority{}
	m := map[string]bool{}
	for _, a := range atts {
		m[a.KeyID] = true
	}

	for _, a := range auths {
		_, ok := m[a.Spec.PrivateKeySecretName]
		if !ok {
			l = append(l, a)
		}
	}
	return l
}
<commit_msg>Improve logging, make sure it includes the ISP.<commit_after>/*
Copyright 2018 Google LLC

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 review

import (
	"fmt"

	"github.com/golang/glog"
	"github.com/grafeas/kritis/pkg/kritis/apis/kritis/v1beta1"
	"github.com/grafeas/kritis/pkg/kritis/container"
	"github.com/grafeas/kritis/pkg/kritis/crd/authority"
	"github.com/grafeas/kritis/pkg/kritis/crd/securitypolicy"
	"github.com/grafeas/kritis/pkg/kritis/metadata"
	"github.com/grafeas/kritis/pkg/kritis/secrets"
	"github.com/grafeas/kritis/pkg/kritis/util"
	"github.com/grafeas/kritis/pkg/kritis/violation"
	"k8s.io/api/core/v1"
)

type Reviewer struct {
	config *Config
	client metadata.Fetcher
}

type Config struct {
	Validate  securitypolicy.ValidateFunc
	Secret    secrets.Fetcher
	Strategy  violation.Strategy
	IsWebhook bool
}

func New(client metadata.Fetcher, c *Config) Reviewer {
	return Reviewer{
		client: client,
		config: c,
	}
}

// For testing
var (
	authFetcher = authority.Authorities
)

// Review reviews a set of images against a set of policies
// Returns error if violations are found and handles them as per violation strategy
func (r Reviewer) Review(images []string, isps []v1beta1.ImageSecurityPolicy, pod *v1.Pod) error {
	images = util.RemoveGloballyWhitelistedImages(images)
	if len(images) == 0 {
		glog.Infof("images are all globally whitelisted, returning successful status: %s", images)
		return nil
	}
	for _, isp := range isps {
		glog.Infof("Validating against ImageSecurityPolicy %s", isp.Name)
		for _, image := range images {
			glog.Infof("Check if %s as valid Attestations.", image)
			isAttested, attestations := r.fetchAndVerifyAttestations(image, isp.Namespace, pod)
			// Skip vulnerability check for Webhook if attestations found.
			if isAttested && r.config.IsWebhook {
				continue
			}

			glog.Infof("Getting vulnz for %s", image)
			violations, err := r.config.Validate(isp, image, r.client)
			if err != nil {
				return fmt.Errorf("error validating image security policy %v", err)
			}
			if len(violations) != 0 {
				return r.handleViolations(image, pod, violations)
			}
			if r.config.IsWebhook {
				if err := r.addAttestations(image, attestations, isp.Namespace); err != nil {
					glog.Errorf("error adding attestations %s", err)
				}
			}
			glog.Infof("Found no violations for %s within ISP %s", image, isp.Name)
		}
	}
	return nil
}

func (r Reviewer) fetchAndVerifyAttestations(image string, ns string, pod *v1.Pod) (bool, []metadata.PGPAttestation) {
	attestations, err := r.client.GetAttestations(image)
	if err != nil {
		glog.Errorf("Error while fetching attestations %s", err)
		return false, attestations
	}
	isAttested := r.hasValidImageAttestations(image, attestations, ns)
	if err := r.config.Strategy.HandleAttestation(image, pod, isAttested); err != nil {
		glog.Errorf("error handling attestations %v", err)
	}
	return isAttested, attestations
}

// hasValidImageAttestations return true if any one image attestation is verified.
func (r Reviewer) hasValidImageAttestations(image string, attestations []metadata.PGPAttestation, ns string) bool {
	if len(attestations) == 0 {
		glog.Infof(`No attestations found for image %s.
This normally happens when you deploy a pod before kritis or no attestation authority is deployed.
Please see instructions `, image)
	}
	host, err := container.NewAtomicContainerSig(image, map[string]string{})
	if err != nil {
		glog.Error(err)
		return false
	}
	for _, a := range attestations {
		// Get Secret from key id.
		secret, err := r.config.Secret(ns, a.KeyID)
		if err != nil {
			glog.Errorf("Could not find secret %s in namespace %s for attestation verification", a.KeyID, ns)
			continue
		}
		if err = host.VerifyAttestationSignature(secret.PublicKey, a.Signature); err != nil {
			glog.Errorf("Could not find verify attestation for attestation authority %s", a.KeyID)
		} else {
			return true
		}
	}
	return false
}

func (r Reviewer) handleViolations(image string, pod *v1.Pod, violations []securitypolicy.Violation) error {
	errMsg := fmt.Sprintf("found violations in %s", image)
	// Check if one of the violations is that the image is not fully qualified
	for _, v := range violations {
		if v.Violation == securitypolicy.UnqualifiedImageViolation {
			errMsg = fmt.Sprintf(`%s is not a fully qualified image.
			  You can run 'kubectl plugin resolve-tags' to qualify all images with a digest.
			  Instructions for installing the plugin can be found at https://github.com/grafeas/kritis/blob/master/cmd/kritis/kubectl/plugins/resolve`, image)
		}
	}
	if err := r.config.Strategy.HandleViolation(image, pod, violations); err != nil {
		return fmt.Errorf("%s. error handling violation %v", errMsg, err)
	}
	return fmt.Errorf(errMsg)
}

func (r Reviewer) addAttestations(image string, atts []metadata.PGPAttestation, ns string) error {
	// Get all AttestationAuthorities in this namespace.
	auths, err := authFetcher(ns)
	if err != nil {
		return err
	}
	if len(auths) == 0 {
		return fmt.Errorf("no attestation quthorities configured for namespace %s", ns)
	}
	// Get all AttestationAuthorities which have not attested the image.
	errMsgs := []string{}
	u := getUnAttested(auths, atts)
	if len(u) == 0 {
		glog.Info("Attestation exists for all authorities")
		return nil
	}
	for _, a := range u {
		// Get or Create Note for this this Authority
		n, err := util.GetOrCreateAttestationNote(r.client, &a)
		if err != nil {
			errMsgs = append(errMsgs, err.Error())
		}
		// Get secret for this Authority
		s, err := r.config.Secret(ns, a.Spec.PrivateKeySecretName)
		if err != nil {
			errMsgs = append(errMsgs, err.Error())
		}
		// Create Attestation Signature
		if _, err := r.client.CreateAttestationOccurence(n, image, s); err != nil {
			errMsgs = append(errMsgs, err.Error())
		}

	}
	if len(errMsgs) == 0 {
		return nil
	}
	return fmt.Errorf("one or more errors adding attestations: %s", errMsgs)
}

func getUnAttested(auths []v1beta1.AttestationAuthority, atts []metadata.PGPAttestation) []v1beta1.AttestationAuthority {
	l := []v1beta1.AttestationAuthority{}
	m := map[string]bool{}
	for _, a := range atts {
		m[a.KeyID] = true
	}

	for _, a := range auths {
		_, ok := m[a.Spec.PrivateKeySecretName]
		if !ok {
			l = append(l, a)
		}
	}
	return l
}
<|endoftext|>