<commit_before>package scepserver_test

import (
	"bytes"
	"context"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"

	"github.com/micromdm/scep/depot"
	filedepot "github.com/micromdm/scep/depot/file"
	scepserver "github.com/micromdm/scep/server"

	kitlog "github.com/go-kit/kit/log"
)

func TestCACaps(t *testing.T) {
	server, _, teardown := newServer(t)
	defer teardown()
	url := server.URL + "/scep?operation=GetCACaps"
	resp, err := http.Get(url)
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Error("expected", http.StatusOK, "got", resp.StatusCode)
	}
}

func TestEncodePKCSReq_Request(t *testing.T) {
	pkcsreq := loadTestFile(t, "../scep/testdata/PKCSReq.der")
	msg := scepserver.SCEPRequest{
		Operation: "PKIOperation",
		Message:   pkcsreq,
	}
	methods := []string{"POST", "GET"}
	for _, method := range methods {
		t.Run(method, func(t *testing.T) {
			r := httptest.NewRequest(method, "http://acme.co/scep", nil)
			rr := *r
			if err := scepserver.EncodeSCEPRequest(context.Background(), &rr, msg); err != nil {
				t.Fatal(err)
			}

			q := r.URL.Query()
			if have, want := q.Get("operation"), msg.Operation; have != want {
				t.Errorf("have %s, want %s", have, want)
			}

			if method == "POST" {
				if have, want := rr.ContentLength, int64(len(msg.Message)); have != want {
					t.Errorf("have %d, want %d", have, want)
				}
			}

			if method == "GET" {
				if q.Get("message") == "" {
					t.Errorf("expected GET PKIOperation to have a non-empty message field")
				}
			}

		})
	}

}

func TestGetCACertMessage(t *testing.T) {
	testMsg := "testMsg"
	sr := scepserver.SCEPRequest{Operation: "GetCACert", Message: []byte(testMsg)}
	req, err := http.NewRequest("GET", "http://127.0.0.1:8080/scep", nil)
	if err != nil {
		t.Fatal(err)
	}
	err = scepserver.EncodeSCEPRequest(context.Background(), req, sr)
	if err != nil {
		t.Fatal(err)
	}
	if !strings.Contains(req.URL.RawQuery, "message="+testMsg) {
		t.Fatal("RawQuery does not contain message")
	}
}

func TestPKIOperation(t *testing.T) {
	server, _, teardown := newServer(t)
	defer teardown()
	pkcsreq := loadTestFile(t, "../scep/testdata/PKCSReq.der")
	body := bytes.NewReader(pkcsreq)
	url := server.URL + "/scep?operation=PKIOperation"
	resp, err := http.Post(url, "", body)
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Error("expected", http.StatusOK, "got", resp.StatusCode)
	}
}

func TestPKIOperationGET(t *testing.T) {
	server, _, teardown := newServer(t)
	defer teardown()
	pkcsreq := loadTestFile(t, "../scep/testdata/PKCSReq.der")
	message := base64.StdEncoding.EncodeToString(pkcsreq)
	req, err := http.NewRequest("GET", server.URL+"/scep", nil)
	if err != nil {
		t.Fatal(err)
	}
	params := req.URL.Query()
	params.Set("operation", "PKIOperation")
	params.Set("message", message)
	req.URL.RawQuery = params.Encode()
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Error("expected", http.StatusOK, "got", resp.StatusCode)
	}
}

func newServer(t *testing.T, opts ...scepserver.ServiceOption) (*httptest.Server, scepserver.Service, func()) {
	var err error
	var depot depot.Depot // cert storage
	{
		depot, err = filedepot.NewFileDepot("../scep/testdata/testca")
		if err != nil {
			t.Fatal(err)
		}
		depot = &noopDepot{depot}
	}
	crt, key, err := depot.CA([]byte{})
	var svc scepserver.Service // scep service
	{
		svc, err = scepserver.NewService(crt[0], key, scepserver.NopCSRSigner())
		if err != nil {
			t.Fatal(err)
		}
	}
	logger := kitlog.NewNopLogger()
	e := scepserver.MakeServerEndpoints(svc)
	handler := scepserver.MakeHTTPHandler(e, svc, logger)
	server := httptest.NewServer(handler)
	teardown := func() {
		server.Close()
		os.Remove("../scep/testdata/testca/serial")
		os.Remove("../scep/testdata/testca/index.txt")
	}
	return server, svc, teardown
}

type noopDepot struct{ depot.Depot }

func (d *noopDepot) Put(name string, crt *x509.Certificate) error {
	return nil
}

/* helpers */
const (
	rsaPrivateKeyPEMBlockType = "RSA PRIVATE KEY"
	certificatePEMBlockType   = "CERTIFICATE"
)

func loadTestFile(t *testing.T, path string) []byte {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		t.Fatal(err)
	}
	return data
}

// create a new RSA private key
func newRSAKey(bits int) (*rsa.PrivateKey, error) {
	private, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		return nil, err
	}
	return private, nil
}

// create a CSR using the same parameters as Keychain Access would produce
func newKeychainCSR(priv *rsa.PrivateKey, email, country, cname string) ([]byte, error) {
	subj := pkix.Name{
		Country:    []string{country},
		CommonName: cname,
		ExtraNames: []pkix.AttributeTypeAndValue{{
			Type:  []int{1, 2, 840, 113549, 1, 9, 1},
			Value: email,
		}},
	}
	template := &x509.CertificateRequest{
		Subject: subj,
	}
	return x509.CreateCertificateRequest(rand.Reader, template, priv)
}
func loadCACredentials(t *testing.T) (*x509.Certificate, *rsa.PrivateKey) {
	cert, err := loadCertFromFile("../scep/testdata/testca/ca.crt")
	if err != nil {
		t.Fatal(err)
	}
	key, err := loadKeyFromFile("../scep/testdata/testca/ca.key")
	if err != nil {
		t.Fatal(err)
	}
	return cert, key
}

func loadCertFromFile(path string) (*x509.Certificate, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	pemBlock, _ := pem.Decode(data)
	if pemBlock == nil {
		return nil, errors.New("PEM decode failed")
	}
	if pemBlock.Type != certificatePEMBlockType {
		return nil, errors.New("unmatched type or headers")
	}
	return x509.ParseCertificate(pemBlock.Bytes)
}

// load an encrypted private key from disk
func loadKeyFromFile(path string) (*rsa.PrivateKey, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	pemBlock, _ := pem.Decode(data)
	if pemBlock == nil {
		return nil, errors.New("PEM decode failed")
	}
	if pemBlock.Type != rsaPrivateKeyPEMBlockType {
		return nil, errors.New("unmatched type or headers")
	}

	// testca key has a password
	if len(pemBlock.Headers) > 0 {
		password := []byte("")
		b, err := x509.DecryptPEMBlock(pemBlock, password)
		if err != nil {
			return nil, err
		}
		return x509.ParsePKCS1PrivateKey(b)
	}

	return x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
}
<commit_msg>Remove unused test functions (#161)<commit_after>package scepserver_test

import (
	"bytes"
	"context"
	"crypto/x509"
	"encoding/base64"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"

	"github.com/micromdm/scep/depot"
	filedepot "github.com/micromdm/scep/depot/file"
	scepserver "github.com/micromdm/scep/server"

	kitlog "github.com/go-kit/kit/log"
)

func TestCACaps(t *testing.T) {
	server, _, teardown := newServer(t)
	defer teardown()
	url := server.URL + "/scep?operation=GetCACaps"
	resp, err := http.Get(url)
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Error("expected", http.StatusOK, "got", resp.StatusCode)
	}
}

func TestEncodePKCSReq_Request(t *testing.T) {
	pkcsreq := loadTestFile(t, "../scep/testdata/PKCSReq.der")
	msg := scepserver.SCEPRequest{
		Operation: "PKIOperation",
		Message:   pkcsreq,
	}
	methods := []string{"POST", "GET"}
	for _, method := range methods {
		t.Run(method, func(t *testing.T) {
			r := httptest.NewRequest(method, "http://acme.co/scep", nil)
			rr := *r
			if err := scepserver.EncodeSCEPRequest(context.Background(), &rr, msg); err != nil {
				t.Fatal(err)
			}

			q := r.URL.Query()
			if have, want := q.Get("operation"), msg.Operation; have != want {
				t.Errorf("have %s, want %s", have, want)
			}

			if method == "POST" {
				if have, want := rr.ContentLength, int64(len(msg.Message)); have != want {
					t.Errorf("have %d, want %d", have, want)
				}
			}

			if method == "GET" {
				if q.Get("message") == "" {
					t.Errorf("expected GET PKIOperation to have a non-empty message field")
				}
			}

		})
	}

}

func TestGetCACertMessage(t *testing.T) {
	testMsg := "testMsg"
	sr := scepserver.SCEPRequest{Operation: "GetCACert", Message: []byte(testMsg)}
	req, err := http.NewRequest("GET", "http://127.0.0.1:8080/scep", nil)
	if err != nil {
		t.Fatal(err)
	}
	err = scepserver.EncodeSCEPRequest(context.Background(), req, sr)
	if err != nil {
		t.Fatal(err)
	}
	if !strings.Contains(req.URL.RawQuery, "message="+testMsg) {
		t.Fatal("RawQuery does not contain message")
	}
}

func TestPKIOperation(t *testing.T) {
	server, _, teardown := newServer(t)
	defer teardown()
	pkcsreq := loadTestFile(t, "../scep/testdata/PKCSReq.der")
	body := bytes.NewReader(pkcsreq)
	url := server.URL + "/scep?operation=PKIOperation"
	resp, err := http.Post(url, "", body)
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Error("expected", http.StatusOK, "got", resp.StatusCode)
	}
}

func TestPKIOperationGET(t *testing.T) {
	server, _, teardown := newServer(t)
	defer teardown()
	pkcsreq := loadTestFile(t, "../scep/testdata/PKCSReq.der")
	message := base64.StdEncoding.EncodeToString(pkcsreq)
	req, err := http.NewRequest("GET", server.URL+"/scep", nil)
	if err != nil {
		t.Fatal(err)
	}
	params := req.URL.Query()
	params.Set("operation", "PKIOperation")
	params.Set("message", message)
	req.URL.RawQuery = params.Encode()
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Error("expected", http.StatusOK, "got", resp.StatusCode)
	}
}

func newServer(t *testing.T, opts ...scepserver.ServiceOption) (*httptest.Server, scepserver.Service, func()) {
	var err error
	var depot depot.Depot // cert storage
	{
		depot, err = filedepot.NewFileDepot("../scep/testdata/testca")
		if err != nil {
			t.Fatal(err)
		}
		depot = &noopDepot{depot}
	}
	crt, key, err := depot.CA([]byte{})
	var svc scepserver.Service // scep service
	{
		svc, err = scepserver.NewService(crt[0], key, scepserver.NopCSRSigner())
		if err != nil {
			t.Fatal(err)
		}
	}
	logger := kitlog.NewNopLogger()
	e := scepserver.MakeServerEndpoints(svc)
	handler := scepserver.MakeHTTPHandler(e, svc, logger)
	server := httptest.NewServer(handler)
	teardown := func() {
		server.Close()
		os.Remove("../scep/testdata/testca/serial")
		os.Remove("../scep/testdata/testca/index.txt")
	}
	return server, svc, teardown
}

type noopDepot struct{ depot.Depot }

func (d *noopDepot) Put(name string, crt *x509.Certificate) error {
	return nil
}

/* helpers */
const (
	rsaPrivateKeyPEMBlockType = "RSA PRIVATE KEY"
	certificatePEMBlockType   = "CERTIFICATE"
)

func loadTestFile(t *testing.T, path string) []byte {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		t.Fatal(err)
	}
	return data
}
<|endoftext|>


<commit_before>package localfile

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

	"github.com/AaronGoldman/ccfs/objects"
	"github.com/AaronGoldman/ccfs/services"
)

//localfileservice is an
type localfileservice struct{}

func (lfs localfileservice) PostBlob(b objects.Blob) (err error) {
	filepath := fmt.Sprintf("bin/blobs/%s", b.Hash().Hex())
	//log.Printf("[localfileservice] PostBlob %s", filepath)
	err = os.MkdirAll("bin/blobs", 0764)
	err = ioutil.WriteFile(filepath, b.Bytes(), 0664)
	return
}
func (lfs localfileservice) PostTag(t objects.Tag) (err error) {
	lfs.PostBlob(objects.Blob(t.Bytes()))
	filepath := fmt.Sprintf("bin/tags/%s/%s/%d", t.Hkid.Hex(),
		t.NameSegment, t.Version)
	//log.Printf("[localfileservice] PostTag %s", filepath)
	dirpath := fmt.Sprintf("bin/tags/%s/%s", t.Hkid.Hex(),
		t.NameSegment)
	err = os.MkdirAll(dirpath, 0764)
	err = ioutil.WriteFile(filepath, t.Bytes(), 0664)
	return
}
func (lfs localfileservice) PostCommit(c objects.Commit) (err error) {
	lfs.PostBlob(objects.Blob(c.Bytes()))
	filepath := fmt.Sprintf("bin/commits/%s/%d", c.Hkid.Hex(), c.Version)
	//log.Printf("[localfileservice] PostCommit %s\n\t%d", filepath, c.Version())
	dirpath := fmt.Sprintf("bin/commits/%s", c.Hkid.Hex())
	err = os.MkdirAll(dirpath, 0764)
	err = ioutil.WriteFile(filepath, c.Bytes(), 0664)
	return
}
func (lfs localfileservice) PostKey(p *objects.PrivateKey) (err error) {
	err = os.MkdirAll("bin/keys", 0700)
	filepath := fmt.Sprintf("bin/keys/%s", p.Hkid().Hex())
	err = ioutil.WriteFile(filepath, objects.PrivateKey(*p).Bytes(), 0600)
	return
}

