<commit_before>/*
 * Copyright (C) 2016 Sam Kumar, Michael Andersen, and the University
 * of California, Berkeley.
 *
 * This file is part of Mr. Plotter (the Multi-Resolution Plotter).
 *
 * Mr. Plotter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Mr. Plotter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Mr. Plotter.  If not, see <http://www.gnu.org/licenses/>.
 */

/* This file contains the logic for users to login and change their password.
   It also contains the logic to keep track of login sessions. */

package main

import (
	"context"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha512"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"time"

	acl "github.com/BTrDB/smartgridstore/acl"
	etcd "github.com/coreos/etcd/clientv3"
)

var sessionExpirySeconds uint64

var aes_encrypt_cipher cipher.Block
var hmac_key []byte

type LoginSession struct {
	Issued   int64
	Prefixes map[string]struct{}
	User     string
}

func (loginsession *LoginSession) PrefixSlice() []string {
	prefixlist := make([]string, len(loginsession.Prefixes))
	for pfx := range loginsession.Prefixes {
		prefixlist = append(prefixlist, pfx)
	}
	return prefixlist
}

func setSessionExpiry(seconds uint64) {
	sessionExpirySeconds = seconds
}

func setEncryptKey(key []byte) error {
	var keylen = len(key)
	if keylen != 16 && keylen != 24 && keylen != 32 {
		return fmt.Errorf("Key length is invalid: must be 16, 24, or 32 bytes (got %d bytes)", keylen)
	}
	cipher, err := aes.NewCipher(key)
	if err == nil {
		aes_encrypt_cipher = cipher
	}
	return err
}

func setMACKey(key []byte) error {
	var keylen int = len(key)
	if keylen < 16 {
		return fmt.Errorf("Key length must be at least 16 bytes (got %d bytes)", keylen)
	}
	hmac_key = key
	return nil
}

func checkpassword(ctx context.Context, etcdConn *etcd.Client, user string, password []byte) (*acl.User, error) {
	ae := acl.NewACLEngine("btrdb", etcdConn)
	okay, u, err := ae.AuthenticateUser(user, string(password))
	if err != nil {
		return nil, err
	}
	if !okay {
		return nil, nil
	}
	for _, c := range u.Capabilities {
		if c == "plotter" {
			return u, nil
		}
	}
	return nil, nil
}

/* Writing to the returned slice results in undefined behavior.
   The returned slice is guaranteed to have a length of TOKEN_BYTE_LEN. */
func userlogin(ctx context.Context, etcdConn *etcd.Client, user string, password []byte) ([]byte, error) {
	acc, err := checkpassword(ctx, etcdConn, user, password)
	if err != nil {
		return nil, err
	} else if acc == nil {
		/* Wrong password */
		return nil, nil
	}

	// Create a new session
	loginsession := &LoginSession{
		Issued:   time.Now().Unix(),
		Prefixes: make(map[string]struct{}),
		User:     user,
	}
	for _, p := range acc.Prefixes {
		loginsession.Prefixes[p] = struct{}{}
	}

	// Construct the JSON plaintext for this login session
	var plaintext []byte
	plaintext, err = json.Marshal(loginsession)
	if err != nil {
		log.Fatalf("Could not JSON-encode login session: %v", err)
	}
	var blocksize = aes_encrypt_cipher.BlockSize()
	var paddinglen = (blocksize - (len(plaintext) % blocksize)) % blocksize
	var padding = make([]byte, paddinglen)
	plaintext = append(plaintext, padding...)

	// Encrypt and MAC the plaintext to get the token
	// The token consists of the IV, ciphertext, and HMAC concatenated
	var hmac_hash = hmac.New(sha512.New, hmac_key)
	var macsize = hmac_hash.Size()
	var token = make([]byte, blocksize+len(plaintext), blocksize+len(plaintext)+macsize)
	var iv = token[:blocksize]
	var ciphertext = token[blocksize:]

	_, err = io.ReadFull(rand.Reader, iv)
	if err != nil {
		log.Fatalf("Could not generate IV: %v", err)
	}

	var encrypter = cipher.NewCBCEncrypter(aes_encrypt_cipher, iv)
	encrypter.CryptBlocks(ciphertext, plaintext)

	_, err = hmac_hash.Write(plaintext)
	if err != nil {
		log.Fatalf("Could not compute HMAC of plaintext token: %v", err)
	}
	token = hmac_hash.Sum(token)

	return token, nil
}

func decodetoken(token []byte) []byte {
	var hmac_hash = hmac.New(sha512.New, hmac_key)

	var blocksize = aes_encrypt_cipher.BlockSize()
	var macsize = hmac_hash.Size()

	if len(token) <= blocksize+macsize {
		return nil
	}

	var iv = token[:blocksize]
	var ciphertext = token[blocksize : len(token)-macsize]
	var mac = token[len(token)-macsize:]

	if (len(ciphertext) % blocksize) != 0 {
		return nil
	}

	var plaintext = make([]byte, len(ciphertext))
	var decrypter = cipher.NewCBCDecrypter(aes_encrypt_cipher, iv)
	decrypter.CryptBlocks(plaintext, ciphertext)

	_, err := hmac_hash.Write(plaintext)
	if err != nil {
		log.Fatalf("Could not compute HMAC of plaintext token: %v", err)
	}
	var computedmac = hmac_hash.Sum(make([]byte, 0, macsize))

	if !hmac.Equal(computedmac, mac) {
		log.Printf("Invalid MAC detected: someone is trying to forge a token!")
		return nil
	}

	return plaintext
}

func stolenkeys() {
	log.Println("THE MAC KEY HAS BEEN STOLEN, AND THE ENCRYPT KEY PROBABLY TOO. CHANGE THE KEYS AND RESTART THIS PROGRAM.")
}

func getloginsession(token []byte) *LoginSession {
	var plaintext = decodetoken(token)
	if plaintext == nil {
		return nil
	}

	var i int
	for i = len(plaintext) - 1; i >= 0; i-- {
		if plaintext[i] != 0 {
			break
		}
	}

	if len(plaintext)-i-1 >= aes_encrypt_cipher.BlockSize() {
		log.Println("Invalid padding on token is correctly MAC'ed")
		stolenkeys()
		return nil
	}

	var rawjson = plaintext[:i+1]
	var loginsession *LoginSession
	var err = json.Unmarshal(rawjson, &loginsession)
	if err != nil {
		log.Printf("Correctly MAC'ed token is incorrect JSON: %v", err)
		stolenkeys()
		return nil
	}
	if loginsession == nil {
		log.Println("Correctly MAC'ed token is null")
		stolenkeys()
		return nil
	}

	var now = time.Now().Unix()
	if uint64(now-loginsession.Issued) >= sessionExpirySeconds {
		log.Printf("Session expired: (issued at %v, expired at %v, now is %v)", loginsession.Issued, loginsession.Issued+int64(sessionExpirySeconds), now)
		return nil
	}

	return loginsession
}

func userlogoff(token []byte) bool {
	loginsession := getloginsession(token)
	if loginsession == nil {
		return false
	}

	return true
}

func userprefixes(token []byte) []string {
	loginsession := getloginsession(token)
	if loginsession == nil {
		return nil
	}

	return loginsession.PrefixSlice()
}

// func userchangepassword(ctx context.Context, etcdConn *etcd.Client, token []byte, oldpw []byte, newpw []byte) string {
// 	loginsession := getloginsession(token)
// 	if loginsession == nil {
// 		return ERROR_INVALID_TOKEN
// 	}
//
// 	acc, err := accounts.RetrieveAccount(ctx, etcdConn, loginsession.User)
// 	if err != nil {
// 		return "Server error"
// 	}
//
// 	correct, err := acc.CheckPassword(oldpw)
// 	if err != nil {
// 		return "Server error"
// 	}
//
// 	if !correct {
// 		return "Incorrect password"
// 	}
//
// 	err = acc.SetPassword(newpw)
// 	if err != nil {
// 		return "Server error"
// 	}
//
// 	success, err := accounts.UpsertAccountAtomically(ctx, etcdConn, acc)
// 	if err != nil {
// 		return "Server error"
// 	}
//
// 	if success {
// 		return SUCCESS
// 	} else {
// 		return "Transaction failed; try again"
// 	}
// }
<commit_msg>Fix for new ACL<commit_after>/*
 * Copyright (C) 2016 Sam Kumar, Michael Andersen, and the University
 * of California, Berkeley.
 *
 * This file is part of Mr. Plotter (the Multi-Resolution Plotter).
 *
 * Mr. Plotter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Mr. Plotter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Mr. Plotter.  If not, see <http://www.gnu.org/licenses/>.
 */

/* This file contains the logic for users to login and change their password.
   It also contains the logic to keep track of login sessions. */

package main

import (
	"context"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha512"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"time"

	acl "github.com/BTrDB/smartgridstore/acl"
	etcd "github.com/coreos/etcd/clientv3"
)

var sessionExpirySeconds uint64

var aes_encrypt_cipher cipher.Block
var hmac_key []byte

type LoginSession struct {
	Issued   int64
	Prefixes map[string]struct{}
	User     string
}

func (loginsession *LoginSession) PrefixSlice() []string {
	prefixlist := make([]string, len(loginsession.Prefixes))
	for pfx := range loginsession.Prefixes {
		prefixlist = append(prefixlist, pfx)
	}
	return prefixlist
}

func setSessionExpiry(seconds uint64) {
	sessionExpirySeconds = seconds
}

func setEncryptKey(key []byte) error {
	var keylen = len(key)
	if keylen != 16 && keylen != 24 && keylen != 32 {
		return fmt.Errorf("Key length is invalid: must be 16, 24, or 32 bytes (got %d bytes)", keylen)
	}
	cipher, err := aes.NewCipher(key)
	if err == nil {
		aes_encrypt_cipher = cipher
	}
	return err
}

func setMACKey(key []byte) error {
	var keylen int = len(key)
	if keylen < 16 {
		return fmt.Errorf("Key length must be at least 16 bytes (got %d bytes)", keylen)
	}
	hmac_key = key
	return nil
}

func checkpassword(ctx context.Context, etcdConn *etcd.Client, user string, password []byte) (*acl.User, error) {
	ae := acl.NewACLEngine("btrdb", etcdConn)
	okay, u, err := ae.AuthenticateUser(user, string(password))
	if err != nil {
		return nil, err
	}
	if !okay {
		return nil, nil
	}
	if u.HasCapability("plotter") {
		return u, nil
	}
	return nil, nil
}

/* Writing to the returned slice results in undefined behavior.
   The returned slice is guaranteed to have a length of TOKEN_BYTE_LEN. */