func (lfs localfileservice) GetBlob(h objects.HCID) (b objects.Blob, err error) {
	//ToDo Validate input
	if h == nil {
		return nil, fmt.Errorf("[localfileservice] GetBlob() HCID is nil")
	}
	filepath := fmt.Sprintf("bin/blobs/%s", h.Hex())
	//log.Printf("Filepath: %v", filepath)
	data, err := ioutil.ReadFile(filepath)
	if err != nil {
		log.Printf("\n\t%v\n", err)
	}
	//build object
	b = objects.Blob(data)
	return b, err
}
func (lfs localfileservice) GetCommit(h objects.HKID) (c objects.Commit, err error) {
	//Validate input
	matches, err := filepath.Glob(fmt.Sprintf("bin/commits/%s/*", h.Hex()))
	filepath := lfs.latestVersion(matches)
	//log.Printf("Filepath: %v", filepath)
	data, err := ioutil.ReadFile(filepath)
	if err == nil {
		c, _ = objects.CommitFromBytes(data)
	}
	return c, err
}
func (lfs localfileservice) GetTag(h objects.HKID, namesegment string) (t objects.Tag, err error) {
	//ToDo Validate input
	matches, err := filepath.Glob(fmt.Sprintf("bin/tags/%s/%s/*",
		h.Hex(), namesegment))
	filepath := lfs.latestVersion(matches)
	//log.Printf("Filepath: %v", filepath)
	data, err := ioutil.ReadFile(filepath)
	if err == nil {
		t, _ = objects.TagFromBytes(data)
	}
	return t, err
}
func (lfs localfileservice) GetTags(h objects.HKID) (tags []objects.Tag, err error) {
	//ToDo Validate input
	directoryEntries, err := ioutil.ReadDir(fmt.Sprintf("bin/tags/%s", h.Hex()))
	if err != nil {
		log.Println(err)
	}
	log.Println(h)
	namesegment := ""
	for _, directoryEntry := range directoryEntries {
		if directoryEntry.IsDir() {
			namesegment = directoryEntry.Name()
		} else {
			continue
		}
		matches, err := filepath.Glob(fmt.Sprintf("bin/tags/%s/%s/*",
			h.Hex(), namesegment))
		filepath := lfs.latestVersion(matches)
		//log.Printf("Filepath: %v", filepath)
		data, err := ioutil.ReadFile(filepath)
		if err == nil {
			tag, err := objects.TagFromBytes(data)
			if err == nil {
				tags = append(tags, tag)
			}
		}
	}
	return tags, err
}
func (lfs localfileservice) GetKey(h objects.HKID) (objects.Blob, error) {
	filepath := fmt.Sprintf("bin/keys/%s", h.Hex())
	//log.Printf("Filepath: %v", filepath)
	filedata, err := ioutil.ReadFile(filepath)
	if err != nil {
		log.Println(err)
	}
	return filedata, err
}

func (lfs localfileservice) latestVersion(matches []string) string {
	match := ""
	for _, element := range matches {
		if match < element {
			match = element
		}
	}
	return match
}

var Instance localfileservice = localfileservice{}

func init() {
	services.Registerblobgeter(Instance)
	services.Registerblobposter(Instance)
	services.Registercommitgeter(Instance)
	services.Registercommitposter(Instance)
	services.Registertaggeter(Instance)
	services.Registertagposter(Instance)
	services.Registerkeygeter(Instance)
	services.Registerkeyposter(Instance)
}
<commit_msg>added some error handeling to getTags()<commit_after>package localfile

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

	"github.com/AaronGoldman/ccfs/objects"
	"github.com/AaronGoldman/ccfs/services"
)

//localfileservice is an
type localfileservice struct{}

func (lfs localfileservice) PostBlob(b objects.Blob) (err error) {
	filepath := fmt.Sprintf("bin/blobs/%s", b.Hash().Hex())
	//log.Printf("[localfileservice] PostBlob %s", filepath)
	err = os.MkdirAll("bin/blobs", 0764)
	err = ioutil.WriteFile(filepath, b.Bytes(), 0664)
	return
}
func (lfs localfileservice) PostTag(t objects.Tag) (err error) {
	lfs.PostBlob(objects.Blob(t.Bytes()))
	filepath := fmt.Sprintf("bin/tags/%s/%s/%d", t.Hkid.Hex(),
		t.NameSegment, t.Version)
	//log.Printf("[localfileservice] PostTag %s", filepath)
	dirpath := fmt.Sprintf("bin/tags/%s/%s", t.Hkid.Hex(),
		t.NameSegment)
	err = os.MkdirAll(dirpath, 0764)
	err = ioutil.WriteFile(filepath, t.Bytes(), 0664)
	return
}
func (lfs localfileservice) PostCommit(c objects.Commit) (err error) {
	lfs.PostBlob(objects.Blob(c.Bytes()))
	filepath := fmt.Sprintf("bin/commits/%s/%d", c.Hkid.Hex(), c.Version)
	//log.Printf("[localfileservice] PostCommit %s\n\t%d", filepath, c.Version())
	dirpath := fmt.Sprintf("bin/commits/%s", c.Hkid.Hex())
	err = os.MkdirAll(dirpath, 0764)
	err = ioutil.WriteFile(filepath, c.Bytes(), 0664)
	return
}
func (lfs localfileservice) PostKey(p *objects.PrivateKey) (err error) {
	err = os.MkdirAll("bin/keys", 0700)
	filepath := fmt.Sprintf("bin/keys/%s", p.Hkid().Hex())
	err = ioutil.WriteFile(filepath, objects.PrivateKey(*p).Bytes(), 0600)
	return
}

func (lfs localfileservice) GetBlob(h objects.HCID) (b objects.Blob, err error) {
	//ToDo Validate input
	if h == nil {
		return nil, fmt.Errorf("[localfileservice] GetBlob() HCID is nil")
	}
	filepath := fmt.Sprintf("bin/blobs/%s", h.Hex())
	//log.Printf("Filepath: %v", filepath)
	data, err := ioutil.ReadFile(filepath)
	if err != nil {
		log.Printf("\n\t%v\n", err)
	}
	//build object
	b = objects.Blob(data)
	return b, err
}
func (lfs localfileservice) GetCommit(h objects.HKID) (c objects.Commit, err error) {
	//Validate input
	matches, err := filepath.Glob(fmt.Sprintf("bin/commits/%s/*", h.Hex()))
	filepath := lfs.latestVersion(matches)
	//log.Printf("Filepath: %v", filepath)
	data, err := ioutil.ReadFile(filepath)
	if err == nil {
		c, _ = objects.CommitFromBytes(data)
	}
	return c, err
}
func (lfs localfileservice) GetTag(h objects.HKID, namesegment string) (t objects.Tag, err error) {
	//ToDo Validate input
	matches, err := filepath.Glob(fmt.Sprintf("bin/tags/%s/%s/*",
		h.Hex(), namesegment))
	filepath := lfs.latestVersion(matches)
	//log.Printf("Filepath: %v", filepath)
	data, err := ioutil.ReadFile(filepath)
	if err == nil {
		t, _ = objects.TagFromBytes(data)
	}
	return t, err
}
func (lfs localfileservice) GetTags(h objects.HKID) (tags []objects.Tag, err error) {
	//ToDo Validate input
	directoryEntries, err := ioutil.ReadDir(fmt.Sprintf("bin/tags/%s", h.Hex()))
	if err != nil {
		log.Println(err)
	}
	log.Println(h)
	namesegment := ""
	for _, directoryEntry := range directoryEntries {
		if directoryEntry.IsDir() {
			namesegment = directoryEntry.Name()
		} else {
			continue
		}
		matches, err := filepath.Glob(fmt.Sprintf("bin/tags/%s/%s/*",
			h.Hex(), namesegment))
		filepath := lfs.latestVersion(matches)
		//log.Printf("Filepath: %v", filepath)
		data, err := ioutil.ReadFile(filepath)
		if err == nil {
			tag, err := objects.TagFromBytes(data)
			if err == nil {
				tags = append(tags, tag)
			} else {
				log.Println(err)
			}
		} else {
			log.Panicln(err)
		}
	}
	log.Println(tags)
	return tags, err
}
func (lfs localfileservice) GetKey(h objects.HKID) (objects.Blob, error) {
	filepath := fmt.Sprintf("bin/keys/%s", h.Hex())
	//log.Printf("Filepath: %v", filepath)
	filedata, err := ioutil.ReadFile(filepath)
	if err != nil {
		log.Println(err)
	}
	return filedata, err
}

func (lfs localfileservice) latestVersion(matches []string) string {
	match := ""
	for _, element := range matches {
		if match < element {
			match = element
		}
	}
	return match
}

var Instance localfileservice = localfileservice{}

func init() {
	services.Registerblobgeter(Instance)
	services.Registerblobposter(Instance)
	services.Registercommitgeter(Instance)
	services.Registercommitposter(Instance)
	services.Registertaggeter(Instance)
	services.Registertagposter(Instance)
	services.Registerkeygeter(Instance)
	services.Registerkeyposter(Instance)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package syncutil

import (
	"sync"

	"golang.org/x/net/context"
)

// XXX: Comments
type Bundle struct {
	context context.Context
	cancel  context.CancelFunc

	waitGroup sync.WaitGroup

	errorOnce  sync.Once
	firstError error
}

// XXX: Comments
func (b *Bundle) Add(f func(context.Context) error) {
	b.waitGroup.Add(1)

	// Run the function in the background.
	go func() {
		defer b.waitGroup.Done()

		err := f(b.context)
		if err == nil {
			return
		}

		// On first error, cancel the context and save the error.
		b.errorOnce.Do(func() {
			b.firstError = err
			b.cancel()
		})
	}()
}

// XXX: Comments
func (b *Bundle) Join() error {
	b.waitGroup.Wait()
	return b.firstError
}

// XXX: Comments for interface and impl
func NewBundle(parent context.Context) *Bundle {
	if parent == nil {
		parent = context.Background()
	}

	b := &Bundle{}
	b.context, b.cancel = context.WithCancel(parent)

	return b
}
<commit_msg>Added overall documentation to Bundle.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package syncutil

import (
	"sync"

	"golang.org/x/net/context"
)

// A collection of concurrently-executing operations, each of which may fail.
//
// Operations are defined by functions that accept a context and return an
// error. If any operation returns a non-nil error, all concurrent and future
// operations will have their contexts cancelled . When Join() is called on a
// bundle with one or more operations that returned an error, it always returns
// the first error (i.e. that which led to the cancellation of others).
//
// Bundles can be used to set up pipelines of concurrent actors sending data to
// each other, conveniently cancelling the pipeline if anything fails. A
// typical use looks like the following:
//
//     // Run a pipeline that consists of one goroutine listing object names,
//     // while N goroutines concurrently delete the listed objects one by one.
//     // If any listing or deletion operation fails, cancel the whole pipeline
//     // and return the error.
//     func deleteAllObjects(ctx context.Context, N int) error {
//       bundle := syncutil.NewBundle(ctx)
//
//       // List objects into a channel. Assuming that listObjects responds to
//       // cancellation of its context, it will not get stuck blocking forever
//       // on a write into objectNames if the deleters return early in error
//       // before draining the channel.
//       objectNames := make(chan string)
//       bundle.Add(func(ctx context.Context) error {
//         return listObjects(ctx, objectNames)
//       })
//
//       // Run N deletion workers.
//       for i := 0; i < N; i++ {
//         bundle.Add(func(ctx context.Context) error {
//           for name := range objectNames {
//             if err := deleteObject(ctx, name); err != nil {
//               return err
//             }
//           }
//         })
//       }
//
//       // Wait for the whole pipeline to finish, and return its status.
//       return bundle.Wait()
//    }
//
type Bundle struct {
	context context.Context
	cancel  context.CancelFunc

	waitGroup sync.WaitGroup

	errorOnce  sync.Once
	firstError error
}

// XXX: Comments
func (b *Bundle) Add(f func(context.Context) error) {
	b.waitGroup.Add(1)

	// Run the function in the background.
	go func() {
		defer b.waitGroup.Done()

		err := f(b.context)
		if err == nil {
			return
		}

		// On first error, cancel the context and save the error.
		b.errorOnce.Do(func() {
			b.firstError = err
			b.cancel()
		})
	}()
}

// XXX: Comments
func (b *Bundle) Join() error {
	b.waitGroup.Wait()
	return b.firstError
}

// XXX: Comments for interface and impl
func NewBundle(parent context.Context) *Bundle {
	if parent == nil {
		parent = context.Background()
	}

	b := &Bundle{}
	b.context, b.cancel = context.WithCancel(parent)

	return b
}
<|endoftext|>


<commit_before>package speccy

import (
	"sync"

	"github.com/veandco/go-sdl2/sdl"
)

// An entry in the map means the key is depressed. (poor key)
type keyboardState struct {
	sync.Mutex
	keysDown map[sdl.Keycode]struct{}
}

func NewKeyboardState() *keyboardState {
	ks := keyboardState{keysDown: make(map[sdl.Keycode]struct{})}
	return &ks
}

// 8 rows - index in array is bit position in addrHi 0->7
// 5 keys = bit0 1st, bit 4last
var keymapForBit = [][]sdl.Keycode{
	[]sdl.Keycode{sdl.K_LSHIFT, sdl.K_z, sdl.K_x, sdl.K_c, sdl.K_v},
	[]sdl.Keycode{sdl.K_a, sdl.K_s, sdl.K_d, sdl.K_f, sdl.K_g},
	[]sdl.Keycode{sdl.K_q, sdl.K_w, sdl.K_e, sdl.K_r, sdl.K_t},
	[]sdl.Keycode{sdl.K_1, sdl.K_2, sdl.K_3, sdl.K_4, sdl.K_5},
	[]sdl.Keycode{sdl.K_0, sdl.K_9, sdl.K_8, sdl.K_7, sdl.K_6},
	[]sdl.Keycode{sdl.K_p, sdl.K_o, sdl.K_i, sdl.K_u, sdl.K_y},
	[]sdl.Keycode{sdl.K_RETURN, sdl.K_l, sdl.K_k, sdl.K_j, sdl.K_h},
	[]sdl.Keycode{sdl.K_SPACE, sdl.K_RSHIFT, sdl.K_m, sdl.K_n, sdl.K_b},
}

func calcInputByte(addrHi byte, keysdown []sdl.Keycode) byte {
	n := byte(0xff)
	addrMask := byte(0x01)
	for _, keyRow := range keymapForBit {
		if addrMask&addrHi == 0 {
			// Bit is low in addrHi - process
			byteMask := byte(0xfe)
			for _, rowKey := range keyRow {
			KEYDOWN:
				for _, keydown := range keysdown {
					if keydown == rowKey {
						n &= byteMask
						break KEYDOWN
					}
				}
				byteMask <<= 1
				byteMask |= 0x01
			}
		}
		addrMask <<= 1
	}
	return n
}

func (ks *keyboardState) keyboardInputHandler(addr uint16) byte {
	hi := byte(addr >> 8)
	lo := byte(addr)

	if lo != 0xfe {
		return 0x00
	}

	keysdown := ks.keysdown()

	return calcInputByte(hi, keysdown)
}

func (ks *keyboardState) keysdown() []sdl.Keycode {
	ks.Lock()
	defer ks.Unlock()

	// Update our view of keys before the interrupt handler runs
	ks.update()

	var keys []sdl.Keycode
	for k := range ks.keysDown {
		keys = append(keys, k)
	}
	return keys
}

func (ks *keyboardState) update() {

	// Drain events to update map
	for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
		switch ev := event.(type) {
		case *sdl.KeyDownEvent:
			kc := ev.Keysym.Sym
			//					s := sdl.GetScancodeName(sc)
			ks.keymove(kc, false)
			//(*ks)[sc] = struct{}{}
		case *sdl.KeyUpEvent:
			kc := ev.Keysym.Sym
			ks.keymove(kc, true)
			//					s := sdl.GetScancodeName(sc)
			//delete(*ks, kc)
		}
	}
}

func (ks *keyboardState) keymove(kc sdl.Keycode, up bool) {
	mappedKeys := []sdl.Keycode{}
	switch kc {
	case sdl.K_LEFT:
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_5)
	case sdl.K_DOWN:
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_6)
	case sdl.K_UP:
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_7)
	case sdl.K_RIGHT:
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_8)
	case sdl.K_ESCAPE:
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_1)
	case sdl.K_BACKSPACE:
		// Delete is shift-0
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_0)
	default:
		mappedKeys = append(mappedKeys, kc)
	}

	for _, mappedKey := range mappedKeys {
		if up {
			delete(ks.keysDown, mappedKey)
		} else {
			ks.keysDown[mappedKey] = struct{}{}
		}
	}
}
<commit_msg>Update to newer go-sdl event types<commit_after>package speccy

import (
	"sync"

	"github.com/veandco/go-sdl2/sdl"
)

// An entry in the map means the key is depressed. (poor key)
type keyboardState struct {
	sync.Mutex
	keysDown map[sdl.Keycode]struct{}
}

func NewKeyboardState() *keyboardState {
	ks := keyboardState{keysDown: make(map[sdl.Keycode]struct{})}
	return &ks
}

// 8 rows - index in array is bit position in addrHi 0->7
// 5 keys = bit0 1st, bit 4last
var keymapForBit = [][]sdl.Keycode{
	[]sdl.Keycode{sdl.K_LSHIFT, sdl.K_z, sdl.K_x, sdl.K_c, sdl.K_v},
	[]sdl.Keycode{sdl.K_a, sdl.K_s, sdl.K_d, sdl.K_f, sdl.K_g},
	[]sdl.Keycode{sdl.K_q, sdl.K_w, sdl.K_e, sdl.K_r, sdl.K_t},
	[]sdl.Keycode{sdl.K_1, sdl.K_2, sdl.K_3, sdl.K_4, sdl.K_5},
	[]sdl.Keycode{sdl.K_0, sdl.K_9, sdl.K_8, sdl.K_7, sdl.K_6},
	[]sdl.Keycode{sdl.K_p, sdl.K_o, sdl.K_i, sdl.K_u, sdl.K_y},
	[]sdl.Keycode{sdl.K_RETURN, sdl.K_l, sdl.K_k, sdl.K_j, sdl.K_h},
	[]sdl.Keycode{sdl.K_SPACE, sdl.K_RSHIFT, sdl.K_m, sdl.K_n, sdl.K_b},
}

func calcInputByte(addrHi byte, keysdown []sdl.Keycode) byte {
	n := byte(0xff)
	addrMask := byte(0x01)
	for _, keyRow := range keymapForBit {
		if addrMask&addrHi == 0 {
			// Bit is low in addrHi - process
			byteMask := byte(0xfe)
			for _, rowKey := range keyRow {
			KEYDOWN:
				for _, keydown := range keysdown {
					if keydown == rowKey {
						n &= byteMask
						break KEYDOWN
					}
				}
				byteMask <<= 1
				byteMask |= 0x01
			}
		}
		addrMask <<= 1
	}
	return n
}

func (ks *keyboardState) keyboardInputHandler(addr uint16) byte {
	hi := byte(addr >> 8)
	lo := byte(addr)

	if lo != 0xfe {
		return 0x00
	}

	keysdown := ks.keysdown()

	return calcInputByte(hi, keysdown)
}

func (ks *keyboardState) keysdown() []sdl.Keycode {
	ks.Lock()
	defer ks.Unlock()

	// Update our view of keys before the interrupt handler runs
	ks.update()

	var keys []sdl.Keycode
	for k := range ks.keysDown {
		keys = append(keys, k)
	}
	return keys
}

func (ks *keyboardState) update() {

	// Drain events to update map
	for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
		switch ev := event.(type) {
		case *sdl.KeyboardEvent:
			kc := ev.Keysym.Sym
			ks.keymove(kc, ev.Type == sdl.KEYUP)
		}
	}
}

func (ks *keyboardState) keymove(kc sdl.Keycode, up bool) {
	mappedKeys := []sdl.Keycode{}
	switch kc {
	case sdl.K_LEFT:
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_5)
	case sdl.K_DOWN:
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_6)
	case sdl.K_UP:
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_7)
	case sdl.K_RIGHT:
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_8)
	case sdl.K_ESCAPE:
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_1)
	case sdl.K_BACKSPACE:
		// Delete is shift-0
		mappedKeys = append(mappedKeys, sdl.K_LSHIFT)
		mappedKeys = append(mappedKeys, sdl.K_0)
	default:
		mappedKeys = append(mappedKeys, kc)
	}

	for _, mappedKey := range mappedKeys {
		if up {
			delete(ks.keysDown, mappedKey)
		} else {
			ks.keysDown[mappedKey] = struct{}{}
		}
	}
}
<|endoftext|>


<commit_before>package lib

// GetVirtualMachine requests an overview of the named VM, regardless of its deletion status.
func (bigv *bigvClient) GetVirtualMachine(name VirtualMachineName) (vm *VirtualMachine, err error) {
	vm = new(VirtualMachine)
	path := BuildURL("/accounts/%s/groups/%s/virtual_machines/%s?include_deleted=true&view=overview", name.Account, name.Group, name.VirtualMachine)

	err = bigv.RequestAndUnmarshal(true, "GET", path, "", vm)
	if err != nil {
		vm = nil
	}
	return vm, err
}

// DeleteVirtualMachine deletes the named virtual machine.
// returns nil on success or an error otherwise.
func (bigv *bigvClient) DeleteVirtualMachine(name VirtualMachineName, purge bool) (err error) {
	// TODO(telyn): URL escaping
	path := BuildURL("/accounts/%s/groups/%s/virtual_machines/%s", name.Account, name.Group, name.VirtualMachine)
	if purge {
		path += "?purge=true"
	}

	_, _, err = bigv.Request(true, "DELETE", path, "")
	return err
}

// UndeleteVirtualMachine changes the deleted flag on a VM back to false.
// Return nil on success, an error otherwise.
func (bigv *bigvClient) UndeleteVirtualMachine(name VirtualMachineName) (err error) {
	path := BuildURL("/accounts/%s/groups/%s/virtual_machines/%s", name.Account, name.Group, name.VirtualMachine)

	_, _, err = bigv.Request(true, "PUT", path, `{"deleted":false}`)
	return err
}
<commit_msg>URL escaping was implemented through a change to BuildURL several commits back; removing TODO<commit_after>package lib

// GetVirtualMachine requests an overview of the named VM, regardless of its deletion status.
func (bigv *bigvClient) GetVirtualMachine(name VirtualMachineName) (vm *VirtualMachine, err error) {
	vm = new(VirtualMachine)
	path := BuildURL("/accounts/%s/groups/%s/virtual_machines/%s?include_deleted=true&view=overview", name.Account, name.Group, name.VirtualMachine)

	err = bigv.RequestAndUnmarshal(true, "GET", path, "", vm)
	if err != nil {
		vm = nil
	}
	return vm, err
}

// DeleteVirtualMachine deletes the named virtual machine.
// returns nil on success or an error otherwise.
func (bigv *bigvClient) DeleteVirtualMachine(name VirtualMachineName, purge bool) (err error) {
	path := BuildURL("/accounts/%s/groups/%s/virtual_machines/%s", name.Account, name.Group, name.VirtualMachine)
	if purge {
		path += "?purge=true"
	}

	_, _, err = bigv.Request(true, "DELETE", path, "")
	return err
}

// UndeleteVirtualMachine changes the deleted flag on a VM back to false.
// Return nil on success, an error otherwise.
func (bigv *bigvClient) UndeleteVirtualMachine(name VirtualMachineName) (err error) {
	path := BuildURL("/accounts/%s/groups/%s/virtual_machines/%s", name.Account, name.Group, name.VirtualMachine)

	_, _, err = bigv.Request(true, "PUT", path, `{"deleted":false}`)
	return err
}
<|endoftext|>


<commit_before>// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bootkube

import (
	"fmt"
	"strings"
	"time"

	"github.com/coreos-inc/pluton"
	"github.com/coreos-inc/pluton/spawn"

	"github.com/coreos/mantle/kola/cluster"
	"github.com/coreos/mantle/util"
)

func etcdScale(tc cluster.TestCluster) error {
	// create cluster with self-hosted etcd
	c, err := spawn.MakeBootkubeCluster(tc, 1, true)
	if err != nil {
		return err
	}

	// add two master nodes to cluster
	if err := c.AddMasters(2); err != nil {
		return err
	}

	// scale up etcd operator
	if err := resizeSelfHostedEtcd(c, 3); err != nil {
		return err
	}

	// todo check that each pod runs on a different master node
	if err := checkEtcdPodDistribution(c, 3); err != nil {
		return err
	}

	// scale back to 1
	if err := resizeSelfHostedEtcd(c, 1); err != nil {
		return err
	}

	// run an nginx deployment and ping it
	if err := nginxCheck(c); err != nil {
		return fmt.Errorf("nginxCheck: %v", err)
	}
	return nil
}

// resizes self-hosted etcd and checks that the desired number of pods are in a running state
func resizeSelfHostedEtcd(c *pluton.Cluster, size int) error {
	const (
		tprGroup   = "etcd.coreos.com"
		apiVersion = "v1beta1"
		tprKind    = "clusters"
	)
	var tprEndpoint = fmt.Sprintf("http://127.0.0.1:8080/apis/%s/%s/namespaces/kube-system/%s/kube-etcd",
		tprGroup, apiVersion, tprKind)

	scaleCmds := []string{
		fmt.Sprintf("curl -H 'Content-Type: application/json' -X GET %v > body.json", tprEndpoint),
		// delete resourceVersion field before curling back
		fmt.Sprintf("jq 'recurse(.metadata) |= del(.resourceVersion)' < out.json | jq .spec.size=%v > newbody.json", size),
		fmt.Sprintf("curl -H 'Content-Type: application/json' -X PUT --data @newbody.json %v", tprEndpoint),
	}
	for _, cmd := range scaleCmds {
		_, err := c.Masters[0].SSH(cmd)
		if err != nil {
			return fmt.Errorf("error in scale up command: %v: %v", cmd, err)
		}
	}

	// check that all pods are running
	podsReady := func() error {
		out, err := c.Kubectl(`get po -l etcd_cluster=kube-etcd -o jsonpath='{.items[*].status.phase}' --namespace=kube-system`)
		if err != nil {
			return err
		}
		phases := strings.Split(out, " ")
		if len(phases) != size {
			return fmt.Errorf("expected %d etcd pods got %d: %v", size, len(phases), phases)
		}
		for _, phase := range phases {
			if phase != "Running" {
				return fmt.Errorf("one or more etcd pods not in a 'Running' phase")
			}
		}
		return nil
	}

	if err := util.Retry(10, 12*time.Second, podsReady); err != nil {
		return err
	}
	return nil
}