func userlogin(ctx context.Context, etcdConn *etcd.Client, user string, password []byte) ([]byte, error) {
	acc, err := checkpassword(ctx, etcdConn, user, password)
	if err != nil {
		return nil, err
	} else if acc == nil {
		/* Wrong password */
		return nil, nil
	}

	// Create a new session
	loginsession := &LoginSession{
		Issued:   time.Now().Unix(),
		Prefixes: make(map[string]struct{}),
		User:     user,
	}
	for _, g := range acc.FullGroups {
		for _, cap := range g.Capabilities {
			if cap == "plotter" {
				for _, p := range g.Prefixes {
					loginsession.Prefixes[p] = struct{}{}
				}
			}
		}
	}

	// Construct the JSON plaintext for this login session
	var plaintext []byte
	plaintext, err = json.Marshal(loginsession)
	if err != nil {
		log.Fatalf("Could not JSON-encode login session: %v", err)
	}
	var blocksize = aes_encrypt_cipher.BlockSize()
	var paddinglen = (blocksize - (len(plaintext) % blocksize)) % blocksize
	var padding = make([]byte, paddinglen)
	plaintext = append(plaintext, padding...)

	// Encrypt and MAC the plaintext to get the token
	// The token consists of the IV, ciphertext, and HMAC concatenated
	var hmac_hash = hmac.New(sha512.New, hmac_key)
	var macsize = hmac_hash.Size()
	var token = make([]byte, blocksize+len(plaintext), blocksize+len(plaintext)+macsize)
	var iv = token[:blocksize]
	var ciphertext = token[blocksize:]

	_, err = io.ReadFull(rand.Reader, iv)
	if err != nil {
		log.Fatalf("Could not generate IV: %v", err)
	}

	var encrypter = cipher.NewCBCEncrypter(aes_encrypt_cipher, iv)
	encrypter.CryptBlocks(ciphertext, plaintext)

	_, err = hmac_hash.Write(plaintext)
	if err != nil {
		log.Fatalf("Could not compute HMAC of plaintext token: %v", err)
	}
	token = hmac_hash.Sum(token)

	return token, nil
}

func decodetoken(token []byte) []byte {
	var hmac_hash = hmac.New(sha512.New, hmac_key)

	var blocksize = aes_encrypt_cipher.BlockSize()
	var macsize = hmac_hash.Size()

	if len(token) <= blocksize+macsize {
		return nil
	}

	var iv = token[:blocksize]
	var ciphertext = token[blocksize : len(token)-macsize]
	var mac = token[len(token)-macsize:]

	if (len(ciphertext) % blocksize) != 0 {
		return nil
	}

	var plaintext = make([]byte, len(ciphertext))
	var decrypter = cipher.NewCBCDecrypter(aes_encrypt_cipher, iv)
	decrypter.CryptBlocks(plaintext, ciphertext)

	_, err := hmac_hash.Write(plaintext)
	if err != nil {
		log.Fatalf("Could not compute HMAC of plaintext token: %v", err)
	}
	var computedmac = hmac_hash.Sum(make([]byte, 0, macsize))

	if !hmac.Equal(computedmac, mac) {
		log.Printf("Invalid MAC detected: someone is trying to forge a token!")
		return nil
	}

	return plaintext
}

func stolenkeys() {
	log.Println("THE MAC KEY HAS BEEN STOLEN, AND THE ENCRYPT KEY PROBABLY TOO. CHANGE THE KEYS AND RESTART THIS PROGRAM.")
}

func getloginsession(token []byte) *LoginSession {
	var plaintext = decodetoken(token)
	if plaintext == nil {
		return nil
	}

	var i int
	for i = len(plaintext) - 1; i >= 0; i-- {
		if plaintext[i] != 0 {
			break
		}
	}

	if len(plaintext)-i-1 >= aes_encrypt_cipher.BlockSize() {
		log.Println("Invalid padding on token is correctly MAC'ed")
		stolenkeys()
		return nil
	}

	var rawjson = plaintext[:i+1]
	var loginsession *LoginSession
	var err = json.Unmarshal(rawjson, &loginsession)
	if err != nil {
		log.Printf("Correctly MAC'ed token is incorrect JSON: %v", err)
		stolenkeys()
		return nil
	}
	if loginsession == nil {
		log.Println("Correctly MAC'ed token is null")
		stolenkeys()
		return nil
	}

	var now = time.Now().Unix()
	if uint64(now-loginsession.Issued) >= sessionExpirySeconds {
		log.Printf("Session expired: (issued at %v, expired at %v, now is %v)", loginsession.Issued, loginsession.Issued+int64(sessionExpirySeconds), now)
		return nil
	}

	return loginsession
}

func userlogoff(token []byte) bool {
	loginsession := getloginsession(token)
	if loginsession == nil {
		return false
	}

	return true
}

func userprefixes(token []byte) []string {
	loginsession := getloginsession(token)
	if loginsession == nil {
		return nil
	}

	return loginsession.PrefixSlice()
}

// func userchangepassword(ctx context.Context, etcdConn *etcd.Client, token []byte, oldpw []byte, newpw []byte) string {
// 	loginsession := getloginsession(token)
// 	if loginsession == nil {
// 		return ERROR_INVALID_TOKEN
// 	}
//
// 	acc, err := accounts.RetrieveAccount(ctx, etcdConn, loginsession.User)
// 	if err != nil {
// 		return "Server error"
// 	}
//
// 	correct, err := acc.CheckPassword(oldpw)
// 	if err != nil {
// 		return "Server error"
// 	}
//
// 	if !correct {
// 		return "Incorrect password"
// 	}
//
// 	err = acc.SetPassword(newpw)
// 	if err != nil {
// 		return "Server error"
// 	}
//
// 	success, err := accounts.UpsertAccountAtomically(ctx, etcdConn, acc)
// 	if err != nil {
// 		return "Server error"
// 	}
//
// 	if success {
// 		return SUCCESS
// 	} else {
// 		return "Transaction failed; try again"
// 	}
// }
<|endoftext|>


<commit_before>package main

import (
	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared/i18n"
)

type fingerCmd struct{}

func (c *fingerCmd) showByDefault() bool {
	return false
}

func (c *fingerCmd) usage() string {
	return i18n.G(
		`Check if the LXD instance is up.

lxc finger <remote>`)
}

func (c *fingerCmd) flags() {}

func (c *fingerCmd) run(config *lxd.Config, args []string) error {
	if len(args) > 1 {
		return errArgs
	}

	var remote string
	if len(args) == 1 {
		remote = config.ParseRemote(args[0])
	} else {
		remote = config.DefaultRemote
	}

	// New client may or may not need to connect to the remote host, but
	// client.ServerStatus will at least request the basic information from
	// the server.
	client, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}
	_, err = client.ServerStatus()
	return err
}
<commit_msg>finger: Update help<commit_after>package main

import (
	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared/i18n"
)

type fingerCmd struct{}

func (c *fingerCmd) showByDefault() bool {
	return false
}

func (c *fingerCmd) usage() string {
	return i18n.G(
		`Check if the LXD instance is up.

lxc finger [<remote>:]`)
}

func (c *fingerCmd) flags() {}

func (c *fingerCmd) run(config *lxd.Config, args []string) error {
	if len(args) > 1 {
		return errArgs
	}

	var remote string
	if len(args) == 1 {
		remote = config.ParseRemote(args[0])
	} else {
		remote = config.DefaultRemote
	}

	// New client may or may not need to connect to the remote host, but
	// client.ServerStatus will at least request the basic information from
	// the server.
	client, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}
	_, err = client.ServerStatus()
	return err
}
<|endoftext|>


<commit_before>package git

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/google/go-github/github"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

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

func TestParseRepositoryName(t *testing.T) {
	owner, repo := ParseRepositoryName("test/me")
	assert.Equal(t, owner, "test")
	assert.Equal(t, repo, "me")
}

func TestNewGithubRepositories_WithToken(t *testing.T) {
	ctx := context.WithValue(context.Background(), GithubToken, "secret_token")

	r, err := NewGithubRepositories(ctx)
	require.NoError(t, err, "Expected NewGithubRepositories to succeed")
	assert.NotNil(t, r, "Expected NewGithubRepositories to return new instance")
}

func TestNewGithubRepositories_NoToken(t *testing.T) {
	_, err := NewGithubRepositories(context.Background())
	assert.Error(t, err, "Expected NewGithubRepositories to return an error")
}

func TestNewGithubRepositories_EmptyToken(t *testing.T) {
	ctx := context.WithValue(context.Background(), GithubToken, "")

	_, err := NewGithubRepositories(ctx)
	assert.Error(t, err, "Expected NewGithubRepositories to return an error")
}

func TestGithubRepositoriesAll_SingleRepository_DefaultFields(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `[{
                    "full_name": "user1/repo1",
                    "git_url": "git@github.com:user1/repo1.git"
                }]`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repos, err := githubRepos.All()
	require.NoError(t, err)

	if assert.Len(t, repos, 1) {
		repo := repos[0]
		assert.Equal(t, repo.FullName, "user1/repo1")
		assert.Equal(t, repo.Master, "master")
		assert.Empty(t, repo.Description)
		assert.Empty(t, repo.GitURL)
		assert.Empty(t, repo.HTMLURL)
	}
}

func TestGithubRepositoriesAll_SingleRepository_AllFields(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `[{
                    "full_name": "user1/repo1",
                    "description": "Repo1",
                    "default_branch": "production",
                    "git_url": "git@github.com:user1/repo1.git",
                    "html_url": "https://github.com/user1/repo1"
                }]`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repos, err := githubRepos.All()
	require.NoError(t, err)

	if assert.Len(t, repos, 1) {
		repo := repos[0]
		assert.Equal(t, repo.FullName, "user1/repo1")
		assert.Equal(t, repo.Description, "Repo1")
		assert.Equal(t, repo.Master, "production")
		assert.Empty(t, repo.GitURL)
	}
}

func TestGithubRepositoriesAll_MultipleRepositories(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `[{
                    "full_name": "user1/repo1",
                    "git_url": "https://github.com/user1/repo1"
                },{
                    "full_name": "user2/repo2",
                    "git_url": "https://github.com/user2/repo2"
                }]`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repos, err := githubRepos.All()
	require.NoError(t, err)

	assert.Len(t, repos, 2)
}

func TestGithubRepositoriesAll_SkipWithoutGitURL(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `[{
                    "full_name": "user1/repo1"
                },{
                    "full_name": "user2/repo2",
                    "git_url": "git:git@github.com:user2/repo2.git"
                }]`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repos, err := githubRepos.All()
	require.NoError(t, err)

	if assert.Len(t, repos, 1, "Should exclude one repository without git_url") {
		assert.Equal(t, repos[0].FullName, "user2/repo2")
	}
}

func TestGithubRepositoriesAll_SkipWithoutFullName(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `[{
                    "full_name": "user1/repo1",
                    "git_url": "git:git@github.com:user1/repo1.git"
                },{
                    "git_url": "git:git@github.com:user2/repo2.git"
                }]`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repos, err := githubRepos.All()
	require.NoError(t, err)

	if assert.Len(t, repos, 1, "Should exclude one repository without full_name") {
		assert.Equal(t, repos[0].FullName, "user1/repo1")
	}
}