// checks that self-hosted etcd pods are scheduled on different master nodes
// when possible
func checkEtcdPodDistribution(c *pluton.Cluster, etcdClusterSize int) error {
	// check that number of unique nodes etcd pods run on is equal to the
	// lesser value betweeen total number of master nodes and total number
	// of etcd pods
	out, err := c.Kubectl(`get po -l etcd_cluster=kube-etcd -o jsonpath='{.items[*].status.hostIP}' --namespace=kube-system`)
	if err != nil {
		return err
	}

	nodeIPs := strings.Split(out, " ")
	nodeSet := map[string]struct{}{}
	for _, node := range nodeIPs {
		nodeSet[node] = struct{}{}
	}

	var expectedUniqueNodes int
	if len(c.Masters) > etcdClusterSize {
		expectedUniqueNodes = etcdClusterSize
	} else {
		expectedUniqueNodes = len(c.Masters)
	}

	if len(nodeSet) != expectedUniqueNodes {
		return fmt.Errorf("self-hosted etcd pods not properly distributed")
	}

	// check that each node in nodeSet is a master node
	masterSet := map[string]struct{}{}
	for _, m := range c.Masters {
		masterSet[m.PrivateIP()] = struct{}{}
	}

	for k, _ := range nodeSet {
		if _, ok := masterSet[k]; !ok {
			// Just warn instead of erroring until/if supported
			plog.Infof("detected self-hosted etcd pod running on non-master node %v %v", masterSet, nodeSet)
		}
	}

	return nil
}
<commit_msg>tests/bootkube: one more fix<commit_after>// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bootkube

import (
	"fmt"
	"strings"
	"time"

	"github.com/coreos-inc/pluton"
	"github.com/coreos-inc/pluton/spawn"

	"github.com/coreos/mantle/kola/cluster"
	"github.com/coreos/mantle/util"
)

func etcdScale(tc cluster.TestCluster) error {
	// create cluster with self-hosted etcd
	c, err := spawn.MakeBootkubeCluster(tc, 1, true)
	if err != nil {
		return err
	}

	// add two master nodes to cluster
	if err := c.AddMasters(2); err != nil {
		return err
	}

	// scale up etcd operator
	if err := resizeSelfHostedEtcd(c, 3); err != nil {
		return err
	}

	// todo check that each pod runs on a different master node
	if err := checkEtcdPodDistribution(c, 3); err != nil {
		return err
	}

	// scale back to 1
	if err := resizeSelfHostedEtcd(c, 1); err != nil {
		return err
	}

	// run an nginx deployment and ping it
	if err := nginxCheck(c); err != nil {
		return fmt.Errorf("nginxCheck: %v", err)
	}
	return nil
}

// resizes self-hosted etcd and checks that the desired number of pods are in a running state
func resizeSelfHostedEtcd(c *pluton.Cluster, size int) error {
	const (
		tprGroup   = "etcd.coreos.com"
		apiVersion = "v1beta1"
		tprKind    = "clusters"
	)
	var tprEndpoint = fmt.Sprintf("http://127.0.0.1:8080/apis/%s/%s/namespaces/kube-system/%s/kube-etcd",
		tprGroup, apiVersion, tprKind)

	scaleCmds := []string{
		fmt.Sprintf("curl -H 'Content-Type: application/json' -X GET %v > body.json", tprEndpoint),
		// delete resourceVersion field before curling back
		fmt.Sprintf("jq 'recurse(.metadata) |= del(.resourceVersion)' < body.json | jq .spec.size=%v > newbody.json", size),
		fmt.Sprintf("curl -H 'Content-Type: application/json' -X PUT --data @newbody.json %v", tprEndpoint),
	}
	for _, cmd := range scaleCmds {
		_, err := c.Masters[0].SSH(cmd)
		if err != nil {
			return fmt.Errorf("error in scale up command: %v: %v", cmd, err)
		}
	}

	// check that all pods are running
	podsReady := func() error {
		out, err := c.Kubectl(`get po -l etcd_cluster=kube-etcd -o jsonpath='{.items[*].status.phase}' --namespace=kube-system`)
		if err != nil {
			return err
		}
		phases := strings.Split(out, " ")
		if len(phases) != size {
			return fmt.Errorf("expected %d etcd pods got %d: %v", size, len(phases), phases)
		}
		for _, phase := range phases {
			if phase != "Running" {
				return fmt.Errorf("one or more etcd pods not in a 'Running' phase")
			}
		}
		return nil
	}

	if err := util.Retry(10, 12*time.Second, podsReady); err != nil {
		return err
	}
	return nil
}

// checks that self-hosted etcd pods are scheduled on different master nodes
// when possible
func checkEtcdPodDistribution(c *pluton.Cluster, etcdClusterSize int) error {
	// check that number of unique nodes etcd pods run on is equal to the
	// lesser value betweeen total number of master nodes and total number
	// of etcd pods
	out, err := c.Kubectl(`get po -l etcd_cluster=kube-etcd -o jsonpath='{.items[*].status.hostIP}' --namespace=kube-system`)
	if err != nil {
		return err
	}

	nodeIPs := strings.Split(out, " ")
	nodeSet := map[string]struct{}{}
	for _, node := range nodeIPs {
		nodeSet[node] = struct{}{}
	}

	var expectedUniqueNodes int
	if len(c.Masters) > etcdClusterSize {
		expectedUniqueNodes = etcdClusterSize
	} else {
		expectedUniqueNodes = len(c.Masters)
	}

	if len(nodeSet) != expectedUniqueNodes {
		return fmt.Errorf("self-hosted etcd pods not properly distributed")
	}

	// check that each node in nodeSet is a master node
	masterSet := map[string]struct{}{}
	for _, m := range c.Masters {
		masterSet[m.PrivateIP()] = struct{}{}
	}

	for k, _ := range nodeSet {
		if _, ok := masterSet[k]; !ok {
			// Just warn instead of erroring until/if supported
			plog.Infof("detected self-hosted etcd pod running on non-master node %v %v", masterSet, nodeSet)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package librariesio

import (
	"context"
	"fmt"
	"net/http"
	"time"
)

// Project represents a project on libraries.io
type Project struct {
	Description              *string    `json:"description,omitempty"`
	Forks                    *int       `json:"forks,omitempty"`
	Homepage                 *string    `json:"homepage,omitempty"`
	Keywords                 []*string  `json:"keywords,omitempty"`
	Language                 *string    `json:"language,omitempty"`
	LatestReleaseNumber      *string    `json:"latest_release_number,omitempty"`
	LatestReleasePublishedAt *time.Time `json:"latest_release_published_at,omitempty"`
	LatestStableRelease      *Release   `json:"latest_stable_release,omitempty"`
	Name                     *string    `json:"name,omitempty"`
	NormalizedLicenses       []*string  `json:"normalized_licenses,omitempty"`
	PackageManagerURL        *string    `json:"package_manager_url,omitempty"`
	Platform                 *string    `json:"platform,omitempty"`
	Rank                     *int       `json:"rank,omitempty"`
	Stars                    *int       `json:"stars,omitempty"`
	Status                   *string    `json:"status,omitempty"`
	Versions                 []*Release `json:"versions,omitempty"`
}

// Release represents a release of the project
type Release struct {
	Number      string    `json:"number,omitempty"`
	PublishedAt time.Time `json:"published_at,omitempty"`
}

// GetProject returns information about a project and it's versions.
// GET https://libraries.io/api/:platform/:name
func (c *Client) GetProject(ctx context.Context, platform string, name string) (*Project, *http.Response, error) {
	urlStr := fmt.Sprintf("%v/%v", platform, name)

	request, err := c.NewRequest("GET", urlStr, nil)

	if err != nil {
		return nil, nil, err
	}

	project := new(Project)
	response, err := c.Do(ctx, request, project)
	if err != nil {
		return nil, response, err
	}

	return project, response, nil
}
<commit_msg>Add ProjectDependency model<commit_after>package librariesio

import (
	"context"
	"fmt"
	"net/http"
	"time"
)

// Project represents a project on libraries.io
type Project struct {
	Description              *string    `json:"description,omitempty"`
	Forks                    *int       `json:"forks,omitempty"`
	Homepage                 *string    `json:"homepage,omitempty"`
	Keywords                 []*string  `json:"keywords,omitempty"`
	Language                 *string    `json:"language,omitempty"`
	LatestReleaseNumber      *string    `json:"latest_release_number,omitempty"`
	LatestReleasePublishedAt *time.Time `json:"latest_release_published_at,omitempty"`
	LatestStableRelease      *Release   `json:"latest_stable_release,omitempty"`
	Name                     *string    `json:"name,omitempty"`
	NormalizedLicenses       []*string  `json:"normalized_licenses,omitempty"`
	PackageManagerURL        *string    `json:"package_manager_url,omitempty"`
	Platform                 *string    `json:"platform,omitempty"`
	Rank                     *int       `json:"rank,omitempty"`
	Stars                    *int       `json:"stars,omitempty"`
	Status                   *string    `json:"status,omitempty"`
	Versions                 []*Release `json:"versions,omitempty"`

	// Dependencies are only populated for GetProjectDependencies
	Dependencies []*ProjectDependency `json:"dependencies,omitempty"`
}

// Release represents a release of the project
type Release struct {
	Number      string    `json:"number,omitempty"`
	PublishedAt time.Time `json:"published_at,omitempty"`
}

// ProjectDependency represents a dependency of the project
type ProjectDependency struct {
	Deprecated   bool   `json:"deprecated,omitempty"`
	Latest       string `json:"latest,omitempty"`
	LatestStable string `json:"latest_stable,omitempty"`
	Name         string `json:"name,omitempty"`
	Outdated     bool   `json:"outdated,omitempty"`
	Platform     string `json:"platform,omitempty"`
	ProjectName  string `json:"project_name,omitempty"`
	Requirements string `json:"requirements,omitempty"`
}

// GetProject returns information about a project and it's versions.
// GET https://libraries.io/api/:platform/:name
func (c *Client) GetProject(ctx context.Context, platform string, name string) (*Project, *http.Response, error) {
	urlStr := fmt.Sprintf("%v/%v", platform, name)

	request, err := c.NewRequest("GET", urlStr, nil)

	if err != nil {
		return nil, nil, err
	}

	project := new(Project)
	response, err := c.Do(ctx, request, project)
	if err != nil {
		return nil, response, err
	}

	return project, response, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package options

import (
	"fmt"

	"github.com/spf13/pflag"

	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apiserver/pkg/registry/generic"
	genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
	"k8s.io/apiserver/pkg/server"
	serverstorage "k8s.io/apiserver/pkg/server/storage"
	"k8s.io/apiserver/pkg/storage/storagebackend"
)

type EtcdOptions struct {
	StorageConfig storagebackend.Config

	EtcdServersOverrides []string

	// To enable protobuf as storage format, it is enough
	// to set it to "application/vnd.kubernetes.protobuf".
	DefaultStorageMediaType string
	DeleteCollectionWorkers int
	EnableGarbageCollection bool

	// Set EnableWatchCache to false to disable all watch caches
	EnableWatchCache        bool
	// Set DefaultWatchCacheSize to zero to disable watch caches for those resources that have no explicit cache size set
	DefaultWatchCacheSize   int
}

func NewEtcdOptions(backendConfig *storagebackend.Config) *EtcdOptions {
	return &EtcdOptions{
		StorageConfig:           *backendConfig,
		DefaultStorageMediaType: "application/json",
		DeleteCollectionWorkers: 1,
		EnableGarbageCollection: true,
		EnableWatchCache:        true,
		DefaultWatchCacheSize:   100,
	}
}

func (s *EtcdOptions) Validate() []error {
	allErrors := []error{}
	if len(s.StorageConfig.ServerList) == 0 {
		allErrors = append(allErrors, fmt.Errorf("--etcd-servers must be specified"))
	}

	return allErrors
}

// AddEtcdFlags adds flags related to etcd storage for a specific APIServer to the specified FlagSet
func (s *EtcdOptions) AddFlags(fs *pflag.FlagSet) {
	fs.StringSliceVar(&s.EtcdServersOverrides, "etcd-servers-overrides", s.EtcdServersOverrides, ""+
		"Per-resource etcd servers overrides, comma separated. The individual override "+
		"format: group/resource#servers, where servers are http://ip:port, semicolon separated.")

	fs.StringVar(&s.DefaultStorageMediaType, "storage-media-type", s.DefaultStorageMediaType, ""+
		"The media type to use to store objects in storage. "+
		"Some resources or storage backends may only support a specific media type and will ignore this setting.")
	fs.IntVar(&s.DeleteCollectionWorkers, "delete-collection-workers", s.DeleteCollectionWorkers,
		"Number of workers spawned for DeleteCollection call. These are used to speed up namespace cleanup.")

	fs.BoolVar(&s.EnableGarbageCollection, "enable-garbage-collector", s.EnableGarbageCollection, ""+
		"Enables the generic garbage collector. MUST be synced with the corresponding flag "+
		"of the kube-controller-manager.")

	// TODO: enable cache in integration tests.
	fs.BoolVar(&s.EnableWatchCache, "watch-cache", s.EnableWatchCache,
		"Enable watch caching in the apiserver")

	fs.StringVar(&s.StorageConfig.Type, "storage-backend", s.StorageConfig.Type,
		"The storage backend for persistence. Options: 'etcd3' (default), 'etcd2'.")

	fs.IntVar(&s.StorageConfig.DeserializationCacheSize, "deserialization-cache-size", s.StorageConfig.DeserializationCacheSize,
		"Number of deserialized json objects to cache in memory.")

	fs.StringSliceVar(&s.StorageConfig.ServerList, "etcd-servers", s.StorageConfig.ServerList,
		"List of etcd servers to connect with (scheme://ip:port), comma separated.")

	fs.StringVar(&s.StorageConfig.Prefix, "etcd-prefix", s.StorageConfig.Prefix,
		"The prefix to prepend to all resource paths in etcd.")

	fs.StringVar(&s.StorageConfig.KeyFile, "etcd-keyfile", s.StorageConfig.KeyFile,
		"SSL key file used to secure etcd communication.")

	fs.StringVar(&s.StorageConfig.CertFile, "etcd-certfile", s.StorageConfig.CertFile,
		"SSL certification file used to secure etcd communication.")

	fs.StringVar(&s.StorageConfig.CAFile, "etcd-cafile", s.StorageConfig.CAFile,
		"SSL Certificate Authority file used to secure etcd communication.")

	fs.BoolVar(&s.StorageConfig.Quorum, "etcd-quorum-read", s.StorageConfig.Quorum,
		"If true, enable quorum read.")
}

func (s *EtcdOptions) ApplyTo(c *server.Config) error {
	c.RESTOptionsGetter = &SimpleRestOptionsFactory{Options: *s}
	return nil
}

func (s *EtcdOptions) ApplyWithStorageFactoryTo(factory serverstorage.StorageFactory, c *server.Config) error {
	c.RESTOptionsGetter = &storageFactoryRestOptionsFactory{Options: *s, StorageFactory: factory}
	return nil
}

type SimpleRestOptionsFactory struct {
	Options EtcdOptions
}

func (f *SimpleRestOptionsFactory) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
	ret := generic.RESTOptions{
		StorageConfig:           &f.Options.StorageConfig,
		Decorator:               generic.UndecoratedStorage,
		EnableGarbageCollection: f.Options.EnableGarbageCollection,
		DeleteCollectionWorkers: f.Options.DeleteCollectionWorkers,
		ResourcePrefix:          f.Options.StorageConfig.Prefix + "/" + resource.Group + "/" + resource.Resource,
	}
	if f.Options.EnableWatchCache {
		ret.Decorator = genericregistry.StorageWithCacher(f.Options.DefaultWatchCacheSize)
	}
	return ret, nil
}

type storageFactoryRestOptionsFactory struct {
	Options        EtcdOptions
	StorageFactory serverstorage.StorageFactory
}

func (f *storageFactoryRestOptionsFactory) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
	storageConfig, err := f.StorageFactory.NewConfig(resource)
	if err != nil {
		return generic.RESTOptions{}, fmt.Errorf("unable to find storage destination for %v, due to %v", resource, err.Error())
	}

	ret := generic.RESTOptions{
		StorageConfig:           storageConfig,
		Decorator:               generic.UndecoratedStorage,
		DeleteCollectionWorkers: f.Options.DeleteCollectionWorkers,
		EnableGarbageCollection: f.Options.EnableGarbageCollection,
		ResourcePrefix:          f.StorageFactory.ResourcePrefix(resource),
	}
	if f.Options.EnableWatchCache {
		ret.Decorator = genericregistry.StorageWithCacher(f.Options.DefaultWatchCacheSize)
	}

	return ret, nil
}

<commit_msg>UPSTREAM: 47060: Fix etcd storage location for CRs<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package options

import (
	"fmt"

	"github.com/spf13/pflag"

	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apiserver/pkg/registry/generic"
	genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
	"k8s.io/apiserver/pkg/server"
	serverstorage "k8s.io/apiserver/pkg/server/storage"
	"k8s.io/apiserver/pkg/storage/storagebackend"
)

type EtcdOptions struct {
	StorageConfig storagebackend.Config

	EtcdServersOverrides []string

	// To enable protobuf as storage format, it is enough
	// to set it to "application/vnd.kubernetes.protobuf".
	DefaultStorageMediaType string
	DeleteCollectionWorkers int
	EnableGarbageCollection bool

	// Set EnableWatchCache to false to disable all watch caches
	EnableWatchCache bool
	// Set DefaultWatchCacheSize to zero to disable watch caches for those resources that have no explicit cache size set
	DefaultWatchCacheSize int
}

func NewEtcdOptions(backendConfig *storagebackend.Config) *EtcdOptions {
	return &EtcdOptions{
		StorageConfig:           *backendConfig,
		DefaultStorageMediaType: "application/json",
		DeleteCollectionWorkers: 1,
		EnableGarbageCollection: true,
		EnableWatchCache:        true,
		DefaultWatchCacheSize:   100,
	}
}

func (s *EtcdOptions) Validate() []error {
	allErrors := []error{}
	if len(s.StorageConfig.ServerList) == 0 {
		allErrors = append(allErrors, fmt.Errorf("--etcd-servers must be specified"))
	}

	return allErrors
}

// AddEtcdFlags adds flags related to etcd storage for a specific APIServer to the specified FlagSet
func (s *EtcdOptions) AddFlags(fs *pflag.FlagSet) {
	fs.StringSliceVar(&s.EtcdServersOverrides, "etcd-servers-overrides", s.EtcdServersOverrides, ""+
		"Per-resource etcd servers overrides, comma separated. The individual override "+
		"format: group/resource#servers, where servers are http://ip:port, semicolon separated.")

	fs.StringVar(&s.DefaultStorageMediaType, "storage-media-type", s.DefaultStorageMediaType, ""+
		"The media type to use to store objects in storage. "+
		"Some resources or storage backends may only support a specific media type and will ignore this setting.")
	fs.IntVar(&s.DeleteCollectionWorkers, "delete-collection-workers", s.DeleteCollectionWorkers,
		"Number of workers spawned for DeleteCollection call. These are used to speed up namespace cleanup.")

	fs.BoolVar(&s.EnableGarbageCollection, "enable-garbage-collector", s.EnableGarbageCollection, ""+
		"Enables the generic garbage collector. MUST be synced with the corresponding flag "+
		"of the kube-controller-manager.")

	// TODO: enable cache in integration tests.
	fs.BoolVar(&s.EnableWatchCache, "watch-cache", s.EnableWatchCache,
		"Enable watch caching in the apiserver")

	fs.StringVar(&s.StorageConfig.Type, "storage-backend", s.StorageConfig.Type,
		"The storage backend for persistence. Options: 'etcd3' (default), 'etcd2'.")

	fs.IntVar(&s.StorageConfig.DeserializationCacheSize, "deserialization-cache-size", s.StorageConfig.DeserializationCacheSize,
		"Number of deserialized json objects to cache in memory.")

	fs.StringSliceVar(&s.StorageConfig.ServerList, "etcd-servers", s.StorageConfig.ServerList,
		"List of etcd servers to connect with (scheme://ip:port), comma separated.")

	fs.StringVar(&s.StorageConfig.Prefix, "etcd-prefix", s.StorageConfig.Prefix,
		"The prefix to prepend to all resource paths in etcd.")

	fs.StringVar(&s.StorageConfig.KeyFile, "etcd-keyfile", s.StorageConfig.KeyFile,
		"SSL key file used to secure etcd communication.")

	fs.StringVar(&s.StorageConfig.CertFile, "etcd-certfile", s.StorageConfig.CertFile,
		"SSL certification file used to secure etcd communication.")

	fs.StringVar(&s.StorageConfig.CAFile, "etcd-cafile", s.StorageConfig.CAFile,
		"SSL Certificate Authority file used to secure etcd communication.")

	fs.BoolVar(&s.StorageConfig.Quorum, "etcd-quorum-read", s.StorageConfig.Quorum,
		"If true, enable quorum read.")
}

func (s *EtcdOptions) ApplyTo(c *server.Config) error {
	c.RESTOptionsGetter = &SimpleRestOptionsFactory{Options: *s}
	return nil
}

func (s *EtcdOptions) ApplyWithStorageFactoryTo(factory serverstorage.StorageFactory, c *server.Config) error {
	c.RESTOptionsGetter = &storageFactoryRestOptionsFactory{Options: *s, StorageFactory: factory}
	return nil
}

type SimpleRestOptionsFactory struct {
	Options EtcdOptions
}

func (f *SimpleRestOptionsFactory) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
	ret := generic.RESTOptions{
		StorageConfig:           &f.Options.StorageConfig,
		Decorator:               generic.UndecoratedStorage,
		EnableGarbageCollection: f.Options.EnableGarbageCollection,
		DeleteCollectionWorkers: f.Options.DeleteCollectionWorkers,
		ResourcePrefix:          resource.Group + "/" + resource.Resource,
	}
	if f.Options.EnableWatchCache {
		ret.Decorator = genericregistry.StorageWithCacher(f.Options.DefaultWatchCacheSize)
	}
	return ret, nil
}

type storageFactoryRestOptionsFactory struct {
	Options        EtcdOptions
	StorageFactory serverstorage.StorageFactory
}

func (f *storageFactoryRestOptionsFactory) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
	storageConfig, err := f.StorageFactory.NewConfig(resource)
	if err != nil {
		return generic.RESTOptions{}, fmt.Errorf("unable to find storage destination for %v, due to %v", resource, err.Error())
	}

	ret := generic.RESTOptions{
		StorageConfig:           storageConfig,
		Decorator:               generic.UndecoratedStorage,
		DeleteCollectionWorkers: f.Options.DeleteCollectionWorkers,
		EnableGarbageCollection: f.Options.EnableGarbageCollection,
		ResourcePrefix:          f.StorageFactory.ResourcePrefix(resource),
	}
	if f.Options.EnableWatchCache {
		ret.Decorator = genericregistry.StorageWithCacher(f.Options.DefaultWatchCacheSize)
	}

	return ret, nil
}
<|endoftext|>


<commit_before>package courier

import (
	"github.com/hackform/governor"
	"net/http"
	"net/url"
	"regexp"
)

const (
	lengthCap     = 63
	lengthCapLink = 63
	lengthCapURL  = 2047
	amountCap     = 1024
)

var (
	linkRegex = regexp.MustCompile(`^[a-z][a-z0-9_-]+$`)
)

func validLinkID(linkid string) error {
	if len(linkid) == 0 {
		return nil
	}
	if len(linkid) < 3 {
		return governor.NewErrorUser("Link must be longer than 2 characters", http.StatusBadRequest, nil)
	}
	if len(linkid) > lengthCapLink {
		return governor.NewErrorUser("Link must be shorter than 64 characters", http.StatusBadRequest, nil)
	}
	if !linkRegex.MatchString(linkid) {
		return governor.NewErrorUser("Link can only contain a-z,0-9,_,-", http.StatusBadRequest, nil)
	}
	return nil
}

func validhasLinkID(linkid string) error {
	if len(linkid) == 0 {
		return governor.NewErrorUser("Link must be provided", http.StatusBadRequest, nil)
	}
	if len(linkid) > lengthCapLink {
		return governor.NewErrorUser("Link is too long", http.StatusBadRequest, nil)
	}
	return nil
}

func validURL(rawurl string) error {
	if len(rawurl) == 0 {
		return governor.NewErrorUser("Url must be provided", http.StatusBadRequest, nil)
	}
	if len(rawurl) > lengthCapURL {
		return governor.NewErrorUser("Url is too long", http.StatusBadRequest, nil)
	}
	if _, err := url.ParseRequestURI(rawurl); err != nil {
		return governor.NewErrorUser("Url is invalid", http.StatusBadRequest, nil)
	}
	return nil
}

func validhasCreatorID(creatorid string) error {
	if len(creatorid) == 0 {
		return governor.NewErrorUser("Creatorid must be provided", http.StatusBadRequest, nil)
	}
	if len(creatorid) > lengthCap {
		return governor.NewErrorUser("Creatorid is too large", http.StatusBadRequest, nil)
	}
	return nil
}

func validAmount(amt int) error {
	if amt == 0 {
		return governor.NewErrorUser("Amount is invalid", http.StatusBadRequest, nil)
	}
	if amt > amountCap {
		return governor.NewErrorUser("Amount is too large", http.StatusBadRequest, nil)
	}
	return nil
}

func validOffset(offset int) error {
	if offset == 0 {
		return governor.NewErrorUser("Offset is invalid", http.StatusBadRequest, nil)
	}
	return nil
}
<commit_msg>Bugfix courier valid offset<commit_after>package courier

import (
	"github.com/hackform/governor"
	"net/http"
	"net/url"
	"regexp"
)

const (
	lengthCap     = 63
	lengthCapLink = 63
	lengthCapURL  = 2047
	amountCap     = 1024
)

var (
	linkRegex = regexp.MustCompile(`^[a-z][a-z0-9_-]+$`)
)

func validLinkID(linkid string) error {
	if len(linkid) == 0 {
		return nil
	}
	if len(linkid) < 3 {
		return governor.NewErrorUser("Link must be longer than 2 characters", http.StatusBadRequest, nil)
	}
	if len(linkid) > lengthCapLink {
		return governor.NewErrorUser("Link must be shorter than 64 characters", http.StatusBadRequest, nil)
	}
	if !linkRegex.MatchString(linkid) {
		return governor.NewErrorUser("Link can only contain a-z,0-9,_,-", http.StatusBadRequest, nil)
	}
	return nil
}