func TestGithubRepositoriesAll_HandlePagination(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) {
		if page := r.FormValue("page"); page == "" || page == "1" {
			perPage := r.FormValue("per_page")
			if perPage == "" {
				perPage = "30"
			}

			w.Header().Set("Link", fmt.Sprintf(`<https://api.github.com/user/repos?page=2&per_page=%s>; rel="next"`, perPage))
		}

		fmt.Fprint(w, `[{"full_name": "user1/repo1","git_url": "git:git@github.com:user1/repo1.git"}]`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repos, err := githubRepos.All()
	require.NoError(t, err)

	assert.Len(t, repos, 2)
}

func setup() (ctx context.Context, mux *http.ServeMux, teardownFn func()) {
	mux = http.NewServeMux()
	server := httptest.NewServer(mux)

	client := github.NewClient(nil)
	url, _ := url.Parse(server.URL)
	client.BaseURL = url

	ctx = context.Background()
	ctx = context.WithValue(ctx, GithubToken, "secret_token")
	ctx = context.WithValue(ctx, httpClient, client)

	return ctx, mux, server.Close
}
<commit_msg>Add tests for git.GithubRepository.Get()<commit_after>package git

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/google/go-github/github"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

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

func TestParseRepositoryName(t *testing.T) {
	owner, repo := ParseRepositoryName("test/me")
	assert.Equal(t, owner, "test")
	assert.Equal(t, repo, "me")
}

func TestNewGithubRepositories_WithToken(t *testing.T) {
	ctx := context.WithValue(context.Background(), GithubToken, "secret_token")

	r, err := NewGithubRepositories(ctx)
	require.NoError(t, err, "Expected NewGithubRepositories to succeed")
	assert.NotNil(t, r, "Expected NewGithubRepositories to return new instance")
}

func TestNewGithubRepositories_NoToken(t *testing.T) {
	_, err := NewGithubRepositories(context.Background())
	assert.Error(t, err, "Expected NewGithubRepositories to return an error")
}

func TestNewGithubRepositories_EmptyToken(t *testing.T) {
	ctx := context.WithValue(context.Background(), GithubToken, "")

	_, err := NewGithubRepositories(ctx)
	assert.Error(t, err, "Expected NewGithubRepositories to return an error")
}

func TestGithubRepositoriesAll_SingleRepository_DefaultFields(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `[{
                    "full_name": "user1/repo1",
                    "git_url": "git@github.com:user1/repo1.git"
                }]`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repos, err := githubRepos.All()
	require.NoError(t, err)

	if assert.Len(t, repos, 1) {
		repo := repos[0]
		assert.Equal(t, repo.FullName, "user1/repo1")
		assert.Equal(t, repo.Master, "master")
		assert.Empty(t, repo.Description)
		assert.Empty(t, repo.GitURL)
		assert.Empty(t, repo.HTMLURL)
	}
}

func TestGithubRepositoriesAll_SingleRepository_AllFields(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `[{
                    "full_name": "user1/repo1",
                    "description": "Repo1",
                    "default_branch": "production",
                    "git_url": "git@github.com:user1/repo1.git",
                    "html_url": "https://github.com/user1/repo1"
                }]`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repos, err := githubRepos.All()
	require.NoError(t, err)

	if assert.Len(t, repos, 1) {
		repo := repos[0]
		assert.Equal(t, repo.FullName, "user1/repo1")
		assert.Equal(t, repo.Description, "Repo1")
		assert.Equal(t, repo.Master, "production")
		assert.Empty(t, repo.GitURL)
	}
}

func TestGithubRepositoriesAll_MultipleRepositories(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `[{
                    "full_name": "user1/repo1",
                    "git_url": "https://github.com/user1/repo1"
                },{
                    "full_name": "user2/repo2",
                    "git_url": "https://github.com/user2/repo2"
                }]`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repos, err := githubRepos.All()
	require.NoError(t, err)

	assert.Len(t, repos, 2)
}

func TestGithubRepositoriesAll_SkipWithoutGitURL(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `[{
                    "full_name": "user1/repo1"
                },{
                    "full_name": "user2/repo2",
                    "git_url": "git:git@github.com:user2/repo2.git"
                }]`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repos, err := githubRepos.All()
	require.NoError(t, err)

	if assert.Len(t, repos, 1, "Should exclude one repository without git_url") {
		assert.Equal(t, repos[0].FullName, "user2/repo2")
	}
}

func TestGithubRepositoriesAll_SkipWithoutFullName(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `[{
                    "full_name": "user1/repo1",
                    "git_url": "git:git@github.com:user1/repo1.git"
                },{
                    "git_url": "git:git@github.com:user2/repo2.git"
                }]`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repos, err := githubRepos.All()
	require.NoError(t, err)

	if assert.Len(t, repos, 1, "Should exclude one repository without full_name") {
		assert.Equal(t, repos[0].FullName, "user1/repo1")
	}
}

func TestGithubRepositoriesAll_HandlePagination(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) {
		if page := r.FormValue("page"); page == "" || page == "1" {
			perPage := r.FormValue("per_page")
			if perPage == "" {
				perPage = "30"
			}

			w.Header().Set("Link", fmt.Sprintf(`<https://api.github.com/user/repos?page=2&per_page=%s>; rel="next"`, perPage))
		}

		fmt.Fprint(w, `[{"full_name": "user1/repo1","git_url": "git:git@github.com:user1/repo1.git"}]`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repos, err := githubRepos.All()
	require.NoError(t, err)

	assert.Len(t, repos, 2)
}

func TestGithubRepositoriesGet_RepositoryExists(t *testing.T) {
	ctx, mux, teardown := setup()
	defer teardown()

	// Get repo
	mux.HandleFunc("/repos/user1/repo1", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `{
		    "full_name": "user1/repo1",
		    "description":
		    "Test repo",
		    "default_branch":"production",
		    "git_url":"git:git@github.com:user1/repo1.git",
		    "html_url": "https://github.com/user1/repo1"
		}`)
	})
	// Get branch
	mux.HandleFunc("/repos/user1/repo1/branches/production", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `{"name":"production","commit":{"sha":"abc123"}}`)
	})
	// Get git commit
	mux.HandleFunc("/repos/user1/repo1/git/commits/abc123", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `{
		    "author":{"name":"Jon Doe","date":"2011-04-14T16:00:49Z"},
		    "committer":{"name":"Doppel Ganger","date":"2016-04-14T16:00:00Z"},
		    "message":"Commit message",
		    "sha":"abc123"
		}`)
	})

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	repo, err := githubRepos.Get("user1/repo1")
	require.NoError(t, err)

	assert.Equal(t, repo.FullName, "user1/repo1")
	assert.Equal(t, repo.GitURL, "git:git@github.com:user1/repo1.git")
}

func TestGithubRepositoriesGet_NotFound(t *testing.T) {
	ctx, _, teardown := setup()
	defer teardown()

	githubRepos, err := NewGithubRepositories(ctx)
	require.NoError(t, err)

	_, err = githubRepos.Get("user1/repo1")
	assert.Equal(t, err, ErrorNotFound)
}

func setup() (ctx context.Context, mux *http.ServeMux, teardownFn func()) {
	mux = http.NewServeMux()
	server := httptest.NewServer(mux)

	client := github.NewClient(nil)
	url, _ := url.Parse(server.URL)
	client.BaseURL = url

	ctx = context.Background()
	ctx = context.WithValue(ctx, GithubToken, "secret_token")
	ctx = context.WithValue(ctx, httpClient, client)

	return ctx, mux, server.Close
}
<|endoftext|>


<commit_before>package main

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

	"context"

	"gopkg.in/yaml.v2"

	"github.com/lxc/lxd/lxd/backup"
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/state"
	storagePools "github.com/lxc/lxd/lxd/storage"
	storageDrivers "github.com/lxc/lxd/lxd/storage/drivers"
	"github.com/lxc/lxd/lxd/task"
	"github.com/lxc/lxd/shared"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/pkg/errors"
)

// Create a new backup.
func backupCreate(s *state.State, args db.InstanceBackupArgs, sourceInst instance.Instance) error {
	// Create the database entry.
	err := s.Cluster.ContainerBackupCreate(args)
	if err != nil {
		if err == db.ErrAlreadyDefined {
			return fmt.Errorf("backup '%s' already exists", args.Name)
		}

		return errors.Wrap(err, "Insert backup info into database")
	}

	revert := true
	defer func() {
		if !revert {
			return
		}
		s.Cluster.ContainerBackupRemove(args.Name)
	}()

	// Get the backup struct.
	b, err := backup.LoadByName(s, sourceInst.Project(), args.Name)
	if err != nil {
		return errors.Wrap(err, "Load backup object")
	}

	b.SetCompressionAlgorithm(args.CompressionAlgorithm)

	// Create a temporary path for the backup.
	tmpPath, err := ioutil.TempDir(shared.VarPath("backups"), "lxd_backup_")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tmpPath)

	pool, err := storagePools.GetPoolByInstance(s, sourceInst)
	if err != storageDrivers.ErrUnknownDriver && err != storageDrivers.ErrNotImplemented {
		if err != nil {
			return errors.Wrap(err, "Load instance storage pool")
		}

		err = pool.BackupInstance(sourceInst, tmpPath, b.OptimizedStorage(), !b.InstanceOnly(), nil)
		if err != nil {
			return errors.Wrap(err, "Backup create")
		}
	} else if sourceInst.Type() == instancetype.Container {
		ourStart, err := sourceInst.StorageStart()
		if err != nil {
			return err
		}
		if ourStart {
			defer sourceInst.StorageStop()
		}

		ct := sourceInst.(*containerLXC)
		err = ct.Storage().ContainerBackupCreate(tmpPath, *b, sourceInst)
		if err != nil {
			return errors.Wrap(err, "Backup create")
		}
	} else {
		return fmt.Errorf("Instance type not supported")
	}

	// Pack the backup.
	err = backupCreateTarball(s, tmpPath, *b, sourceInst)
	if err != nil {
		return err
	}

	revert = false
	return nil
}

// fixBackupStoragePool changes the pool information in the backup.yaml. This
// is done only if the provided pool doesn't exist. In this case, the pool of
// the default profile will be used.
func backupFixStoragePool(c *db.Cluster, b backup.Info, useDefaultPool bool) error {
	var poolName string

	if useDefaultPool {
		// Get the default profile
		_, profile, err := c.ProfileGet("default", "default")
		if err != nil {
			return err
		}

		_, v, err := shared.GetRootDiskDevice(profile.Devices)
		if err != nil {
			return err
		}

		poolName = v["pool"]
	} else {
		poolName = b.Pool
	}

	// Get the default's profile pool
	_, pool, err := c.StoragePoolGet(poolName)
	if err != nil {
		return err
	}

	f := func(path string) error {
		// Read in the backup.yaml file.
		backup, err := slurpBackupFile(path)
		if err != nil {
			return err
		}

		rootDiskDeviceFound := false

		// Change the pool in the backup.yaml
		backup.Pool = pool
		if backup.Container.Devices != nil {
			devName, _, err := shared.GetRootDiskDevice(backup.Container.Devices)
			if err == nil {
				backup.Container.Devices[devName]["pool"] = poolName
				rootDiskDeviceFound = true
			}
		}

		if backup.Container.ExpandedDevices != nil {
			devName, _, err := shared.GetRootDiskDevice(backup.Container.ExpandedDevices)
			if err == nil {
				backup.Container.ExpandedDevices[devName]["pool"] = poolName
				rootDiskDeviceFound = true
			}
		}

		if !rootDiskDeviceFound {
			return fmt.Errorf("No root device could be found")
		}

		file, err := os.Create(path)
		if err != nil {
			return err
		}
		defer file.Close()

		data, err := yaml.Marshal(&backup)
		if err != nil {
			return err
		}

		_, err = file.Write(data)
		if err != nil {
			return err
		}

		return nil
	}

	err = f(shared.VarPath("storage-pools", pool.Name, "containers", project.Prefix(b.Project, b.Name), "backup.yaml"))
	if err != nil {
		return err
	}

	for _, snap := range b.Snapshots {
		err = f(shared.VarPath("storage-pools", pool.Name, "containers-snapshots", project.Prefix(b.Project, b.Name), snap,
			"backup.yaml"))
		if err != nil {
			return err
		}
	}
	return nil
}

func backupCreateTarball(s *state.State, path string, b backup.Backup, c instance.Instance) error {
	// Create the index
	pool, err := c.StoragePool()
	if err != nil {
		return err
	}

	if c.Type() != instancetype.Container {
		return fmt.Errorf("Instance type must be container")
	}

	ct := c.(*containerLXC)

	indexFile := backup.Info{
		Name:       c.Name(),
		Backend:    ct.Storage().GetStorageTypeName(),
		Privileged: c.IsPrivileged(),
		Pool:       pool,
		Snapshots:  []string{},
	}

	if !b.InstanceOnly() {
		snaps, err := c.Snapshots()
		if err != nil {
			return err
		}

		for _, snap := range snaps {
			_, snapName, _ := shared.InstanceGetParentAndSnapshotName(snap.Name())
			indexFile.Snapshots = append(indexFile.Snapshots, snapName)
		}
	}

	data, err := yaml.Marshal(&indexFile)
	if err != nil {
		return err
	}

	file, err := os.Create(filepath.Join(path, "index.yaml"))
	if err != nil {
		return err
	}

	_, err = file.Write(data)
	file.Close()
	if err != nil {
		return err
	}

	// Create the target path if needed
	backupsPath := shared.VarPath("backups", project.Prefix(c.Project(), c.Name()))
	if !shared.PathExists(backupsPath) {
		err := os.MkdirAll(backupsPath, 0700)
		if err != nil {
			return err
		}
	}

	// Create the tarball
	backupPath := shared.VarPath("backups", project.Prefix(c.Project(), b.Name()))
	success := false
	defer func() {
		if success {
			return
		}

		os.RemoveAll(backupPath)
	}()

	args := []string{"-cf", backupPath, "--numeric-owner", "--xattrs", "-C", path, "--transform", "s,^./,backup/,", "."}
	_, err = shared.RunCommand("tar", args...)
	if err != nil {
		return err
	}

	err = os.RemoveAll(path)
	if err != nil {
		return err
	}

	var compress string

	if b.CompressionAlgorithm() != "" {
		compress = b.CompressionAlgorithm()
	} else {
		compress, err = cluster.ConfigGetString(s.Cluster, "backups.compression_algorithm")
		if err != nil {
			return err
		}
	}

	if compress != "none" {
		infile, err := os.Open(backupPath)
		if err != nil {
			return err
		}
		defer infile.Close()

		compressed, err := os.Create(backupPath + ".compressed")
		if err != nil {
			return err
		}
		compressedName := compressed.Name()

		defer compressed.Close()
		defer os.Remove(compressedName)

		err = compressFile(compress, infile, compressed)
		if err != nil {
			return err
		}

		err = os.Remove(backupPath)
		if err != nil {
			return err
		}

		err = os.Rename(compressedName, backupPath)
		if err != nil {
			return err
		}
	}

	// Set permissions
	err = os.Chmod(backupPath, 0600)
	if err != nil {
		return err
	}

	success = true
	return nil
}

func pruneExpiredContainerBackupsTask(d *Daemon) (task.Func, task.Schedule) {
	f := func(ctx context.Context) {
		opRun := func(op *operations.Operation) error {
			return pruneExpiredContainerBackups(ctx, d)
		}

		op, err := operations.OperationCreate(d.State(), "", operations.OperationClassTask, db.OperationBackupsExpire, nil, nil, opRun, nil, nil)
		if err != nil {
			logger.Error("Failed to start expired backups operation", log.Ctx{"err": err})
			return
		}

		logger.Info("Pruning expired container backups")
		_, err = op.Run()
		if err != nil {
			logger.Error("Failed to expire backups", log.Ctx{"err": err})
		}
		logger.Info("Done pruning expired container backups")
	}

	f(context.Background())

	first := true
	schedule := func() (time.Duration, error) {
		interval := time.Hour

		if first {
			first = false
			return interval, task.ErrSkip
		}

		return interval, nil
	}

	return f, schedule
}

func pruneExpiredContainerBackups(ctx context.Context, d *Daemon) error {
	// Get the list of expired backups.
	backups, err := d.cluster.ContainerBackupsGetExpired()
	if err != nil {
		return errors.Wrap(err, "Unable to retrieve the list of expired container backups")
	}

	for _, b := range backups {
		inst, err := instanceLoadById(d.State(), b.InstanceID)
		if err != nil {
			return errors.Wrapf(err, "Error deleting container backup %s", b.Name)
		}

		err = backup.DoBackupDelete(d.State(), inst.Project(), b.Name, inst.Name())
		if err != nil {
			return errors.Wrapf(err, "Error deleting container backup %s", b.Name)
		}
	}

	return nil
}
<commit_msg>lxd/backup: Removes backupFixStoragePool<commit_after>package main

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

	"context"

	"gopkg.in/yaml.v2"

	"github.com/lxc/lxd/lxd/backup"
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/state"
	storagePools "github.com/lxc/lxd/lxd/storage"
	storageDrivers "github.com/lxc/lxd/lxd/storage/drivers"
	"github.com/lxc/lxd/lxd/task"
	"github.com/lxc/lxd/shared"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/pkg/errors"
)

// Create a new backup.
func backupCreate(s *state.State, args db.InstanceBackupArgs, sourceInst instance.Instance) error {
	// Create the database entry.
	err := s.Cluster.ContainerBackupCreate(args)
	if err != nil {
		if err == db.ErrAlreadyDefined {
			return fmt.Errorf("backup '%s' already exists", args.Name)
		}

		return errors.Wrap(err, "Insert backup info into database")
	}

	revert := true
	defer func() {
		if !revert {
			return
		}
		s.Cluster.ContainerBackupRemove(args.Name)
	}()

	// Get the backup struct.
	b, err := backup.LoadByName(s, sourceInst.Project(), args.Name)
	if err != nil {
		return errors.Wrap(err, "Load backup object")
	}

	b.SetCompressionAlgorithm(args.CompressionAlgorithm)

	// Create a temporary path for the backup.
	tmpPath, err := ioutil.TempDir(shared.VarPath("backups"), "lxd_backup_")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tmpPath)

	pool, err := storagePools.GetPoolByInstance(s, sourceInst)
	if err != storageDrivers.ErrUnknownDriver && err != storageDrivers.ErrNotImplemented {
		if err != nil {
			return errors.Wrap(err, "Load instance storage pool")
		}

		err = pool.BackupInstance(sourceInst, tmpPath, b.OptimizedStorage(), !b.InstanceOnly(), nil)
		if err != nil {
			return errors.Wrap(err, "Backup create")
		}
	} else if sourceInst.Type() == instancetype.Container {
		ourStart, err := sourceInst.StorageStart()
		if err != nil {
			return err
		}
		if ourStart {
			defer sourceInst.StorageStop()
		}

		ct := sourceInst.(*containerLXC)
		err = ct.Storage().ContainerBackupCreate(tmpPath, *b, sourceInst)
		if err != nil {
			return errors.Wrap(err, "Backup create")
		}
	} else {
		return fmt.Errorf("Instance type not supported")
	}

	// Pack the backup.
	err = backupCreateTarball(s, tmpPath, *b, sourceInst)
	if err != nil {
		return err
	}

	revert = false
	return nil
}

func backupCreateTarball(s *state.State, path string, b backup.Backup, c instance.Instance) error {
	// Create the index
	pool, err := c.StoragePool()
	if err != nil {
		return err
	}

	if c.Type() != instancetype.Container {
		return fmt.Errorf("Instance type must be container")
	}

	ct := c.(*containerLXC)

	indexFile := backup.Info{
		Name:       c.Name(),
		Backend:    ct.Storage().GetStorageTypeName(),
		Privileged: c.IsPrivileged(),
		Pool:       pool,
		Snapshots:  []string{},
	}

	if !b.InstanceOnly() {
		snaps, err := c.Snapshots()
		if err != nil {
			return err
		}

		for _, snap := range snaps {
			_, snapName, _ := shared.InstanceGetParentAndSnapshotName(snap.Name())
			indexFile.Snapshots = append(indexFile.Snapshots, snapName)
		}
	}

	data, err := yaml.Marshal(&indexFile)
	if err != nil {
		return err
	}

	file, err := os.Create(filepath.Join(path, "index.yaml"))
	if err != nil {
		return err
	}

	_, err = file.Write(data)
	file.Close()
	if err != nil {
		return err
	}

	// Create the target path if needed
	backupsPath := shared.VarPath("backups", project.Prefix(c.Project(), c.Name()))
	if !shared.PathExists(backupsPath) {
		err := os.MkdirAll(backupsPath, 0700)
		if err != nil {
			return err
		}
	}

	// Create the tarball
	backupPath := shared.VarPath("backups", project.Prefix(c.Project(), b.Name()))
	success := false
	defer func() {
		if success {
			return
		}

		os.RemoveAll(backupPath)
	}()

	args := []string{"-cf", backupPath, "--numeric-owner", "--xattrs", "-C", path, "--transform", "s,^./,backup/,", "."}
	_, err = shared.RunCommand("tar", args...)
	if err != nil {
		return err
	}

	err = os.RemoveAll(path)
	if err != nil {
		return err
	}

	var compress string

	if b.CompressionAlgorithm() != "" {
		compress = b.CompressionAlgorithm()
	} else {
		compress, err = cluster.ConfigGetString(s.Cluster, "backups.compression_algorithm")
		if err != nil {
			return err
		}
	}

	if compress != "none" {
		infile, err := os.Open(backupPath)
		if err != nil {
			return err
		}
		defer infile.Close()

		compressed, err := os.Create(backupPath + ".compressed")
		if err != nil {
			return err
		}
		compressedName := compressed.Name()

		defer compressed.Close()
		defer os.Remove(compressedName)

		err = compressFile(compress, infile, compressed)
		if err != nil {
			return err
		}

		err = os.Remove(backupPath)
		if err != nil {
			return err
		}

		err = os.Rename(compressedName, backupPath)
		if err != nil {
			return err
		}
	}

	// Set permissions
	err = os.Chmod(backupPath, 0600)
	if err != nil {
		return err
	}

	success = true
	return nil
}