func validhasLinkID(linkid string) error {
	if len(linkid) == 0 {
		return governor.NewErrorUser("Link must be provided", http.StatusBadRequest, nil)
	}
	if len(linkid) > lengthCapLink {
		return governor.NewErrorUser("Link is too long", http.StatusBadRequest, nil)
	}
	return nil
}

func validURL(rawurl string) error {
	if len(rawurl) == 0 {
		return governor.NewErrorUser("Url must be provided", http.StatusBadRequest, nil)
	}
	if len(rawurl) > lengthCapURL {
		return governor.NewErrorUser("Url is too long", http.StatusBadRequest, nil)
	}
	if _, err := url.ParseRequestURI(rawurl); err != nil {
		return governor.NewErrorUser("Url is invalid", http.StatusBadRequest, nil)
	}
	return nil
}

func validhasCreatorID(creatorid string) error {
	if len(creatorid) == 0 {
		return governor.NewErrorUser("Creatorid must be provided", http.StatusBadRequest, nil)
	}
	if len(creatorid) > lengthCap {
		return governor.NewErrorUser("Creatorid is too large", http.StatusBadRequest, nil)
	}
	return nil
}

func validAmount(amt int) error {
	if amt == 0 {
		return governor.NewErrorUser("Amount is invalid", http.StatusBadRequest, nil)
	}
	if amt > amountCap {
		return governor.NewErrorUser("Amount is too large", http.StatusBadRequest, nil)
	}
	return nil
}

func validOffset(offset int) error {
	if offset < 0 {
		return governor.NewErrorUser("Offset is invalid", http.StatusBadRequest, nil)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"
	"sort"
	"strconv"
	"time"
)

func Shuffle(data sort.Interface) {
	length := data.Len()

	for i := 0; i < length; i++ {
		i2 := rand.Intn(i + 1)
		data.Swap(i, i2)
	}
}

type Maze struct {
	c    []Cell
	w, h int
}

func RandomMaze(w, h int) *Maze {
	m := &Maze{
		c: make([]Cell, w*h),

		w: w,
		h: h,
	}

	for i := range m.c {
		m.c[i].set = i
	}

	m.randomize()

	return m
}

func (m Maze) neighbor(i, p int) (n *Cell, b int) {
	switch p {
	case up:
		if i < m.w {
			return nil, -1
		}
		return &m.c[i-m.w], down

	case down:
		if i >= len(m.c)-m.w {
			return nil, -1
		}
		return &m.c[i+m.w], up

	case left:
		if i%m.w == 0 {
			return nil, -1
		}
		return &m.c[i-1], right

	case right:
		if (i+1)%m.w == 0 {
			return nil, -1
		}
		return &m.c[i+1], left
	}

	panic("Unexpected path value: " + strconv.FormatInt(int64(p), 10))
}

func (m *Maze) randomize() {
	indexes := make(sort.IntSlice, len(m.c))
	for i := range indexes {
		indexes[i] = i
	}
	Shuffle(indexes)

	paths := sort.IntSlice{up, down, left, right}

	for _, i := range indexes {
		c := &m.c[i]

		Shuffle(paths)

		for _, p := range paths {
			n, b := m.neighbor(i, p)
			if (n == nil) || (c.set == n.set) {
				continue
			}

			c.paths[p] = n
			n.paths[b] = c

			old := n.set
			for i := range m.c {
				if m.c[i].set == old {
					m.c[i].set = c.set
				}
			}
		}
	}
}

func (m Maze) At(x, y int) *Cell {
	return &m.c[y*m.w+x]
}

const (
	up = iota
	down
	left
	right
)

type Cell struct {
	paths [4]*Cell

	set int
}

func (c Cell) Walls() (u, d, l, r bool) {
	u = c.paths[up] == nil
	d = c.paths[down] == nil
	l = c.paths[left] == nil
	r = c.paths[right] == nil

	return
}

func init() {
	rand.Seed(time.Now().UnixNano())
}

func main() {
	m := RandomMaze(5, 5)

	fmt.Printf("|")
	for x := 0; x < m.w; x++ {
		fmt.Printf("--|")
	}
	fmt.Println()

	for y := 1; y < m.h; y++ {
		fmt.Printf("|")
		for x := 0; x < m.w; x++ {
			fmt.Printf("  ")

			_, _, _, r := m.At(x, y).Walls()
			if r {
				fmt.Printf("|")
			} else {
				fmt.Printf(" ")
			}
		}
		fmt.Println()

		fmt.Printf("|")
		for x := 0; x < m.w; x++ {
			_, d, _, _ := m.At(x, y).Walls()
			if d {
				fmt.Printf("--|")
			} else {
				fmt.Printf("  |")
			}
		}
		fmt.Println()
	}
}
<commit_msg>Replace Cell.Walls() with seperate methods.<commit_after>package main

import (
	"fmt"
	"math/rand"
	"sort"
	"strconv"
	"time"
)

func Shuffle(data sort.Interface) {
	length := data.Len()

	for i := 0; i < length; i++ {
		i2 := rand.Intn(i + 1)
		data.Swap(i, i2)
	}
}

type Maze struct {
	c    []Cell
	w, h int
}

func RandomMaze(w, h int) *Maze {
	m := &Maze{
		c: make([]Cell, w*h),

		w: w,
		h: h,
	}

	for i := range m.c {
		m.c[i].set = i
	}

	m.randomize()

	return m
}

func (m Maze) neighbor(i, p int) (n *Cell, b int) {
	switch p {
	case up:
		if i < m.w {
			return nil, -1
		}
		return &m.c[i-m.w], down

	case down:
		if i >= len(m.c)-m.w {
			return nil, -1
		}
		return &m.c[i+m.w], up

	case left:
		if i%m.w == 0 {
			return nil, -1
		}
		return &m.c[i-1], right

	case right:
		if (i+1)%m.w == 0 {
			return nil, -1
		}
		return &m.c[i+1], left
	}

	panic("Unexpected path value: " + strconv.FormatInt(int64(p), 10))
}

func (m *Maze) randomize() {
	indexes := make(sort.IntSlice, len(m.c))
	for i := range indexes {
		indexes[i] = i
	}
	Shuffle(indexes)

	paths := sort.IntSlice{up, down, left, right}

	for _, i := range indexes {
		c := &m.c[i]

		Shuffle(paths)

		for _, p := range paths {
			n, b := m.neighbor(i, p)
			if (n == nil) || (c.set == n.set) {
				continue
			}

			c.paths[p] = n
			n.paths[b] = c

			old := n.set
			for i := range m.c {
				if m.c[i].set == old {
					m.c[i].set = c.set
				}
			}
		}
	}
}

func (m Maze) At(x, y int) *Cell {
	return &m.c[y*m.w+x]
}

const (
	up = iota
	down
	left
	right
)

type Cell struct {
	paths [4]*Cell

	set int
}

func (c Cell) Up() *Cell {
	return c.paths[up]
}

func (c Cell) Down() *Cell {
	return c.paths[down]
}

func (c Cell) Left() *Cell {
	return c.paths[left]
}

func (c Cell) Right() *Cell {
	return c.paths[right]
}

func init() {
	rand.Seed(time.Now().UnixNano())
}

func main() {
	m := RandomMaze(5, 5)

	fmt.Printf("|")
	for x := 0; x < m.w; x++ {
		fmt.Printf("--|")
	}
	fmt.Println()

	for y := 1; y < m.h; y++ {
		fmt.Printf("|")
		for x := 0; x < m.w; x++ {
			fmt.Printf("  ")

			if m.At(x, y).Right() == nil {
				fmt.Printf("|")
			} else {
				fmt.Printf(" ")
			}
		}
		fmt.Println()

		fmt.Printf("|")
		for x := 0; x < m.w; x++ {
			if m.At(x, y).Down() == nil {
				fmt.Printf("--|")
			} else {
				fmt.Printf("  |")
			}
		}
		fmt.Println()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"github.com/bcicen/ctop/config"
	"github.com/bcicen/ctop/container"
	"github.com/bcicen/ctop/widgets"
	"github.com/bcicen/ctop/widgets/menu"
	ui "github.com/gizak/termui"
)

// MenuFn executes a menu window, returning the next menu or nil
type MenuFn func() MenuFn

var helpDialog = []menu.Item{
	{"<enter> - open container menu", ""},
	{"", ""},
	{"[a] - toggle display of all containers", ""},
	{"[f] - filter displayed containers", ""},
	{"[h] - open this help dialog", ""},
	{"[H] - toggle ctop header", ""},
	{"[s] - select container sort field", ""},
	{"[r] - reverse container sort order", ""},
	{"[o] - open single view", ""},
	{"[l] - view container logs ([t] to toggle timestamp when open)", ""},
	{"[S] - save current configuration to file", ""},
	{"[q] - exit ctop", ""},
}

func HelpMenu() MenuFn {
	ui.Clear()
	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()

	m := menu.NewMenu()
	m.BorderLabel = "Help"
	m.AddItems(helpDialog...)
	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		ui.Clear()
		ui.Render(m)
	})
	ui.Handle("/sys/kbd/", func(ui.Event) {
		ui.StopLoop()
	})
	ui.Loop()
	return nil
}

func FilterMenu() MenuFn {
	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()

	i := widgets.NewInput()
	i.BorderLabel = "Filter"
	i.SetY(ui.TermHeight() - i.Height)
	i.Data = config.GetVal("filterStr")
	ui.Render(i)

	// refresh container rows on input
	stream := i.Stream()
	go func() {
		for s := range stream {
			config.Update("filterStr", s)
			RefreshDisplay()
			ui.Render(i)
		}
	}()

	i.InputHandlers()
	ui.Handle("/sys/kbd/<escape>", func(ui.Event) {
		config.Update("filterStr", "")
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		config.Update("filterStr", i.Data)
		ui.StopLoop()
	})
	ui.Loop()
	return nil
}

func SortMenu() MenuFn {
	ui.Clear()
	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()

	m := menu.NewMenu()
	m.Selectable = true
	m.SortItems = true
	m.BorderLabel = "Sort Field"

	for _, field := range container.SortFields() {
		m.AddItems(menu.Item{field, ""})
	}

	// set cursor position to current sort field
	m.SetCursor(config.GetVal("sortField"))

	HandleKeys("up", m.Up)
	HandleKeys("down", m.Down)
	HandleKeys("exit", ui.StopLoop)

	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		config.Update("sortField", m.SelectedItem().Val)
		ui.StopLoop()
	})

	ui.Render(m)
	ui.Loop()
	return nil
}

func ContainerMenu() MenuFn {
	c := cursor.Selected()
	if c == nil {
		return nil
	}

	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()

	m := menu.NewMenu()
	m.Selectable = true
	m.BorderLabel = "Menu"

	items := []menu.Item{
		menu.Item{Val: "single", Label: "single view"},
		menu.Item{Val: "logs", Label: "log view"},
	}

	if c.Meta["state"] == "running" {
		items = append(items, menu.Item{Val: "stop", Label: "stop"})
		items = append(items, menu.Item{Val: "pause", Label: "pause"})
		items = append(items, menu.Item{Val: "restart", Label: "restart"})
	}
	if c.Meta["state"] == "exited" || c.Meta["state"] == "created" {
		items = append(items, menu.Item{Val: "start", Label: "start"})
		items = append(items, menu.Item{Val: "remove", Label: "remove"})
	}
	if c.Meta["state"] == "paused" {
		items = append(items, menu.Item{Val: "unpause", Label: "unpause"})
	}
	items = append(items, menu.Item{Val: "cancel", Label: "cancel"})

	m.AddItems(items...)
	ui.Render(m)

	var nextMenu MenuFn
	HandleKeys("up", m.Up)
	HandleKeys("down", m.Down)
	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		switch m.SelectedItem().Val {
		case "single":
			nextMenu = SingleView
		case "logs":
			nextMenu = LogMenu
		case "start":
			nextMenu = Confirm(confirmTxt("start", c.GetMeta("name")), c.Start)
		case "stop":
			nextMenu = Confirm(confirmTxt("stop", c.GetMeta("name")), c.Stop)
		case "remove":
			nextMenu = Confirm(confirmTxt("remove", c.GetMeta("name")), c.Remove)
		case "pause":
			nextMenu = Confirm(confirmTxt("pause", c.GetMeta("name")), c.Pause)
		case "unpause":
			nextMenu = Confirm(confirmTxt("unpause", c.GetMeta("name")), c.Unpause)
		case "restart":
			nextMenu = Confirm(confirmTxt("restart", c.GetMeta("name")), c.Restart)
		}
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/", func(ui.Event) {
		ui.StopLoop()
	})
	ui.Loop()
	return nextMenu
}

func LogMenu() MenuFn {

	c := cursor.Selected()
	if c == nil {
		return nil
	}

	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()

	logs, quit := logReader(c)
	m := widgets.NewTextView(logs)
	m.BorderLabel = fmt.Sprintf("Logs [%s]", c.GetMeta("name"))
	ui.Render(m)

	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		m.Resize()
	})
	ui.Handle("/sys/kbd/t", func(ui.Event) {
		m.Toggle()
	})
	ui.Handle("/sys/kbd/", func(ui.Event) {
		quit <- true
		ui.StopLoop()
	})
	ui.Loop()
	return nil
}