func pruneExpiredContainerBackupsTask(d *Daemon) (task.Func, task.Schedule) {
	f := func(ctx context.Context) {
		opRun := func(op *operations.Operation) error {
			return pruneExpiredContainerBackups(ctx, d)
		}

		op, err := operations.OperationCreate(d.State(), "", operations.OperationClassTask, db.OperationBackupsExpire, nil, nil, opRun, nil, nil)
		if err != nil {
			logger.Error("Failed to start expired backups operation", log.Ctx{"err": err})
			return
		}

		logger.Info("Pruning expired container backups")
		_, err = op.Run()
		if err != nil {
			logger.Error("Failed to expire backups", log.Ctx{"err": err})
		}
		logger.Info("Done pruning expired container backups")
	}

	f(context.Background())

	first := true
	schedule := func() (time.Duration, error) {
		interval := time.Hour

		if first {
			first = false
			return interval, task.ErrSkip
		}

		return interval, nil
	}

	return f, schedule
}

func pruneExpiredContainerBackups(ctx context.Context, d *Daemon) error {
	// Get the list of expired backups.
	backups, err := d.cluster.ContainerBackupsGetExpired()
	if err != nil {
		return errors.Wrap(err, "Unable to retrieve the list of expired container backups")
	}

	for _, b := range backups {
		inst, err := instanceLoadById(d.State(), b.InstanceID)
		if err != nil {
			return errors.Wrapf(err, "Error deleting container backup %s", b.Name)
		}

		err = backup.DoBackupDelete(d.State(), inst.Project(), b.Name, inst.Name())
		if err != nil {
			return errors.Wrapf(err, "Error deleting container backup %s", b.Name)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"image/color"
	"math"

	gl "github.com/go-gl/gl"
	"github.com/leafo/weeklyloops/loops"
)

func main() {
	loop := loops.NewLoopWindow()
	loop.Title = "loop1"
	loop.Speed = 0.3

	v := loops.Vec2{0, 0.5}
	v.Print()

	x1 := v[0]
	y1 := v[1]

	v = v.RotateAngle(360.0 / 3)
	v.Print()

	x2 := v[0]
	y2 := v[1]

	v = v.RotateAngle(360.0 / 3)
	v.Print()

	x3 := v[0]
	y3 := v[1]

	triangle := []float32{x1, y1, x2, y2, x3, y3}

	loop.Draw = func(t float64, g *loops.Graphics) {
		g.SetColor(color.RGBA{20, 20, 20, 255})
		g.SetMat(loops.NewIdentityMat4())
		g.DrawRect(-1, -1, 2, 2)

		t = loops.Smoothstep(t)
		s := loops.NewScaleMatrix(0.1, 0.1, 1)

		rad := float32(t * math.Pi * 2)

		i := 1
		row := 0
		col := 0

		g.SetColor(color.RGBA{200, 200, 200, 255})
		for y := -0.8; y <= 0.8; y += 0.1 {
			col = 0
			for x := -0.8; x <= 0.8; x += 0.1 {
				rad = -rad
				realRad := rad * float32(row/3+1)
				realScale := 1.0
				if i%2 == 0 {
					realScale = realScale / 1.5
				}

				g.SetMat(s.Scale(float32(realScale), float32(realScale), 1).Rotate(realRad).Translate(float32(x), float32(y), 0))
				g.Draw(gl.TRIANGLE_STRIP, triangle)

				i += 1
				col += 1
			}

			row += 1
		}

	}

	loop.Run()
}
<commit_msg>clean up<commit_after>package main

import (
	"image/color"
	"math"

	gl "github.com/go-gl/gl"
	"github.com/leafo/weeklyloops/loops"
)

func main() {
	loop := loops.NewLoopWindow()
	loop.Title = "loop1"
	loop.Speed = 0.3

	a := loops.Vec2{0, 0.5}
	b := a.RotateAngle(360.0 / 3)
	c := b.RotateAngle(360.0 / 3)

	triangle := []float32{
		a[0], a[1],
		b[0], b[1],
		c[0], c[1],
	}

	loop.Draw = func(t float64, g *loops.Graphics) {
		g.SetColor(color.RGBA{20, 20, 20, 255})
		g.SetMat(loops.NewIdentityMat4())
		g.DrawRect(-1, -1, 2, 2)

		t = loops.Smoothstep(t)
		s := loops.NewScaleMatrix(0.1, 0.1, 1)

		rad := float32(t * math.Pi * 2)

		i := 1
		row := 0
		col := 0

		g.SetColor(color.RGBA{200, 200, 200, 255})
		for y := -0.8; y <= 0.8; y += 0.1 {
			col = 0
			for x := -0.8; x <= 0.8; x += 0.1 {

				rad = -rad
				realRad := rad * float32(row/3+1)
				realScale := 1.0
				if i%2 == 0 {
					realScale = realScale / 1.5
				}

				m := s.Scale(float32(realScale), float32(realScale), 1).
					Rotate(realRad).
					Translate(float32(x), float32(y), 0)

				g.SetMat(m)
				g.Draw(gl.TRIANGLE_STRIP, triangle)

				i += 1
				col += 1
			}

			row += 1
		}

	}

	loop.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"

	"github.com/gorilla/mux"
	"github.com/lxc/lxd/shared"
	"gopkg.in/tomb.v2"
)

// A Daemon can respond to requests from a shared client.
type Daemon struct {
	tomb        tomb.Tomb
	unixl       net.Listener
	tcpl        net.Listener
	id_map      *Idmap
	lxcpath     string
	certf       string
	keyf        string
	mux         *mux.Router
	clientCerts map[string]x509.Certificate
}

type Command struct {
	name          string
	untrustedGet  bool
	untrustedPost bool
	get           func(d *Daemon, r *http.Request) Response
	put           func(d *Daemon, r *http.Request) Response
	post          func(d *Daemon, r *http.Request) Response
	delete        func(d *Daemon, r *http.Request) Response
}

func readMyCert() (string, string, error) {
	certf := shared.VarPath("server.crt")
	keyf := shared.VarPath("server.key")
	shared.Debugf("looking for existing certificates: %s %s", certf, keyf)

	err := shared.FindOrGenCert(certf, keyf)

	return certf, keyf, err
}

func readSavedClientCAList(d *Daemon) {
	dirpath := shared.VarPath("clientcerts")
	d.clientCerts = make(map[string]x509.Certificate)
	fil, err := ioutil.ReadDir(dirpath)
	if err != nil {
		return
	}
	for i := range fil {
		n := fil[i].Name()
		fnam := fmt.Sprintf("%s/%s", dirpath, n)
		cf, err := ioutil.ReadFile(fnam)
		if err != nil {
			continue
		}

		cert_block, _ := pem.Decode(cf)

		cert, err := x509.ParseCertificate(cert_block.Bytes)
		if err != nil {
			continue
		}
		n, _ = shared.SplitExt(n)
		d.clientCerts[n] = *cert
		shared.Debugf("Loaded cert %s", fnam)
	}
}

func (d *Daemon) isTrustedClient(r *http.Request) bool {
	if r.RemoteAddr == "@" {
		// Unix socket
		return true
	}
	if r.TLS == nil {
		return false
	}
	for i := range r.TLS.PeerCertificates {
		if d.CheckTrustState(*r.TLS.PeerCertificates[i]) {
			return true
		}
	}
	return false
}

func (d *Daemon) createCmd(version string, c Command) {
	var uri string
	if c.name == "" {
		uri = fmt.Sprintf("/%s", version)
	} else {
		uri = fmt.Sprintf("/%s/%s", version, c.name)
	}

	d.mux.HandleFunc(uri, func(w http.ResponseWriter, r *http.Request) {

		if d.isTrustedClient(r) {
			shared.Debugf("handling %s %s", r.Method, r.URL.RequestURI())
		} else if r.Method == "GET" && c.untrustedGet {
			shared.Debugf("allowing untrusted GET to %s", r.URL.RequestURI())
		} else if r.Method == "POST" && c.untrustedPost {
			shared.Debugf("allowing untrusted POST to %s", r.URL.RequestURI())
		} else {
			shared.Debugf("rejecting request from untrusted client")
			Forbidden.Render(w)
			return
		}

		var resp Response
		resp = NotImplemented

		switch r.Method {
		case "GET":
			if c.get != nil {
				resp = c.get(d, r)
			}
		case "PUT":
			if c.put != nil {
				resp = c.put(d, r)
			}
		case "POST":
			if c.post != nil {
				resp = c.post(d, r)
			}
		case "DELETE":
			if c.delete != nil {
				resp = c.delete(d, r)
			}
		default:
			resp = NotFound
		}

		if err := resp.Render(w); err != nil {
			err := InternalError(err).Render(w)
			if err != nil {
				shared.Debugf("failed writing error for error, giving up.")
			}
		}
	})
}

// StartDaemon starts the shared daemon with the provided configuration.
func StartDaemon(listenAddr string) (*Daemon, error) {
	d := &Daemon{}

	d.lxcpath = shared.VarPath("lxc")
	err := os.MkdirAll(shared.VarPath("/"), 0755)
	if err != nil {
		return nil, err
	}
	err = os.MkdirAll(d.lxcpath, 0755)
	if err != nil {
		return nil, err
	}

	certf, keyf, err := readMyCert()
	if err != nil {
		return nil, err
	}
	d.certf = certf
	d.keyf = keyf

	// TODO load known client certificates
	readSavedClientCAList(d)

	d.mux = mux.NewRouter()

	d.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		SyncResponse(true, []string{"/1.0"}).Render(w)
	})

	for _, c := range api10 {
		d.createCmd("1.0", c)
	}

	d.mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		shared.Debugf("sending top level 404: %s", r.URL)
		NotFound.Render(w)
	})

	d.id_map, err = NewIdmap()
	if err != nil {
		shared.Logf("error reading idmap: %s", err.Error())
		shared.Logf("operations requiring idmap will not be available")
	} else {
		shared.Debugf("idmap is %d %d %d %d\n",
			d.id_map.Uidmin,
			d.id_map.Uidrange,
			d.id_map.Gidmin,
			d.id_map.Gidrange)
	}

	localSocket := shared.VarPath("unix.socket")
	unixAddr, err := net.ResolveUnixAddr("unix", localSocket)
	if err != nil {
		return nil, fmt.Errorf("cannot resolve unix socket address: %v", err)
	}
	unixl, err := net.ListenUnix("unix", unixAddr)
	if err != nil {
		return nil, fmt.Errorf("cannot listen on unix socket: %v", err)
	}
	d.unixl = unixl

	if err := os.Chmod(localSocket, 0775); err != nil {
		return nil, err
	}

	gid, err := shared.GroupId(*group)
	if err != nil {
		return nil, err
	}

	if err := os.Chown(localSocket, os.Getuid(), gid); err != nil {
		return nil, err
	}

	if listenAddr != "" {
		// Watch out. There's a listener active which must be closed on errors.
		mycert, err := tls.LoadX509KeyPair(d.certf, d.keyf)
		if err != nil {
			return nil, err
		}
		config := tls.Config{Certificates: []tls.Certificate{mycert},
			ClientAuth: tls.RequireAnyClientCert,
			MinVersion: tls.VersionTLS12,
			MaxVersion: tls.VersionTLS12}
		tcpl, err := tls.Listen("tcp", listenAddr, &config)
		if err != nil {
			d.unixl.Close()
			return nil, fmt.Errorf("cannot listen on unix socket: %v", err)
		}
		d.tcpl = tcpl
		d.tomb.Go(func() error { return http.Serve(d.tcpl, d.mux) })
	}

	d.tomb.Go(func() error { return http.Serve(d.unixl, d.mux) })
	return d, nil
}

func (d *Daemon) CheckTrustState(cert x509.Certificate) bool {
	for k, v := range d.clientCerts {
		if bytes.Compare(cert.Raw, v.Raw) == 0 {
			shared.Debugf("found cert for %s", k)
			return true
		} else {
			shared.Debugf("client cert != key for %s", k)
		}
	}
	return false
}

var errStop = fmt.Errorf("requested stop")

// Stop stops the shared daemon.
func (d *Daemon) Stop() error {
	d.tomb.Kill(errStop)
	d.unixl.Close()
	if d.tcpl != nil {
		d.tcpl.Close()
	}
	err := d.tomb.Wait()
	if err == errStop {
		return nil
	}
	return err
}

// None of the daemon methods should print anything to stdout or stderr. If
// there's a local issue in the daemon that the admin should know about, it
// should be logged using either Logf or Debugf.
//
// Then, all of those issues that prevent the request from being served properly
// for any reason (bad parameters or any other local error) should be notified
// back to the client by writing an error json document to w, which in turn will
// be read by the client and returned via the API as an error result. These
// errors then surface via the CLI (cmd/shared/*) in os.Stderr.
//
// Together, these ideas ensure that we have a proper daemon, and a proper client,
// which can both be used independently and also embedded into other applications.
<commit_msg>Fix race in tomb invocation<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"

	"github.com/gorilla/mux"
	"github.com/lxc/lxd/shared"
	"gopkg.in/tomb.v2"
)

// A Daemon can respond to requests from a shared client.
type Daemon struct {
	tomb        tomb.Tomb
	unixl       net.Listener
	tcpl        net.Listener
	id_map      *Idmap
	lxcpath     string
	certf       string
	keyf        string
	mux         *mux.Router
	clientCerts map[string]x509.Certificate
}

type Command struct {
	name          string
	untrustedGet  bool
	untrustedPost bool
	get           func(d *Daemon, r *http.Request) Response
	put           func(d *Daemon, r *http.Request) Response
	post          func(d *Daemon, r *http.Request) Response
	delete        func(d *Daemon, r *http.Request) Response
}

func readMyCert() (string, string, error) {
	certf := shared.VarPath("server.crt")
	keyf := shared.VarPath("server.key")
	shared.Debugf("looking for existing certificates: %s %s", certf, keyf)

	err := shared.FindOrGenCert(certf, keyf)

	return certf, keyf, err
}

func readSavedClientCAList(d *Daemon) {
	dirpath := shared.VarPath("clientcerts")
	d.clientCerts = make(map[string]x509.Certificate)
	fil, err := ioutil.ReadDir(dirpath)
	if err != nil {
		return
	}
	for i := range fil {
		n := fil[i].Name()
		fnam := fmt.Sprintf("%s/%s", dirpath, n)
		cf, err := ioutil.ReadFile(fnam)
		if err != nil {
			continue
		}

		cert_block, _ := pem.Decode(cf)

		cert, err := x509.ParseCertificate(cert_block.Bytes)
		if err != nil {
			continue
		}
		n, _ = shared.SplitExt(n)
		d.clientCerts[n] = *cert
		shared.Debugf("Loaded cert %s", fnam)
	}
}

func (d *Daemon) isTrustedClient(r *http.Request) bool {
	if r.RemoteAddr == "@" {
		// Unix socket
		return true
	}
	if r.TLS == nil {
		return false
	}
	for i := range r.TLS.PeerCertificates {
		if d.CheckTrustState(*r.TLS.PeerCertificates[i]) {
			return true
		}
	}
	return false
}

func (d *Daemon) createCmd(version string, c Command) {
	var uri string
	if c.name == "" {
		uri = fmt.Sprintf("/%s", version)
	} else {
		uri = fmt.Sprintf("/%s/%s", version, c.name)
	}

	d.mux.HandleFunc(uri, func(w http.ResponseWriter, r *http.Request) {

		if d.isTrustedClient(r) {
			shared.Debugf("handling %s %s", r.Method, r.URL.RequestURI())
		} else if r.Method == "GET" && c.untrustedGet {
			shared.Debugf("allowing untrusted GET to %s", r.URL.RequestURI())
		} else if r.Method == "POST" && c.untrustedPost {
			shared.Debugf("allowing untrusted POST to %s", r.URL.RequestURI())
		} else {
			shared.Debugf("rejecting request from untrusted client")
			Forbidden.Render(w)
			return
		}

		var resp Response
		resp = NotImplemented

		switch r.Method {
		case "GET":
			if c.get != nil {
				resp = c.get(d, r)
			}
		case "PUT":
			if c.put != nil {
				resp = c.put(d, r)
			}
		case "POST":
			if c.post != nil {
				resp = c.post(d, r)
			}
		case "DELETE":
			if c.delete != nil {
				resp = c.delete(d, r)
			}
		default:
			resp = NotFound
		}

		if err := resp.Render(w); err != nil {
			err := InternalError(err).Render(w)
			if err != nil {
				shared.Debugf("failed writing error for error, giving up.")
			}
		}
	})
}

// StartDaemon starts the shared daemon with the provided configuration.
func StartDaemon(listenAddr string) (*Daemon, error) {
	d := &Daemon{}

	d.lxcpath = shared.VarPath("lxc")
	err := os.MkdirAll(shared.VarPath("/"), 0755)
	if err != nil {
		return nil, err
	}
	err = os.MkdirAll(d.lxcpath, 0755)
	if err != nil {
		return nil, err
	}

	certf, keyf, err := readMyCert()
	if err != nil {
		return nil, err
	}
	d.certf = certf
	d.keyf = keyf

	// TODO load known client certificates
	readSavedClientCAList(d)

	d.mux = mux.NewRouter()

	d.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		SyncResponse(true, []string{"/1.0"}).Render(w)
	})

	for _, c := range api10 {
		d.createCmd("1.0", c)
	}

	d.mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		shared.Debugf("sending top level 404: %s", r.URL)
		NotFound.Render(w)
	})

	d.id_map, err = NewIdmap()
	if err != nil {
		shared.Logf("error reading idmap: %s", err.Error())
		shared.Logf("operations requiring idmap will not be available")
	} else {
		shared.Debugf("idmap is %d %d %d %d\n",
			d.id_map.Uidmin,
			d.id_map.Uidrange,
			d.id_map.Gidmin,
			d.id_map.Gidrange)
	}

	localSocket := shared.VarPath("unix.socket")
	unixAddr, err := net.ResolveUnixAddr("unix", localSocket)
	if err != nil {
		return nil, fmt.Errorf("cannot resolve unix socket address: %v", err)
	}
	unixl, err := net.ListenUnix("unix", unixAddr)
	if err != nil {
		return nil, fmt.Errorf("cannot listen on unix socket: %v", err)
	}
	d.unixl = unixl

	if err := os.Chmod(localSocket, 0775); err != nil {
		return nil, err
	}

	gid, err := shared.GroupId(*group)
	if err != nil {
		return nil, err
	}

	if err := os.Chown(localSocket, os.Getuid(), gid); err != nil {
		return nil, err
	}

	var tcpListen func() error
	if listenAddr != "" {
		// Watch out. There's a listener active which must be closed on errors.
		mycert, err := tls.LoadX509KeyPair(d.certf, d.keyf)
		if err != nil {
			return nil, err
		}
		config := tls.Config{Certificates: []tls.Certificate{mycert},
			ClientAuth: tls.RequireAnyClientCert,
			MinVersion: tls.VersionTLS12,
			MaxVersion: tls.VersionTLS12}
		tcpl, err := tls.Listen("tcp", listenAddr, &config)
		if err != nil {
			d.unixl.Close()
			return nil, fmt.Errorf("cannot listen on unix socket: %v", err)
		}
		d.tcpl = tcpl
		tcpListen = func() error { return http.Serve(d.tcpl, d.mux) }
	}

	d.tomb.Go(func() error {
		d.tomb.Go(tcpListen)
		d.tomb.Go(func() error { return http.Serve(d.unixl, d.mux) })
		return nil
	})

	return d, nil
}

func (d *Daemon) CheckTrustState(cert x509.Certificate) bool {
	for k, v := range d.clientCerts {
		if bytes.Compare(cert.Raw, v.Raw) == 0 {
			shared.Debugf("found cert for %s", k)
			return true
		} else {
			shared.Debugf("client cert != key for %s", k)
		}
	}
	return false
}

var errStop = fmt.Errorf("requested stop")

// Stop stops the shared daemon.
func (d *Daemon) Stop() error {
	d.tomb.Kill(errStop)
	d.unixl.Close()
	if d.tcpl != nil {
		d.tcpl.Close()
	}
	err := d.tomb.Wait()
	if err == errStop {
		return nil
	}
	return err
}

// None of the daemon methods should print anything to stdout or stderr. If
// there's a local issue in the daemon that the admin should know about, it
// should be logged using either Logf or Debugf.
//
// Then, all of those issues that prevent the request from being served properly
// for any reason (bad parameters or any other local error) should be notified
// back to the client by writing an error json document to w, which in turn will
// be read by the client and returned via the API as an error result. These
// errors then surface via the CLI (cmd/shared/*) in os.Stderr.
//
// Together, these ideas ensure that we have a proper daemon, and a proper client,
// which can both be used independently and also embedded into other applications.
<|endoftext|>


<commit_before>package vcard

import (
	"strings"
	"reflect"
	"testing"
)