// Create a confirmation dialog with a given description string and
// func to perform if confirmed
func Confirm(txt string, fn func()) MenuFn {
	menu := func() MenuFn {
		ui.DefaultEvtStream.ResetHandlers()
		defer ui.DefaultEvtStream.ResetHandlers()

		m := menu.NewMenu()
		m.Selectable = true
		m.BorderLabel = "Confirm"
		m.SubText = txt

		items := []menu.Item{
			menu.Item{Val: "cancel", Label: "[c]ancel"},
			menu.Item{Val: "yes", Label: "[y]es"},
		}

		var response bool

		m.AddItems(items...)
		ui.Render(m)

		yes := func() {
			response = true
			ui.StopLoop()
		}

		no := func() {
			response = false
			ui.StopLoop()
		}

		HandleKeys("up", m.Up)
		HandleKeys("down", m.Down)
		HandleKeys("exit", no)
		ui.Handle("/sys/kbd/c", func(ui.Event) { no() })
		ui.Handle("/sys/kbd/y", func(ui.Event) { yes() })

		ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
			switch m.SelectedItem().Val {
			case "cancel":
				no()
			case "yes":
				yes()
			}
		})

		ui.Loop()
		if response {
			fn()
		}
		return nil
	}
	return menu
}

type toggleLog struct {
	timestamp time.Time
	message   string
}

func (t *toggleLog) Toggle(on bool) string {
	if on {
		return fmt.Sprintf("%s %s", t.timestamp.Format("2006-01-02T15:04:05.999Z07:00"), t.message)
	}
	return t.message
}

func logReader(container *container.Container) (logs chan widgets.ToggleText, quit chan bool) {

	logCollector := container.Logs()
	stream := logCollector.Stream()
	logs = make(chan widgets.ToggleText)
	quit = make(chan bool)

	go func() {
		for {
			select {
			case log := <-stream:
				logs <- &toggleLog{timestamp: log.Timestamp, message: log.Message}
			case <-quit:
				logCollector.Stop()
				close(logs)
				return
			}
		}
	}()
	return
}

func confirmTxt(a, n string) string { return fmt.Sprintf("%s container %s?", a, n) }
<commit_msg>add keyboard shortcuts to container menu<commit_after>package main

import (
	"fmt"
	"time"

	"github.com/bcicen/ctop/config"
	"github.com/bcicen/ctop/container"
	"github.com/bcicen/ctop/widgets"
	"github.com/bcicen/ctop/widgets/menu"
	ui "github.com/gizak/termui"
)

// MenuFn executes a menu window, returning the next menu or nil
type MenuFn func() MenuFn

var helpDialog = []menu.Item{
	{"<enter> - open container menu", ""},
	{"", ""},
	{"[a] - toggle display of all containers", ""},
	{"[f] - filter displayed containers", ""},
	{"[h] - open this help dialog", ""},
	{"[H] - toggle ctop header", ""},
	{"[s] - select container sort field", ""},
	{"[r] - reverse container sort order", ""},
	{"[o] - open single view", ""},
	{"[l] - view container logs ([t] to toggle timestamp when open)", ""},
	{"[S] - save current configuration to file", ""},
	{"[q] - exit ctop", ""},
}

func HelpMenu() MenuFn {
	ui.Clear()
	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()

	m := menu.NewMenu()
	m.BorderLabel = "Help"
	m.AddItems(helpDialog...)
	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		ui.Clear()
		ui.Render(m)
	})
	ui.Handle("/sys/kbd/", func(ui.Event) {
		ui.StopLoop()
	})
	ui.Loop()
	return nil
}

func FilterMenu() MenuFn {
	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()

	i := widgets.NewInput()
	i.BorderLabel = "Filter"
	i.SetY(ui.TermHeight() - i.Height)
	i.Data = config.GetVal("filterStr")
	ui.Render(i)

	// refresh container rows on input
	stream := i.Stream()
	go func() {
		for s := range stream {
			config.Update("filterStr", s)
			RefreshDisplay()
			ui.Render(i)
		}
	}()

	i.InputHandlers()
	ui.Handle("/sys/kbd/<escape>", func(ui.Event) {
		config.Update("filterStr", "")
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		config.Update("filterStr", i.Data)
		ui.StopLoop()
	})
	ui.Loop()
	return nil
}

func SortMenu() MenuFn {
	ui.Clear()
	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()

	m := menu.NewMenu()
	m.Selectable = true
	m.SortItems = true
	m.BorderLabel = "Sort Field"

	for _, field := range container.SortFields() {
		m.AddItems(menu.Item{field, ""})
	}

	// set cursor position to current sort field
	m.SetCursor(config.GetVal("sortField"))

	HandleKeys("up", m.Up)
	HandleKeys("down", m.Down)
	HandleKeys("exit", ui.StopLoop)

	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		config.Update("sortField", m.SelectedItem().Val)
		ui.StopLoop()
	})

	ui.Render(m)
	ui.Loop()
	return nil
}

func ContainerMenu() MenuFn {
	c := cursor.Selected()
	if c == nil {
		return nil
	}

	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()

	m := menu.NewMenu()
	m.Selectable = true
	m.BorderLabel = "Menu"

	items := []menu.Item{
		menu.Item{Val: "single", Label: "[o] single view"},
		menu.Item{Val: "logs", Label: "[l] log view"},
	}

	if c.Meta["state"] == "running" {
		items = append(items, menu.Item{Val: "stop", Label: "[s] stop"})
		items = append(items, menu.Item{Val: "pause", Label: "[p] pause"})
		items = append(items, menu.Item{Val: "restart", Label: "[r] restart"})
	}
	if c.Meta["state"] == "exited" || c.Meta["state"] == "created" {
		items = append(items, menu.Item{Val: "start", Label: "[s] start"})
		items = append(items, menu.Item{Val: "remove", Label: "[R] remove"})
	}
	if c.Meta["state"] == "paused" {
		items = append(items, menu.Item{Val: "unpause", Label: "[p] unpause"})
	}
	items = append(items, menu.Item{Val: "cancel", Label: "[c] cancel"})

	m.AddItems(items...)
	ui.Render(m)

	HandleKeys("up", m.Up)
	HandleKeys("down", m.Down)

	var selected string

	// shortcuts
	ui.Handle("/sys/kbd/o", func(ui.Event) {
		selected = "single"
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/l", func(ui.Event) {
		selected = "logs"
		ui.StopLoop()
	})
	if c.Meta["state"] != "paused" {
		ui.Handle("/sys/kbd/s", func(ui.Event) {
			if c.Meta["state"] == "running" {
				selected = "stop"
			} else {
				selected = "start"
			}
			ui.StopLoop()
		})
	}
	if c.Meta["state"] != "exited" || c.Meta["state"] != "created" {
		ui.Handle("/sys/kbd/p", func(ui.Event) {
			if c.Meta["state"] == "paused" {
				selected = "unpause"
			} else {
				selected = "pause"
			}
			ui.StopLoop()
		})
	}
	if c.Meta["state"] == "running" {
		ui.Handle("/sys/kbd/r", func(ui.Event) {
			selected = "restart"
			ui.StopLoop()
		})
	}
	ui.Handle("/sys/kbd/R", func(ui.Event) {
		selected = "remove"
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/c", func(ui.Event) {
		ui.StopLoop()
	})

	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		selected = m.SelectedItem().Val
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/", func(ui.Event) {
		ui.StopLoop()
	})
	ui.Loop()

	var nextMenu MenuFn
	switch selected {
	case "single":
		nextMenu = SingleView
	case "logs":
		nextMenu = LogMenu
	case "start":
		nextMenu = Confirm(confirmTxt("start", c.GetMeta("name")), c.Start)
	case "stop":
		nextMenu = Confirm(confirmTxt("stop", c.GetMeta("name")), c.Stop)
	case "remove":
		nextMenu = Confirm(confirmTxt("remove", c.GetMeta("name")), c.Remove)
	case "pause":
		nextMenu = Confirm(confirmTxt("pause", c.GetMeta("name")), c.Pause)
	case "unpause":
		nextMenu = Confirm(confirmTxt("unpause", c.GetMeta("name")), c.Unpause)
	case "restart":
		nextMenu = Confirm(confirmTxt("restart", c.GetMeta("name")), c.Restart)
	}

	return nextMenu
}

func LogMenu() MenuFn {

	c := cursor.Selected()
	if c == nil {
		return nil
	}

	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()

	logs, quit := logReader(c)
	m := widgets.NewTextView(logs)
	m.BorderLabel = fmt.Sprintf("Logs [%s]", c.GetMeta("name"))
	ui.Render(m)

	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		m.Resize()
	})
	ui.Handle("/sys/kbd/t", func(ui.Event) {
		m.Toggle()
	})
	ui.Handle("/sys/kbd/", func(ui.Event) {
		quit <- true
		ui.StopLoop()
	})
	ui.Loop()
	return nil
}

// Create a confirmation dialog with a given description string and
// func to perform if confirmed
func Confirm(txt string, fn func()) MenuFn {
	menu := func() MenuFn {
		ui.DefaultEvtStream.ResetHandlers()
		defer ui.DefaultEvtStream.ResetHandlers()

		m := menu.NewMenu()
		m.Selectable = true
		m.BorderLabel = "Confirm"
		m.SubText = txt

		items := []menu.Item{
			menu.Item{Val: "cancel", Label: "[c]ancel"},
			menu.Item{Val: "yes", Label: "[y]es"},
		}

		var response bool

		m.AddItems(items...)
		ui.Render(m)

		yes := func() {
			response = true
			ui.StopLoop()
		}

		no := func() {
			response = false
			ui.StopLoop()
		}

		HandleKeys("up", m.Up)
		HandleKeys("down", m.Down)
		HandleKeys("exit", no)
		ui.Handle("/sys/kbd/c", func(ui.Event) { no() })
		ui.Handle("/sys/kbd/y", func(ui.Event) { yes() })

		ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
			switch m.SelectedItem().Val {
			case "cancel":
				no()
			case "yes":
				yes()
			}
		})

		ui.Loop()
		if response {
			fn()
		}
		return nil
	}
	return menu
}

type toggleLog struct {
	timestamp time.Time
	message   string
}

func (t *toggleLog) Toggle(on bool) string {
	if on {
		return fmt.Sprintf("%s %s", t.timestamp.Format("2006-01-02T15:04:05.999Z07:00"), t.message)
	}
	return t.message
}

func logReader(container *container.Container) (logs chan widgets.ToggleText, quit chan bool) {

	logCollector := container.Logs()
	stream := logCollector.Stream()
	logs = make(chan widgets.ToggleText)
	quit = make(chan bool)

	go func() {
		for {
			select {
			case log := <-stream:
				logs <- &toggleLog{timestamp: log.Timestamp, message: log.Message}
			case <-quit:
				logCollector.Stop()
				close(logs)
				return
			}
		}
	}()
	return
}

func confirmTxt(a, n string) string { return fmt.Sprintf("%s container %s?", a, n) }
<|endoftext|>


<commit_before>// +build e2e