var testCardString = `BEGIN:VCARD
VERSION:4.0
UID:urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1
FN;PID=1.1:J. Doe
N:Doe;J.;;;
EMAIL;PID=1.1:jdoe@example.com
CLIENTPIDMAP:1;urn:uuid:53e374d9-337e-4727-8803-a1e9c14e0556
END:VCARD`

var testCardGoogleString = `BEGIN:VCARD
VERSION:3.0
N:Bloggs;Joe;;;
FN:Joe Bloggs
EMAIL;TYPE=INTERNET;TYPE=HOME:me@joebloggs.com
TEL;TYPE=CELL:+44 20 1234 5678
ADR;TYPE=HOME:;;1 Trafalgar Square;London;;WC2N;United Kingdom
item1.URL:http\://joebloggs.com
item1.X-ABLabel:_$!<HomePage>!$_
X-SKYPE:joe.bloggs
item2.URL:http\://twitter.com/test
item2.X-ABLabel:Twitter
END:VCARD`

var testCardGoogle = Card{
	"VERSION": []*Field{{Value: "3.0"}},
	"N": []*Field{{Value: "Bloggs;Joe;;;"}},
	"FN": []*Field{{Value: "Joe Bloggs"}},
	"EMAIL": []*Field{{
		Value: "me@joebloggs.com",
		Params: map[string]string{"TYPE": "HOME"},
	}},
	"TEL": []*Field{{
		Value: "+44 20 1234 5678",
		Params: map[string]string{"TYPE": "CELL"},
	}},
	"ADR": []*Field{{
		Value: ";;1 Trafalgar Square;London;;WC2N;United Kingdom",
		Params: map[string]string{"TYPE": "HOME"},
	}},
	"URL": []*Field{
		{Value: "http\\://joebloggs.com", Group: "item1"},
		{Value: "http\\://twitter.com/test", Group: "item2"},
	},
	"X-SKYPE": []*Field{{Value: "joe.bloggs"}},
	"X-ABLABEL": []*Field{
		{Value: "_$!<HomePage>!$_", Group: "item1"},
		{Value: "Twitter", Group: "item2"},
	},
}

var decoderTests = []struct{
	s string
	card Card
}{
	{testCardString, testCard},
	{testCardGoogleString, testCardGoogle},
}

func TestDecoder(t *testing.T) {
	for _, test := range decoderTests {
		r := strings.NewReader(test.s)
		dec := NewDecoder(r)
		card, err := dec.Decode()
		if err != nil {
			t.Fatal("Expected no error when decoding card, got:", err)
		}
		if !reflect.DeepEqual(card, test.card) {
			t.Errorf("Invalid parsed card: expected \n%+v\n but got \n%+v", test.card, card)
		}
	}
}
<commit_msg>Adds Apple Contacts test<commit_after>package vcard

import (
	"strings"
	"reflect"
	"testing"
)

// RFC
var testCardString = `BEGIN:VCARD
VERSION:4.0
UID:urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1
FN;PID=1.1:J. Doe
N:Doe;J.;;;
EMAIL;PID=1.1:jdoe@example.com
CLIENTPIDMAP:1;urn:uuid:53e374d9-337e-4727-8803-a1e9c14e0556
END:VCARD`

// Google Contacts (15 November 2012)
var testCardGoogleString = `BEGIN:VCARD
VERSION:3.0
N:Bloggs;Joe;;;
FN:Joe Bloggs
EMAIL;TYPE=INTERNET;TYPE=HOME:me@joebloggs.com
TEL;TYPE=CELL:+44 20 1234 5678
ADR;TYPE=HOME:;;1 Trafalgar Square;London;;WC2N;United Kingdom
item1.URL:http\://joebloggs.com
item1.X-ABLabel:_$!<HomePage>!$_
X-SKYPE:joe.bloggs
item2.URL:http\://twitter.com/test
item2.X-ABLabel:Twitter
END:VCARD`

var testCardGoogle = Card{
	"VERSION": []*Field{{Value: "3.0"}},
	"N": []*Field{{Value: "Bloggs;Joe;;;"}},
	"FN": []*Field{{Value: "Joe Bloggs"}},
	"EMAIL": []*Field{{
		Value: "me@joebloggs.com",
		Params: map[string]string{"TYPE": "HOME"},
	}},
	"TEL": []*Field{{
		Value: "+44 20 1234 5678",
		Params: map[string]string{"TYPE": "CELL"},
	}},
	"ADR": []*Field{{
		Value: ";;1 Trafalgar Square;London;;WC2N;United Kingdom",
		Params: map[string]string{"TYPE": "HOME"},
	}},
	"URL": []*Field{
		{Value: "http\\://joebloggs.com", Group: "item1"},
		{Value: "http\\://twitter.com/test", Group: "item2"},
	},
	"X-SKYPE": []*Field{{Value: "joe.bloggs"}},
	"X-ABLABEL": []*Field{
		{Value: "_$!<HomePage>!$_", Group: "item1"},
		{Value: "Twitter", Group: "item2"},
	},
}

// Apple Contacts (version 7.1)
var testCardAppleString = `BEGIN:VCARD
VERSION:3.0
N:Bloggs;Joe;;;
FN:Joe Bloggs
EMAIL;type=INTERNET;type=HOME;type=pref:me@joebloggs.com
TEL;type=CELL;type=VOICE;type=pref:+44 20 1234 5678
ADR;type=HOME;type=pref:;;1 Trafalgar Square;London;;WC2N;United Kingdom
item1.URL;type=pref:http://joebloggs.com
item1.X-ABLabel:_$!<HomePage>!$_
IMPP;X-SERVICE-TYPE=Skype;type=HOME;type=pref:skype:joe.bloggs
X-SOCIALPROFILE;type=twitter:https://twitter.com/joebloggs
END:VCARD`

var testCardApple = Card{
	"VERSION": []*Field{{Value: "3.0"}},
	"N": []*Field{{Value: "Bloggs;Joe;;;"}},
	"FN": []*Field{{Value: "Joe Bloggs"}},
	"EMAIL": []*Field{{
		Value: "me@joebloggs.com",
		Params: map[string]string{"TYPE": "pref"},
	}},
	"TEL": []*Field{{
		Value: "+44 20 1234 5678",
		Params: map[string]string{"TYPE": "pref"},
	}},
	"ADR": []*Field{{
		Value: ";;1 Trafalgar Square;London;;WC2N;United Kingdom",
		Params: map[string]string{"TYPE": "pref"},
	}},
	"URL": []*Field{
		{
			Value: "http://joebloggs.com",
			Params: map[string]string{"TYPE": "pref"},
			Group: "item1",
		},
	},
	"X-ABLABEL": []*Field{
		{Value: "_$!<HomePage>!$_", Group: "item1"},
	},
	"IMPP": []*Field{{
		Value: "skype:joe.bloggs",
		Params: map[string]string{"X-SERVICE-TYPE": "Skype", "TYPE": "pref"},
	}},
	"X-SOCIALPROFILE": []*Field{{
		Value: "https://twitter.com/joebloggs",
		Params: map[string]string{"TYPE": "twitter"},
	}},
}

var decoderTests = []struct{
	s string
	card Card
}{
	{testCardString, testCard},
	{testCardGoogleString, testCardGoogle},
	{testCardAppleString, testCardApple},
}

func TestDecoder(t *testing.T) {
	for _, test := range decoderTests {
		r := strings.NewReader(test.s)
		dec := NewDecoder(r)
		card, err := dec.Decode()
		if err != nil {
			t.Fatal("Expected no error when decoding card, got:", err)
		}
		if !reflect.DeepEqual(card, test.card) {
			t.Errorf("Invalid parsed card: expected \n%+v\n but got \n%+v", test.card, card)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"net/http"
	"strings"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/events"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/rbac"
	"github.com/lxc/lxd/lxd/response"
	"github.com/lxc/lxd/shared"
)

var eventTypes = []string{"logging", "operation", "lifecycle"}
var privilegedEventTypes = []string{"logging"}

var eventsCmd = APIEndpoint{
	Path: "events",

	Get: APIEndpointAction{Handler: eventsGet, AccessHandler: allowAuthenticated},
}

type eventsServe struct {
	req *http.Request
	d   *Daemon
}

func (r *eventsServe) Render(w http.ResponseWriter) error {
	return eventsSocket(r.d, r.req, w)
}

func (r *eventsServe) String() string {
	return "event handler"
}

func eventsSocket(d *Daemon, r *http.Request, w http.ResponseWriter) error {
	allProjects := shared.IsTrue(queryParam(r, "all-projects"))
	projectQueryParam := queryParam(r, "project")
	if allProjects && projectQueryParam != "" {
		response.BadRequest(fmt.Errorf("Cannot specify a project when requesting events for all projects"))
		return nil
	}

	var projectName string
	if !allProjects {
		if projectQueryParam == "" {
			projectName = project.Default
		} else {
			projectName = projectQueryParam

			_, err := d.cluster.GetProject(projectName)
			if err != nil {
				if errors.Is(err, db.ErrNoSuchObject) {
					response.BadRequest(fmt.Errorf("Project %q not found", projectName)).Render(w)
				}

				return err
			}
		}
	}

	types := strings.Split(r.FormValue("type"), ",")
	if len(types) == 1 && types[0] == "" {
		types = []string{}
		for _, entry := range eventTypes {
			if !rbac.UserIsAdmin(r) && shared.StringInSlice(entry, privilegedEventTypes) {
				continue
			}

			types = append(types, entry)
		}
	}

	// Validate event types.
	for _, entry := range types {
		if !shared.StringInSlice(entry, eventTypes) {
			response.BadRequest(fmt.Errorf("'%s' isn't a supported event type", entry)).Render(w)
			return nil
		}
	}

	if shared.StringInSlice("logging", types) && !rbac.UserIsAdmin(r) {
		response.Forbidden(nil).Render(w)
		return nil
	}

	// Upgrade the connection to websocket
	c, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return err
	}
	defer c.Close() // This ensures the go routine below is ended when this function ends.

	// Get the current local serverName and store it for the events
	// We do that now to avoid issues with changes to the name and to limit
	// the number of DB access to just one per connection
	var serverName string
	err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
		serverName, err = tx.GetLocalNodeName()
		return err
	})
	if err != nil {
		return err
	}

	var excludeSources []events.EventSource

	if isClusterNotification(r) {
		// If client is another cluster member, it will already be pulling events from other cluster
		// members so no need to also deliver forwarded events that this member receives.
		excludeSources = append(excludeSources, events.EventSourcePull)
	}

	listener, err := d.events.AddListener(projectName, allProjects, c, types, serverName, excludeSources, nil)
	if err != nil {
		return err
	}

	listener.Wait(r.Context())

	return nil
}

// swagger:operation GET /1.0/events server events_get
//
// Get the event stream
//
// Connects to the event API using websocket.
//
// ---
// produces:
//   - application/json
// parameters:
//   - in: query
//     name: project
//     description: Project name
//     type: string
//     example: default
//   - in: query
//     name: type
//     description: Event type(s), comma separated (valid types are logging, operation or lifecycle)
//     type: string
//     example: logging,lifecycle
// responses:
//   "200":
//     description: Websocket message (JSON)
//     schema:
//       $ref: "#/definitions/Event"
//   "403":
//     $ref: "#/responses/Forbidden"
//   "500":
//     $ref: "#/responses/InternalServerError"
func eventsGet(d *Daemon, r *http.Request) response.Response {
	return &eventsServe{req: r, d: d}
}
<commit_msg>lxd/events: Updates d.events.AddListener to remove listener location<commit_after>package main

import (
	"errors"
	"fmt"
	"net/http"
	"strings"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/events"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/rbac"
	"github.com/lxc/lxd/lxd/response"
	"github.com/lxc/lxd/shared"
)

var eventTypes = []string{"logging", "operation", "lifecycle"}
var privilegedEventTypes = []string{"logging"}

var eventsCmd = APIEndpoint{
	Path: "events",

	Get: APIEndpointAction{Handler: eventsGet, AccessHandler: allowAuthenticated},
}

type eventsServe struct {
	req *http.Request
	d   *Daemon
}

func (r *eventsServe) Render(w http.ResponseWriter) error {
	return eventsSocket(r.d, r.req, w)
}

func (r *eventsServe) String() string {
	return "event handler"
}

func eventsSocket(d *Daemon, r *http.Request, w http.ResponseWriter) error {
	allProjects := shared.IsTrue(queryParam(r, "all-projects"))
	projectQueryParam := queryParam(r, "project")
	if allProjects && projectQueryParam != "" {
		response.BadRequest(fmt.Errorf("Cannot specify a project when requesting events for all projects"))
		return nil
	}

	var projectName string
	if !allProjects {
		if projectQueryParam == "" {
			projectName = project.Default
		} else {
			projectName = projectQueryParam

			_, err := d.cluster.GetProject(projectName)
			if err != nil {
				if errors.Is(err, db.ErrNoSuchObject) {
					response.BadRequest(fmt.Errorf("Project %q not found", projectName)).Render(w)
				}

				return err
			}
		}
	}

	types := strings.Split(r.FormValue("type"), ",")
	if len(types) == 1 && types[0] == "" {
		types = []string{}
		for _, entry := range eventTypes {
			if !rbac.UserIsAdmin(r) && shared.StringInSlice(entry, privilegedEventTypes) {
				continue
			}

			types = append(types, entry)
		}
	}

	// Validate event types.
	for _, entry := range types {
		if !shared.StringInSlice(entry, eventTypes) {
			response.BadRequest(fmt.Errorf("'%s' isn't a supported event type", entry)).Render(w)
			return nil
		}
	}

	if shared.StringInSlice("logging", types) && !rbac.UserIsAdmin(r) {
		response.Forbidden(nil).Render(w)
		return nil
	}

	// Upgrade the connection to websocket
	c, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return err
	}
	defer c.Close() // This ensures the go routine below is ended when this function ends.

	// Get the current local serverName and store it for the events.
	// We do that now to avoid issues with changes to the name and to limit
	// the number of DB access to just one per connection.
	err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
		serverName, err := tx.GetLocalNodeName()
		if err != nil {
			return err
		}

		d.events.SetLocalLocation(serverName)
		return nil
	})
	if err != nil {
		return err
	}

	var excludeSources []events.EventSource

	if isClusterNotification(r) {
		// If client is another cluster member, it will already be pulling events from other cluster
		// members so no need to also deliver forwarded events that this member receives.
		excludeSources = append(excludeSources, events.EventSourcePull)
	}

	listener, err := d.events.AddListener(projectName, allProjects, c, types, excludeSources, nil)
	if err != nil {
		return err
	}

	listener.Wait(r.Context())

	return nil
}

// swagger:operation GET /1.0/events server events_get
//
// Get the event stream
//
// Connects to the event API using websocket.
//
// ---
// produces:
//   - application/json
// parameters:
//   - in: query
//     name: project
//     description: Project name
//     type: string
//     example: default
//   - in: query
//     name: type
//     description: Event type(s), comma separated (valid types are logging, operation or lifecycle)
//     type: string
//     example: logging,lifecycle
// responses:
//   "200":
//     description: Websocket message (JSON)
//     schema:
//       $ref: "#/definitions/Event"
//   "403":
//     $ref: "#/responses/Forbidden"
//   "500":
//     $ref: "#/responses/InternalServerError"
func eventsGet(d *Daemon, r *http.Request) response.Response {
	return &eventsServe{req: r, d: d}
}
<|endoftext|>


<commit_before>package reddit

import (
	"net/http"
	"net/url"
	"strings"
)

var userAgent = "tsredditbot/0.1 by gsora (aka peppelakappa)"

func createTokenRequest(reqType string, url string, body url.Values, username string, password string) (*http.Request, error) {
	req, err := http.NewRequest(reqType, url, strings.NewReader(body.Encode()))
	if err != nil {
		return &http.Request{}, err
	}

	req.Header.Add("User-Agent", userAgent)
	req.SetBasicAuth(username, password)

	return req, nil
}

func createAuthenticatedRequest(reqType string, url string, token TokenResponse) (*http.Request, error) {
	req, err := http.NewRequest(reqType, url, nil)
	if err != nil {
		return &http.Request{}, err
	}
	req.Header.Add("User-Agent", userAgent)
	req.Header.Add("Authorization", token.GetAuthString())
	return req, nil
}
<commit_msg>accept a data parameter, if any<commit_after>package reddit

import (
	"net/http"
	"net/url"
	"strings"
)

var userAgent = "tsredditbot/0.1 by gsora (aka peppelakappa)"

func createTokenRequest(reqType string, url string, body url.Values, username string, password string) (*http.Request, error) {
	req, err := http.NewRequest(reqType, url, strings.NewReader(body.Encode()))
	if err != nil {
		return &http.Request{}, err
	}

	req.Header.Add("User-Agent", userAgent)
	req.SetBasicAuth(username, password)

	return req, nil
}

func createAuthenticatedRequest(reqType string, url string, token TokenResponse, body url.Values) (*http.Request, error) {
	req, err := http.NewRequest(reqType, url, strings.NewReader(body.Encode()))
	if err != nil {
		return &http.Request{}, err
	}
	req.Header.Add("User-Agent", userAgent)
	req.Header.Add("Authorization", token.GetAuthString())
	return req, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

// +build darwin

package libkb

import (
	"encoding/base64"

	keychain "github.com/keybase/go-keychain"
)

type KeychainSecretStore struct {
	context     SecretStoreContext
	accountName string
}

var _ SecretStore = KeychainSecretStore{}

func (k KeychainSecretStore) serviceName() string {
	return k.context.GetStoredSecretServiceName()
}

func (k KeychainSecretStore) StoreSecret(secret []byte) (err error) {
	encodedSecret := base64.StdEncoding.EncodeToString(secret)
	item := keychain.NewGenericPassword(k.serviceName(), k.accountName, "", []byte(encodedSecret), k.accessGroup())
	item.SetSynchronizable(k.synchronizable())
	item.SetAccessible(k.accessible())
	keychain.DeleteItem(item)
	return keychain.AddItem(item)
}

func (k KeychainSecretStore) RetrieveSecret() ([]byte, error) {
	encodedSecret, err := keychain.GetGenericPassword(k.serviceName(), k.accountName, "", "")
	if err != nil {
		return nil, err
	}
	if encodedSecret == nil {
		return nil, SecretStoreError{Msg: "No secret for " + k.accountName}
	}

	secret, err := base64.StdEncoding.DecodeString(string(encodedSecret))
	if err != nil {
		return nil, err
	}

	return secret, nil
}

func (k KeychainSecretStore) ClearSecret() error {
	query := keychain.NewGenericPassword(k.serviceName(), k.accountName, "", nil, "")
	query.SetMatchLimit(keychain.MatchLimitAll)
	err := keychain.DeleteItem(query)
	if err == keychain.ErrorItemNotFound {
		return nil
	}
	return err
}

func NewSecretStore(context SecretStoreContext, username NormalizedUsername) SecretStore {
	return KeychainSecretStore{
		context:     context,
		accountName: username.String(),
	}
}

func HasSecretStore() bool {
	return true
}

func GetUsersWithStoredSecrets(c SecretStoreContext) ([]string, error) {
	return keychain.GetAccountsForService(c.GetStoredSecretServiceName())
}

func GetTerminalPrompt() string {
	return "Store your key in Apple's local keychain?"
}
<commit_msg>Add back in helpful comment about base64'ing data in secret store<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

// +build darwin

package libkb

import (
	"encoding/base64"

	keychain "github.com/keybase/go-keychain"
)

type KeychainSecretStore struct {
	context     SecretStoreContext
	accountName string
}

var _ SecretStore = KeychainSecretStore{}

func (k KeychainSecretStore) serviceName() string {
	return k.context.GetStoredSecretServiceName()
}

func (k KeychainSecretStore) StoreSecret(secret []byte) (err error) {
	// Base64 encode to make it easy to work with Keychain Access (since we are using a password item and secret is not utf-8)
	encodedSecret := base64.StdEncoding.EncodeToString(secret)
	item := keychain.NewGenericPassword(k.serviceName(), k.accountName, "", []byte(encodedSecret), k.accessGroup())
	item.SetSynchronizable(k.synchronizable())
	item.SetAccessible(k.accessible())
	keychain.DeleteItem(item)
	return keychain.AddItem(item)
}

func (k KeychainSecretStore) RetrieveSecret() ([]byte, error) {
	encodedSecret, err := keychain.GetGenericPassword(k.serviceName(), k.accountName, "", "")
	if err != nil {
		return nil, err
	}
	if encodedSecret == nil {
		return nil, SecretStoreError{Msg: "No secret for " + k.accountName}
	}

	secret, err := base64.StdEncoding.DecodeString(string(encodedSecret))
	if err != nil {
		return nil, err
	}

	return secret, nil
}

func (k KeychainSecretStore) ClearSecret() error {
	query := keychain.NewGenericPassword(k.serviceName(), k.accountName, "", nil, "")
	query.SetMatchLimit(keychain.MatchLimitAll)
	err := keychain.DeleteItem(query)
	if err == keychain.ErrorItemNotFound {
		return nil
	}
	return err
}

func NewSecretStore(context SecretStoreContext, username NormalizedUsername) SecretStore {
	return KeychainSecretStore{
		context:     context,
		accountName: username.String(),
	}
}

func HasSecretStore() bool {
	return true
}

func GetUsersWithStoredSecrets(c SecretStoreContext) ([]string, error) {
	return keychain.GetAccountsForService(c.GetStoredSecretServiceName())
}

func GetTerminalPrompt() string {
	return "Store your key in Apple's local keychain?"
}
<|endoftext|>