/*
Copyright 2018 The Knative Authors

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

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

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

package e2e

import (
	"bytes"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"

	"github.com/knative/serving/test"
	"go.uber.org/zap"
)

const (
	appYaml              = "test_images/helloworld/helloworld.yaml"
	yamlImagePlaceholder = "github.com/knative/serving/test_images/helloworld"
	ingressTimeout       = 5 * time.Minute
	servingTimeout       = 2 * time.Minute
	checkInterval        = 2 * time.Second
)

func noStderrShell(name string, arg ...string) string {
	var void bytes.Buffer
	cmd := exec.Command(name, arg...)
	cmd.Stderr = &void
	out, _ := cmd.Output()
	return string(out)
}

func cleanup(yamlFilename string, logger *zap.SugaredLogger) {
	exec.Command("kubectl", "delete", "-f", yamlFilename).Run()
	os.Remove(yamlFilename)
	// There seems to be an Istio bug where if we delete / create
	// VirtualServices too quickly we will hit pro-longed "No health
	// upstream" causing timeouts.  Adding this small sleep to
	// sidestep the issue.
	//
	// TODO(#1376):  Fix this when upstream fix is released.
	logger.Info("Sleeping for 20 seconds after Route deletion to avoid hitting issue in #1376")
	time.Sleep(20 * time.Second)
}

func TestHelloWorldFromShell(t *testing.T) {
	//add test case specific name to its own logger
	logger := test.GetContextLogger("TestHelloWorldFromShell")

	imagePath := strings.Join([]string{test.Flags.DockerRepo, "helloworld"}, "/")

	logger.Infof("Creating manifest")

	// Create manifest file.
	newYaml, err := ioutil.TempFile("", "helloworld")
	if err != nil {
		t.Fatalf("Failed to create temporary manifest: %v", err)
	}
	newYamlFilename := newYaml.Name()
	defer cleanup(newYamlFilename, logger)
	test.CleanupOnInterrupt(func() { cleanup(newYamlFilename, logger) }, logger)

	// Populate manifets file with the real path to the container
	content, err := ioutil.ReadFile(appYaml)
	if err != nil {
		t.Fatalf("Failed to read file %s: %v", appYaml, err)
	}
	realContent := strings.Replace(string(content), yamlImagePlaceholder, imagePath, -1)
	if _, err = newYaml.WriteString(realContent); err != nil {
		t.Fatalf("Failed to write new manifest: %v", err)
	}
	if err = newYaml.Close(); err != nil {
		t.Fatalf("Failed to close new manifest file: %v", err)
	}

	logger.Infof("Manifest file is '%s'", newYamlFilename)
	logger.Info("Deploying using kubectl")

	// Deply using kubectl
	if err = exec.Command("kubectl", "apply", "-f", newYamlFilename).Run(); err != nil {
		t.Fatalf("Error running kubectl: %v", err)
	}

	logger.Info("Waiting for ingress to come up")

	// Wait for ingress to come up
	serviceIP := ""
	serviceHost := ""
	timeout := ingressTimeout
	for (serviceIP == "" || serviceHost == "") && timeout >= 0 {
		serviceHost = noStderrShell("kubectl", "get", "route", "route-example", "-o", "jsonpath={.status.domain}")
		serviceIP = noStderrShell("kubectl", "get", "svc", "knative-ingressgateway", "-n", "istio-system",
			"-o", "jsonpath={.status.loadBalancer.ingress[*]['ip']}")
		time.Sleep(checkInterval)
		timeout = timeout - checkInterval
	}
	if serviceIP == "" || serviceHost == "" {
		// serviceHost or serviceIP might contain a useful error, dump them.
		t.Fatalf("Ingress not found (IP='%s', host='%s')", serviceIP, serviceHost)
	}
	logger.Infof("Ingress is at %s/%s", serviceIP, serviceHost)

	logger.Info("Accessing app using curl")

	outputString := ""
	timeout = servingTimeout
	for outputString != helloWorldExpectedOutput && timeout >= 0 {
		output, err := exec.Command("curl", "--header", "Host:"+serviceHost, "http://"+serviceIP).Output()
		errorString := "none"
		time.Sleep(checkInterval)
		timeout = timeout - checkInterval
		if err != nil {
			errorString = err.Error()
		}
		outputString = strings.TrimSpace(string(output))
		logger.Infof("App replied with '%s' (error: %s)", outputString, errorString)
	}

	if outputString != helloWorldExpectedOutput {
		t.Fatalf("Timeout waiting for app to start serving")
	}
	logger.Info("App is serving")
}
<commit_msg>Dump kubectl stderr when it fails in TestHelloWorldFromShell (#1587)<commit_after>// +build e2e

/*
Copyright 2018 The Knative Authors

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

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

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

package e2e

import (
	"bytes"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"

	"github.com/knative/serving/test"
	"go.uber.org/zap"
)

const (
	appYaml              = "test_images/helloworld/helloworld.yaml"
	yamlImagePlaceholder = "github.com/knative/serving/test_images/helloworld"
	ingressTimeout       = 5 * time.Minute
	servingTimeout       = 2 * time.Minute
	checkInterval        = 2 * time.Second
)

func noStderrShell(name string, arg ...string) string {
	var void bytes.Buffer
	cmd := exec.Command(name, arg...)
	cmd.Stderr = &void
	out, _ := cmd.Output()
	return string(out)
}

func cleanup(yamlFilename string, logger *zap.SugaredLogger) {
	exec.Command("kubectl", "delete", "-f", yamlFilename).Run()
	os.Remove(yamlFilename)
	// There seems to be an Istio bug where if we delete / create
	// VirtualServices too quickly we will hit pro-longed "No health
	// upstream" causing timeouts.  Adding this small sleep to
	// sidestep the issue.
	//
	// TODO(#1376):  Fix this when upstream fix is released.
	logger.Info("Sleeping for 20 seconds after Route deletion to avoid hitting issue in #1376")
	time.Sleep(20 * time.Second)
}

func TestHelloWorldFromShell(t *testing.T) {
	//add test case specific name to its own logger
	logger := test.GetContextLogger("TestHelloWorldFromShell")

	imagePath := strings.Join([]string{test.Flags.DockerRepo, "helloworld"}, "/")

	logger.Infof("Creating manifest")

	// Create manifest file.
	newYaml, err := ioutil.TempFile("", "helloworld")
	if err != nil {
		t.Fatalf("Failed to create temporary manifest: %v", err)
	}
	newYamlFilename := newYaml.Name()
	defer cleanup(newYamlFilename, logger)
	test.CleanupOnInterrupt(func() { cleanup(newYamlFilename, logger) }, logger)

	// Populate manifets file with the real path to the container
	content, err := ioutil.ReadFile(appYaml)
	if err != nil {
		t.Fatalf("Failed to read file %s: %v", appYaml, err)
	}
	realContent := strings.Replace(string(content), yamlImagePlaceholder, imagePath, -1)
	if _, err = newYaml.WriteString(realContent); err != nil {
		t.Fatalf("Failed to write new manifest: %v", err)
	}
	if err = newYaml.Close(); err != nil {
		t.Fatalf("Failed to close new manifest file: %v", err)
	}

	logger.Infof("Manifest file is '%s'", newYamlFilename)
	logger.Info("Deploying using kubectl")

	// Deploy using kubectl
	if output, err := exec.Command("kubectl", "apply", "-f", newYamlFilename).CombinedOutput(); err != nil {
		t.Fatalf("Error running kubectl: %v", strings.TrimSpace(string(output)))
	}

	logger.Info("Waiting for ingress to come up")

	// Wait for ingress to come up
	serviceIP := ""
	serviceHost := ""
	timeout := ingressTimeout
	for (serviceIP == "" || serviceHost == "") && timeout >= 0 {
		serviceHost = noStderrShell("kubectl", "get", "route", "route-example", "-o", "jsonpath={.status.domain}")
		serviceIP = noStderrShell("kubectl", "get", "svc", "knative-ingressgateway", "-n", "istio-system",
			"-o", "jsonpath={.status.loadBalancer.ingress[*]['ip']}")
		time.Sleep(checkInterval)
		timeout = timeout - checkInterval
	}
	if serviceIP == "" || serviceHost == "" {
		// serviceHost or serviceIP might contain a useful error, dump them.
		t.Fatalf("Ingress not found (IP='%s', host='%s')", serviceIP, serviceHost)
	}
	logger.Infof("Ingress is at %s/%s", serviceIP, serviceHost)

	logger.Info("Accessing app using curl")

	outputString := ""
	timeout = servingTimeout
	for outputString != helloWorldExpectedOutput && timeout >= 0 {
		output, err := exec.Command("curl", "--header", "Host:"+serviceHost, "http://"+serviceIP).Output()
		errorString := "none"
		time.Sleep(checkInterval)
		timeout = timeout - checkInterval
		if err != nil {
			errorString = err.Error()
		}
		outputString = strings.TrimSpace(string(output))
		logger.Infof("App replied with '%s' (error: %s)", outputString, errorString)
	}

	if outputString != helloWorldExpectedOutput {
		t.Fatalf("Timeout waiting for app to start serving")
	}
	logger.Info("App is serving")
}
<|endoftext|>


<commit_before>package sfauth

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strings"
	"time"

	"github.com/zmj/sfslack/sharefile"
)

type oauthToken struct {
	accessToken  string            `json:"access_token,omitempty"`
	refreshToken string            `json:"refresh_token,omitempty"`
	expiresIn    int               `json:"expires_in,omitempty"`
	account      sharefile.Account `json:"-"`
	expiresAt    time.Time         `json:"-"`
}

type oauthCode struct {
	code    string
	account sharefile.Account
}

type login struct {
	token   oauthToken
	account sharefile.Account
	client  *http.Client
}

func (login login) withCredentials(req *http.Request) *http.Request {
	if login.client.Jar == nil {
		jar, _ := cookiejar.New(nil)
		login.client.Jar = jar
	}
	url, _ := url.Parse(fmt.Sprintf("https://%v.%v", login.account.Subdomain, login.account.APIControlPlane))
	cookies := login.client.Jar.Cookies(url)
	if len(cookies) == 0 && len(req.Header.Get("Authorization")) == 0 {
		req.Header.Add("Authorization", "Bearer "+login.token.accessToken)
	}
	return req
}

func parseOAuthCode(values url.Values) (oauthCode, error) {
	code := oauthCode{
		account: sharefile.Account{
			Subdomain:       values.Get("subdomain"),
			AppControlPlane: values.Get("appcp"),
			APIControlPlane: values.Get("apicp"),
		},
		code: values.Get("code"),
	}
	// validate
	return code, nil
}

func (code oauthCode) getToken(oauthID, oauthSecret string) (oauthToken, error) {
	values := map[string]string{
		"client_id":     oauthID,
		"client_secret": oauthSecret,
		"code":          code.code,
		"grant_type":    "authorization_code",
	}
	return tokenPost(code.account, values)
}

func (token oauthToken) refresh(oauthID, oauthSecret string) (oauthToken, error) {
	values := map[string]string{
		"client_id":     oauthID,
		"client_secret": oauthSecret,
		"refresh_token": token.refreshToken,
		"grant_type":    "refresh_token",
	}
	return tokenPost(token.account, values)
}

func tokenPost(acct sharefile.Account, values map[string]string) (oauthToken, error) {
	var valuePairs []string
	for k, v := range values {
		valuePairs = append(valuePairs, fmt.Sprintf("%v=%v", k, v))
	}
	toSend := strings.Join(valuePairs, "&")
	req, err := http.NewRequest("POST",
		fmt.Sprintf("https://%v.%v/oauth/token?requirev3=true", acct.Subdomain, acct.AppControlPlane),
		strings.NewReader(toSend))
	if err != nil {
		return oauthToken{}, err
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	hc := http.Client{}
	resp, err := hc.Do(req)
	if err != nil {
		return oauthToken{}, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return oauthToken{}, errors.New(resp.Status)
	}

	token := oauthToken{}
	err = json.NewDecoder(resp.Body).Decode(&token)
	if err != nil {
		return oauthToken{}, err
	}
	token.account = acct
	token = token.withExpiresAt()
	return token, nil
}

func (token oauthToken) withExpiresAt() oauthToken {
	d := time.Duration(token.expiresIn) * time.Second
	token.expiresAt = time.Now().Add(d)
	return token
}

func (login *login) Account() sharefile.Account {
	return login.token.account
}

func (login *login) Do(req *http.Request) (*http.Response, error) {
	req = login.withCredentials(req)
	resp, err := login.client.Do(req)
	if err != nil {
		return resp, err
	}
	if resp.StatusCode == http.StatusUnauthorized {
		resp.Body.Close()
		login.client.Jar = nil
		req = login.withCredentials(req)
		resp, err = login.client.Do(req)
	}
	return resp, err
}
<commit_msg>undo fancy<commit_after>package sfauth

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strings"
	"time"

	"github.com/zmj/sfslack/sharefile"
)

type oauthToken struct {
	accessToken  string            `json:"access_token,omitempty"`
	refreshToken string            `json:"refresh_token,omitempty"`
	expiresIn    int               `json:"expires_in,omitempty"`
	account      sharefile.Account `json:"-"`
	expiresAt    time.Time         `json:"-"`
}

type oauthCode struct {
	code    string
	account sharefile.Account
}

type login struct {
	token   oauthToken
	account sharefile.Account
	client  *http.Client
}

func (login *login) withCredentials(req *http.Request) *http.Request {
	if login.client.Jar == nil {
		jar, _ := cookiejar.New(nil)
		login.client.Jar = jar
	}
	url, _ := url.Parse(fmt.Sprintf("https://%v.%v", login.account.Subdomain, login.account.APIControlPlane))
	cookies := login.client.Jar.Cookies(url)
	if len(cookies) == 0 { // && len(req.Header.Get("Authorization")) == 0 {
		req.Header.Add("Authorization", "Bearer "+login.token.accessToken)
	}
	return req
}

func parseOAuthCode(values url.Values) (oauthCode, error) {
	code := oauthCode{
		account: sharefile.Account{
			Subdomain:       values.Get("subdomain"),
			AppControlPlane: values.Get("appcp"),
			APIControlPlane: values.Get("apicp"),
		},
		code: values.Get("code"),
	}
	// validate
	return code, nil
}

func (code oauthCode) getToken(oauthID, oauthSecret string) (oauthToken, error) {
	values := map[string]string{
		"client_id":     oauthID,
		"client_secret": oauthSecret,
		"code":          code.code,
		"grant_type":    "authorization_code",
	}
	return tokenPost(code.account, values)
}

func (token oauthToken) refresh(oauthID, oauthSecret string) (oauthToken, error) {
	values := map[string]string{
		"client_id":     oauthID,
		"client_secret": oauthSecret,
		"refresh_token": token.refreshToken,
		"grant_type":    "refresh_token",
	}
	return tokenPost(token.account, values)
}

func tokenPost(acct sharefile.Account, values map[string]string) (oauthToken, error) {
	var valuePairs []string
	for k, v := range values {
		valuePairs = append(valuePairs, fmt.Sprintf("%v=%v", k, v))
	}
	toSend := strings.Join(valuePairs, "&")
	req, err := http.NewRequest("POST",
		fmt.Sprintf("https://%v.%v/oauth/token?requirev3=true", acct.Subdomain, acct.AppControlPlane),
		strings.NewReader(toSend))
	if err != nil {
		return oauthToken{}, err
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	hc := http.Client{}
	resp, err := hc.Do(req)
	if err != nil {
		return oauthToken{}, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return oauthToken{}, errors.New(resp.Status)
	}

	token := oauthToken{}
	err = json.NewDecoder(resp.Body).Decode(&token)
	if err != nil {
		return oauthToken{}, err
	}
	token.account = acct
	token = token.withExpiresAt()
	return token, nil
}

func (token oauthToken) withExpiresAt() oauthToken {
	d := time.Duration(token.expiresIn) * time.Second
	token.expiresAt = time.Now().Add(d)
	return token
}

func (login *login) Account() sharefile.Account {
	return login.token.account
}

func (login *login) Do(req *http.Request) (*http.Response, error) {
	req = login.withCredentials(req)
	return login.client.Do(req)
	// clear jar and reauth on unauthorized
}
<|endoftext|>