<commit_before>/*
 * This file is part of the kubevirt project
 *
 * 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.
 *
 * Copyright 2017 Red Hat, Inc.
 *
 */

package cloudinit

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"

	model "github.com/jeevatkm/go-model"

	"kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/logging"
	"kubevirt.io/kubevirt/pkg/precond"
)

type IsoCreationFunc func(isoOutFile string, inFiles []string) error

var cloudInitLocalDir = "/var/run/libvirt/cloud-init-dir"
var cloudInitOwner = "qemu"
var cloudInitIsoFunc = defaultIsoFunc

const noCloudFile = "noCloud.iso"

// Supported DataSources
const (
	dataSourceNoCloud = "noCloud"
)

func defaultIsoFunc(isoOutFile string, inFiles []string) error {

	var args []string

	args = append(args, "-output")
	args = append(args, isoOutFile)
	args = append(args, "-volid")
	args = append(args, "cidata")
	args = append(args, "-joliet")
	args = append(args, "-rock")
	args = append(args, inFiles...)

	cmd := exec.Command("genisoimage", args...)
	return cmd.Run()
}

func removeFile(path string) error {
	err := os.Remove(path)
	if err != nil && os.IsNotExist(err) {
		return nil
	} else if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("failed to remove cloud-init temporary data file %s", path))
		return err
	}
	return nil
}

func fileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	exists := false

	if err == nil {
		exists = true
	} else if os.IsNotExist(err) {
		err = nil
	}
	return exists, err
}

func md5CheckSum(filePath string) ([]byte, error) {
	var result []byte

	file, err := os.Open(filePath)
	if err != nil {
		return result, err
	}
	defer file.Close()

	hash := md5.New()
	_, err = io.Copy(hash, file)

	if err != nil {
		return result, err
	}

	result = hash.Sum(result)
	return result, nil
}

func setFileOwnership(username string, file string) error {
	usrObj, err := user.Lookup(username)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("unable to look up username %s", username))
		return err
	}

	uid, err := strconv.Atoi(usrObj.Uid)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("unable to find uid for username %s", username))
		return err
	}

	gid, err := strconv.Atoi(usrObj.Gid)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("unable to find gid for username %s", username))
		return err
	}

	return os.Chown(file, uid, gid)
}

func filesAreEqual(path1 string, path2 string) (bool, error) {
	exists, err := fileExists(path1)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("unexpected error encountered while attempting to determine if %s exists", path1))
		return false, err
	} else if exists == false {
		return false, nil
	}

	exists, err = fileExists(path2)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("unexpected error encountered while attempting to determine if %s exists", path2))
		return false, err
	} else if exists == false {
		return false, nil
	}

	sum1, err := md5CheckSum(path1)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("calculating md5 checksum failed for %s", path1))
		return false, err
	}
	sum2, err := md5CheckSum(path2)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("calculating md5 checksum failed for %s", path2))
		return false, err
	}

	return bytes.Equal(sum1, sum2), nil
}

// The unit test suite uses this function
func SetIsoCreationFunction(isoFunc IsoCreationFunc) {
	cloudInitIsoFunc = isoFunc
}

// The unit test suite uses this function
func SetLocalDataOwner(user string) {
	cloudInitOwner = user
}

func SetLocalDirectory(dir string) error {

	err := os.MkdirAll(dir, 0755)
	if err != nil {
		return errors.New(fmt.Sprintf("Unable to initalize cloudInit local cache directory (%s). %v", dir, err))
	}

	exists, err := fileExists(dir)
	if err != nil {
		return errors.New(fmt.Sprintf("CloudInit local cache directory (%s) does not exist or is inaccessible. %v", dir, err))
	} else if exists == false {
		return errors.New(fmt.Sprintf("CloudInit local cache directory (%s) does not exist or is inaccessible.", dir))
	}

	cloudInitLocalDir = dir
	return nil
}

func GetDomainBasePath(domain string, namespace string) string {
	return fmt.Sprintf("%s/%s/%s", cloudInitLocalDir, namespace, domain)
}

// This is called right before a VM is defined with libvirt.
// If the cloud-init type requires altering the domain, this
// is the place to do that.
func MapCloudInitDisks(vm *v1.VM) (*v1.VM, error) {
	namespace := precond.MustNotBeEmpty(vm.GetObjectMeta().GetNamespace())
	domain := precond.MustNotBeEmpty(vm.GetObjectMeta().GetName())

	spec := GetCloudInitSpec(vm)
	if spec == nil {
		return vm, nil
	}

	err := ValidateArgs(spec)
	if err != nil {
		return vm, err
	}

	dataSource := getDataSource(spec)
	switch dataSource {
	case dataSourceNoCloud:
		vmCopy := &v1.VM{}
		model.Copy(vmCopy, vm)
		filePath := fmt.Sprintf("%s/%s", GetDomainBasePath(domain, namespace), noCloudFile)

		for idx, disk := range vmCopy.Spec.Domain.Devices.Disks {
			if disk.Type == "file" && disk.CloudInit != nil {
				newDisk := v1.Disk{}
				newDisk.Type = "file"
				newDisk.Device = "disk"
				newDisk.Driver = &v1.DiskDriver{
					Type: "raw",
					Name: "qemu",
				}
				newDisk.Source.File = filePath
				newDisk.Target = disk.Target
				vmCopy.Spec.Domain.Devices.Disks[idx] = newDisk
			}
		}
		return vmCopy, nil
	default:
		return vm, errors.New(fmt.Sprintf("Unknown CloudInit type %s", dataSource))
	}
}

func ValidateArgs(spec *v1.CloudInitSpec) error {
	if spec == nil {
		return nil
	}

	dataSource := getDataSource(spec)
	switch dataSource {
	case dataSourceNoCloud:
		if spec.NoCloudData == nil {
			return errors.New(fmt.Sprintf("DataSource %s does not have the required data initialized", dataSource))
		}
		if spec.NoCloudData.UserDataBase64 == "" {
			return errors.New(fmt.Sprintf("userDataBase64 is required for cloudInit type %s", dataSource))
		}
		if spec.NoCloudData.MetaDataBase64 == "" {
			return errors.New(fmt.Sprintf("metaDataBase64 is required for cloudInit type %s", dataSource))
		}
	default:
		return errors.New(fmt.Sprintf("Unknown CloudInit dataSource %s", dataSource))
	}

	return nil
}

// Place metadata auto-generation code in here
func ApplyMetadata(vm *v1.VM) {
	spec := GetCloudInitSpec(vm)
	if spec == nil {
		return
	}

	namespace := precond.MustNotBeEmpty(vm.GetObjectMeta().GetNamespace())
	domain := precond.MustNotBeEmpty(vm.GetObjectMeta().GetName())

	switch getDataSource(spec) {
	case dataSourceNoCloud:
		// Only autogenerate metadata if user defined metadata does not exist
		if spec.NoCloudData.MetaDataBase64 != "" {
			return
		}
		// TODO Put local-hostname in MetaData once we get pod DNS working with VMs
		msg := fmt.Sprintf("instance-id: %s-%s\n", domain, namespace)
		spec.NoCloudData.MetaDataBase64 = base64.StdEncoding.EncodeToString([]byte(msg))
	}
}

func RemoveLocalData(domain string, namespace string) error {
	domainBasePath := GetDomainBasePath(domain, namespace)
	err := os.RemoveAll(domainBasePath)
	if err != nil && os.IsNotExist(err) {
		return nil
	}
	return err
}

func GetCloudInitSpec(vm *v1.VM) *v1.CloudInitSpec {
	// search various places cloud init spec may live.
	// at the moment, cloud init only exists on disks.
	for _, disk := range vm.Spec.Domain.Devices.Disks {
		if disk.CloudInit != nil {
			return disk.CloudInit
		}
	}
	return nil
}

func getDataSource(spec *v1.CloudInitSpec) string {
	if spec == nil {
		return ""
	}

	if spec.NoCloudData != nil {
		return dataSourceNoCloud
	}
	return ""
}

func GenerateLocalData(domain string, namespace string, spec *v1.CloudInitSpec) error {
	if spec == nil {
		return nil
	}

	err := ValidateArgs(spec)
	if err != nil {
		return err
	}

	domainBasePath := GetDomainBasePath(domain, namespace)
	err = os.MkdirAll(domainBasePath, 0755)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("unable to create cloud-init base path %s", domainBasePath))
		return err
	}

	switch getDataSource(spec) {
	case dataSourceNoCloud:
		metaFile := fmt.Sprintf("%s/%s", domainBasePath, "meta-data")
		userFile := fmt.Sprintf("%s/%s", domainBasePath, "user-data")
		iso := fmt.Sprintf("%s/%s", domainBasePath, noCloudFile)
		isoStaging := fmt.Sprintf("%s/%s.staging", domainBasePath, noCloudFile)
		userData64 := spec.NoCloudData.UserDataBase64
		metaData64 := spec.NoCloudData.MetaDataBase64

		removeFile(userFile)
		removeFile(metaFile)
		removeFile(isoStaging)

		userDataBytes, err := base64.StdEncoding.DecodeString(userData64)
		if err != nil {
			return err
		}
		metaDataBytes, err := base64.StdEncoding.DecodeString(metaData64)
		if err != nil {
			return err
		}

		err = ioutil.WriteFile(userFile, userDataBytes, 0644)
		if err != nil {
			return err
		}
		err = ioutil.WriteFile(metaFile, metaDataBytes, 0644)
		if err != nil {
			return err
		}

		files := make([]string, 0, 2)
		files = append(files, metaFile)
		files = append(files, userFile)
		err = cloudInitIsoFunc(isoStaging, files)
		if err != nil {
			return err
		}
		removeFile(metaFile)
		removeFile(userFile)

		err = setFileOwnership(cloudInitOwner, isoStaging)
		if err != nil {
			return err
		}

		isEqual, err := filesAreEqual(iso, isoStaging)
		if err != nil {
			return err
		}

		// Only replace the dynamically generated iso if it has a different checksum
		if isEqual {
			removeFile(isoStaging)
		} else {
			removeFile(iso)
			err = os.Rename(isoStaging, iso)
			if err != nil {
				// This error is not something we need to block iso creation for.
				logging.DefaultLogger().Error().Reason(err).Msg(fmt.Sprintf("Cloud-init failed to rename file %s to %s", isoStaging, iso))
				return err
			}
		}

		logging.DefaultLogger().V(2).Info().Msg(fmt.Sprintf("generated nocloud iso file %s", iso))
	}
	return nil
}

// Lists all vms cloud-init has local data for
func ListVmWithLocalData() ([]*v1.VM, error) {
	var keys []*v1.VM

	err := filepath.Walk(cloudInitLocalDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() == false {
			return nil
		}

		relativePath := strings.TrimPrefix(path, cloudInitLocalDir+"/")
		if relativePath == "" {
			return nil
		}
		dirs := strings.Split(relativePath, "/")
		if len(dirs) != 2 {
			return nil
		}

		namespace := dirs[0]
		domain := dirs[1]
		if namespace == "" || domain == "" {
			return nil
		}
		keys = append(keys, v1.NewVMReferenceFromNameWithNS(dirs[0], dirs[1]))
		return nil
	})

	return keys, err
}
<commit_msg>Give cloud-init iso creation a timeout period<commit_after>/*
 * This file is part of the kubevirt project
 *
 * 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.
 *
 * Copyright 2017 Red Hat, Inc.
 *
 */

package cloudinit

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	model "github.com/jeevatkm/go-model"

	"kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/logging"
	"kubevirt.io/kubevirt/pkg/precond"
)

type IsoCreationFunc func(isoOutFile string, inFiles []string) error

var cloudInitLocalDir = "/var/run/libvirt/cloud-init-dir"
var cloudInitOwner = "qemu"
var cloudInitIsoFunc = defaultIsoFunc

const noCloudFile = "noCloud.iso"

// Supported DataSources
const (
	dataSourceNoCloud = "noCloud"
)

func defaultIsoFunc(isoOutFile string, inFiles []string) error {

	var args []string

	args = append(args, "-output")
	args = append(args, isoOutFile)
	args = append(args, "-volid")
	args = append(args, "cidata")
	args = append(args, "-joliet")
	args = append(args, "-rock")
	args = append(args, inFiles...)

	cmd := exec.Command("genisoimage", args...)

	err := cmd.Start()
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("genisoimage cmd failed to start while generating iso file %s", isoOutFile))
		return err
	}

	done := make(chan error)
	go func() { done <- cmd.Wait() }()

	timeout := time.After(10 * time.Second)

	for {
		select {
		case <-timeout:
			logging.DefaultLogger().V(2).Error().Msg(fmt.Sprintf("Timed out generating cloud-init iso at path %s", isoOutFile))
			cmd.Process.Kill()
		case err := <-done:
			if err != nil {
				logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("genisoimage returned non-zero exit code while generating iso file %s", isoOutFile))
				return err
			}
			return nil
		}
	}
}

func removeFile(path string) error {
	err := os.Remove(path)
	if err != nil && os.IsNotExist(err) {
		return nil
	} else if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("failed to remove cloud-init temporary data file %s", path))
		return err
	}
	return nil
}

func fileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	exists := false

	if err == nil {
		exists = true
	} else if os.IsNotExist(err) {
		err = nil
	}
	return exists, err
}

func md5CheckSum(filePath string) ([]byte, error) {
	var result []byte

	file, err := os.Open(filePath)
	if err != nil {
		return result, err
	}
	defer file.Close()

	hash := md5.New()
	_, err = io.Copy(hash, file)

	if err != nil {
		return result, err
	}

	result = hash.Sum(result)
	return result, nil
}

func setFileOwnership(username string, file string) error {
	usrObj, err := user.Lookup(username)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("unable to look up username %s", username))
		return err
	}

	uid, err := strconv.Atoi(usrObj.Uid)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("unable to find uid for username %s", username))
		return err
	}

	gid, err := strconv.Atoi(usrObj.Gid)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("unable to find gid for username %s", username))
		return err
	}

	return os.Chown(file, uid, gid)
}

func filesAreEqual(path1 string, path2 string) (bool, error) {
	exists, err := fileExists(path1)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("unexpected error encountered while attempting to determine if %s exists", path1))
		return false, err
	} else if exists == false {
		return false, nil
	}

	exists, err = fileExists(path2)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("unexpected error encountered while attempting to determine if %s exists", path2))
		return false, err
	} else if exists == false {
		return false, nil
	}

	sum1, err := md5CheckSum(path1)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("calculating md5 checksum failed for %s", path1))
		return false, err
	}
	sum2, err := md5CheckSum(path2)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("calculating md5 checksum failed for %s", path2))
		return false, err
	}

	return bytes.Equal(sum1, sum2), nil
}

// The unit test suite uses this function
func SetIsoCreationFunction(isoFunc IsoCreationFunc) {
	cloudInitIsoFunc = isoFunc
}

// The unit test suite uses this function
func SetLocalDataOwner(user string) {
	cloudInitOwner = user
}

func SetLocalDirectory(dir string) error {

	err := os.MkdirAll(dir, 0755)
	if err != nil {
		return errors.New(fmt.Sprintf("Unable to initalize cloudInit local cache directory (%s). %v", dir, err))
	}

	exists, err := fileExists(dir)
	if err != nil {
		return errors.New(fmt.Sprintf("CloudInit local cache directory (%s) does not exist or is inaccessible. %v", dir, err))
	} else if exists == false {
		return errors.New(fmt.Sprintf("CloudInit local cache directory (%s) does not exist or is inaccessible.", dir))
	}

	cloudInitLocalDir = dir
	return nil
}

func GetDomainBasePath(domain string, namespace string) string {
	return fmt.Sprintf("%s/%s/%s", cloudInitLocalDir, namespace, domain)
}

// This is called right before a VM is defined with libvirt.
// If the cloud-init type requires altering the domain, this
// is the place to do that.
func MapCloudInitDisks(vm *v1.VM) (*v1.VM, error) {
	namespace := precond.MustNotBeEmpty(vm.GetObjectMeta().GetNamespace())
	domain := precond.MustNotBeEmpty(vm.GetObjectMeta().GetName())

	spec := GetCloudInitSpec(vm)
	if spec == nil {
		return vm, nil
	}

	err := ValidateArgs(spec)
	if err != nil {
		return vm, err
	}

	dataSource := getDataSource(spec)
	switch dataSource {
	case dataSourceNoCloud:
		vmCopy := &v1.VM{}
		model.Copy(vmCopy, vm)
		filePath := fmt.Sprintf("%s/%s", GetDomainBasePath(domain, namespace), noCloudFile)

		for idx, disk := range vmCopy.Spec.Domain.Devices.Disks {
			if disk.Type == "file" && disk.CloudInit != nil {
				newDisk := v1.Disk{}
				newDisk.Type = "file"
				newDisk.Device = "disk"
				newDisk.Driver = &v1.DiskDriver{
					Type: "raw",
					Name: "qemu",
				}
				newDisk.Source.File = filePath
				newDisk.Target = disk.Target
				vmCopy.Spec.Domain.Devices.Disks[idx] = newDisk
			}
		}
		return vmCopy, nil
	default:
		return vm, errors.New(fmt.Sprintf("Unknown CloudInit type %s", dataSource))
	}
}

func ValidateArgs(spec *v1.CloudInitSpec) error {
	if spec == nil {
		return nil
	}

	dataSource := getDataSource(spec)
	switch dataSource {
	case dataSourceNoCloud:
		if spec.NoCloudData == nil {
			return errors.New(fmt.Sprintf("DataSource %s does not have the required data initialized", dataSource))
		}
		if spec.NoCloudData.UserDataBase64 == "" {
			return errors.New(fmt.Sprintf("userDataBase64 is required for cloudInit type %s", dataSource))
		}
		if spec.NoCloudData.MetaDataBase64 == "" {
			return errors.New(fmt.Sprintf("metaDataBase64 is required for cloudInit type %s", dataSource))
		}
	default:
		return errors.New(fmt.Sprintf("Unknown CloudInit dataSource %s", dataSource))
	}

	return nil
}

// Place metadata auto-generation code in here
func ApplyMetadata(vm *v1.VM) {
	spec := GetCloudInitSpec(vm)
	if spec == nil {
		return
	}

	namespace := precond.MustNotBeEmpty(vm.GetObjectMeta().GetNamespace())
	domain := precond.MustNotBeEmpty(vm.GetObjectMeta().GetName())

	switch getDataSource(spec) {
	case dataSourceNoCloud:
		// Only autogenerate metadata if user defined metadata does not exist
		if spec.NoCloudData.MetaDataBase64 != "" {
			return
		}
		// TODO Put local-hostname in MetaData once we get pod DNS working with VMs
		msg := fmt.Sprintf("instance-id: %s-%s\n", domain, namespace)
		spec.NoCloudData.MetaDataBase64 = base64.StdEncoding.EncodeToString([]byte(msg))
	}
}

func RemoveLocalData(domain string, namespace string) error {
	domainBasePath := GetDomainBasePath(domain, namespace)
	err := os.RemoveAll(domainBasePath)
	if err != nil && os.IsNotExist(err) {
		return nil
	}
	return err
}

func GetCloudInitSpec(vm *v1.VM) *v1.CloudInitSpec {
	// search various places cloud init spec may live.
	// at the moment, cloud init only exists on disks.
	for _, disk := range vm.Spec.Domain.Devices.Disks {
		if disk.CloudInit != nil {
			return disk.CloudInit
		}
	}
	return nil
}

func getDataSource(spec *v1.CloudInitSpec) string {
	if spec == nil {
		return ""
	}

	if spec.NoCloudData != nil {
		return dataSourceNoCloud
	}
	return ""
}

func GenerateLocalData(domain string, namespace string, spec *v1.CloudInitSpec) error {
	if spec == nil {
		return nil
	}

	err := ValidateArgs(spec)
	if err != nil {
		return err
	}

	domainBasePath := GetDomainBasePath(domain, namespace)
	err = os.MkdirAll(domainBasePath, 0755)
	if err != nil {
		logging.DefaultLogger().V(2).Error().Reason(err).Msg(fmt.Sprintf("unable to create cloud-init base path %s", domainBasePath))
		return err
	}

	switch getDataSource(spec) {
	case dataSourceNoCloud:
		metaFile := fmt.Sprintf("%s/%s", domainBasePath, "meta-data")
		userFile := fmt.Sprintf("%s/%s", domainBasePath, "user-data")
		iso := fmt.Sprintf("%s/%s", domainBasePath, noCloudFile)
		isoStaging := fmt.Sprintf("%s/%s.staging", domainBasePath, noCloudFile)
		userData64 := spec.NoCloudData.UserDataBase64
		metaData64 := spec.NoCloudData.MetaDataBase64

		removeFile(userFile)
		removeFile(metaFile)
		removeFile(isoStaging)

		userDataBytes, err := base64.StdEncoding.DecodeString(userData64)
		if err != nil {
			return err
		}
		metaDataBytes, err := base64.StdEncoding.DecodeString(metaData64)
		if err != nil {
			return err
		}

		err = ioutil.WriteFile(userFile, userDataBytes, 0644)
		if err != nil {
			return err
		}
		err = ioutil.WriteFile(metaFile, metaDataBytes, 0644)
		if err != nil {
			return err
		}

		files := make([]string, 0, 2)
		files = append(files, metaFile)
		files = append(files, userFile)
		err = cloudInitIsoFunc(isoStaging, files)
		if err != nil {
			return err
		}
		removeFile(metaFile)
		removeFile(userFile)

		err = setFileOwnership(cloudInitOwner, isoStaging)
		if err != nil {
			return err
		}

		isEqual, err := filesAreEqual(iso, isoStaging)
		if err != nil {
			return err
		}

		// Only replace the dynamically generated iso if it has a different checksum
		if isEqual {
			removeFile(isoStaging)
		} else {
			removeFile(iso)
			err = os.Rename(isoStaging, iso)
			if err != nil {
				// This error is not something we need to block iso creation for.
				logging.DefaultLogger().Error().Reason(err).Msg(fmt.Sprintf("Cloud-init failed to rename file %s to %s", isoStaging, iso))
				return err
			}
		}

		logging.DefaultLogger().V(2).Info().Msg(fmt.Sprintf("generated nocloud iso file %s", iso))
	}
	return nil
}

// Lists all vms cloud-init has local data for
func ListVmWithLocalData() ([]*v1.VM, error) {
	var keys []*v1.VM

	err := filepath.Walk(cloudInitLocalDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() == false {
			return nil
		}

		relativePath := strings.TrimPrefix(path, cloudInitLocalDir+"/")
		if relativePath == "" {
			return nil
		}
		dirs := strings.Split(relativePath, "/")
		if len(dirs) != 2 {
			return nil
		}

		namespace := dirs[0]
		domain := dirs[1]
		if namespace == "" || domain == "" {
			return nil
		}
		keys = append(keys, v1.NewVMReferenceFromNameWithNS(dirs[0], dirs[1]))
		return nil
	})

	return keys, err
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/fujiwara/fluent-agent-hydra/hydra"
	"log"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"syscall"
)

var (
	version     string
	buildDate   string
	trapSignals = []os.Signal{
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT}
)

func main() {
	var (
		configFile  string
		help        bool
		fieldName   string
		monitorAddr string
		showVersion bool
	)
	flag.StringVar(&configFile, "c", "", "configuration file path")
	flag.BoolVar(&help, "h", false, "show help message")
	flag.BoolVar(&help, "help", false, "show help message")
	flag.StringVar(&fieldName, "f", hydra.DefaultFieldName, "fieldname of fluentd log message attribute (DEFAULT: message)")
	flag.StringVar(&monitorAddr, "monitor", "", "monitor HTTP server address")
	flag.BoolVar(&showVersion, "v", false, "show version")
	flag.BoolVar(&showVersion, "version", false, "show version")
	flag.Parse()

	if showVersion {
		fmt.Println("version:", version)
		fmt.Println("build:", buildDate)
		os.Exit(0)
	}
	if help {
		usage()
	}
	if pprofile := os.Getenv("PPROF"); pprofile != "" {
		f, err := os.Create(pprofile)
		if err != nil {
			log.Fatal("[error] Can't create profiling stat file.", err)
		}
		log.Println("[info] StartCPUProfile() stat file", f.Name())
		pprof.StartCPUProfile(f)
	}

	done := make(chan os.Signal, 1)
	signal.Notify(done, trapSignals...)
	if configFile != "" {
		config, err := hydra.ReadConfig(configFile)
		if err != nil {
			log.Println("Can't load config", err)
			os.Exit(2)
		}
		run(config)
	} else if args := flag.Args(); len(args) >= 3 {
		config := hydra.NewConfigByArgs(args, fieldName, monitorAddr)
		run(config)
	} else {
		usage()
	}
	sig := <-done
	log.Println("[info] SIGNAL", sig, "exit.")
	pprof.StopCPUProfile()
	os.Exit(0)
}

func usage() {
	fmt.Println("Usage of fluent-agent-hydra")
	fmt.Println("")
	fmt.Println("  fluent-agent-hydra -c config.toml")
	fmt.Println("  fluent-agent-hydra [options] TAG TARGET_FILE PRIMARY_SERVER SECONDARY_SERVER")
	fmt.Println("")
	flag.PrintDefaults()
	os.Exit(1)
}

func run(config *hydra.Config) {
	messageCh, monitorCh := hydra.NewChannel()

	if config.ReadBufferSize > 0 {
		hydra.ReadBufferSize = config.ReadBufferSize
		log.Println("[info] set ReadBufferSize", hydra.ReadBufferSize)
	}

	// start monitor server
	monitor, err := hydra.NewMonitor(config, monitorCh)
	if err != nil {
		log.Println("[error] Couldn't start monitor server.", err)
	} else {
		go monitor.Run()
	}

	// start out_forward
	outForward, err := hydra.NewOutForward(config.Servers, messageCh, monitorCh)
	if err != nil {
		log.Println("[error]", err)
	} else {
		go outForward.Run()
	}

	// start watcher && in_tail
	if len(config.Logs) > 0 {
		watcher, err := hydra.NewWatcher()
		if err != nil {
			log.Println("[error]", err)
		}
		for _, configLogfile := range config.Logs {
			tail, err := hydra.NewInTail(configLogfile, watcher, messageCh, monitorCh)
			if err != nil {
				log.Println("[error]", err)
			} else {
				go tail.Run()
			}
		}
		go watcher.Run()
	}

	// start in_forward
	if config.Receiver != nil {
		if runtime.GOMAXPROCS(0) < 2 {
			log.Println("[warning] When using Receiver, recommend to set GOMAXPROCS >= 2.")
		}
		inForward, err := hydra.NewInForward(config.Receiver, messageCh, monitorCh)
		if err != nil {
			log.Println("[error]", err)
		} else {
			go inForward.Run()
		}
	}
}
<commit_msg>alias cmd option -m (-monitor)<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/fujiwara/fluent-agent-hydra/hydra"
	"log"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"syscall"
)

var (
	version     string
	buildDate   string
	trapSignals = []os.Signal{
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT}
)

func main() {
	var (
		configFile  string
		help        bool
		fieldName   string
		monitorAddr string
		showVersion bool
	)
	flag.StringVar(&configFile, "c", "", "configuration file path")
	flag.BoolVar(&help, "h", false, "show help message")
	flag.BoolVar(&help, "help", false, "show help message")
	flag.StringVar(&fieldName, "f", hydra.DefaultFieldName, "fieldname of fluentd log message attribute (DEFAULT: message)")
	flag.StringVar(&monitorAddr, "monitor", "", "monitor HTTP server address")
	flag.StringVar(&monitorAddr, "m", "", "monitor HTTP server address")
	flag.BoolVar(&showVersion, "v", false, "show version")
	flag.BoolVar(&showVersion, "version", false, "show version")
	flag.Parse()

	if showVersion {
		fmt.Println("version:", version)
		fmt.Println("build:", buildDate)
		os.Exit(0)
	}
	if help {
		usage()
	}
	if pprofile := os.Getenv("PPROF"); pprofile != "" {
		f, err := os.Create(pprofile)
		if err != nil {
			log.Fatal("[error] Can't create profiling stat file.", err)
		}
		log.Println("[info] StartCPUProfile() stat file", f.Name())
		pprof.StartCPUProfile(f)
	}

	done := make(chan os.Signal, 1)
	signal.Notify(done, trapSignals...)
	if configFile != "" {
		config, err := hydra.ReadConfig(configFile)
		if err != nil {
			log.Println("Can't load config", err)
			os.Exit(2)
		}
		run(config)
	} else if args := flag.Args(); len(args) >= 3 {
		config := hydra.NewConfigByArgs(args, fieldName, monitorAddr)
		run(config)
	} else {
		usage()
	}
	sig := <-done
	log.Println("[info] SIGNAL", sig, "exit.")
	pprof.StopCPUProfile()
	os.Exit(0)
}

func usage() {
	fmt.Println("Usage of fluent-agent-hydra")
	fmt.Println("")
	fmt.Println("  fluent-agent-hydra -c config.toml")
	fmt.Println("  fluent-agent-hydra [options] TAG TARGET_FILE PRIMARY_SERVER SECONDARY_SERVER")
	fmt.Println("")
	flag.PrintDefaults()
	os.Exit(1)
}

func run(config *hydra.Config) {
	messageCh, monitorCh := hydra.NewChannel()

	if config.ReadBufferSize > 0 {
		hydra.ReadBufferSize = config.ReadBufferSize
		log.Println("[info] set ReadBufferSize", hydra.ReadBufferSize)
	}

	// start monitor server
	monitor, err := hydra.NewMonitor(config, monitorCh)
	if err != nil {
		log.Println("[error] Couldn't start monitor server.", err)
	} else {
		go monitor.Run()
	}

	// start out_forward
	outForward, err := hydra.NewOutForward(config.Servers, messageCh, monitorCh)
	if err != nil {
		log.Println("[error]", err)
	} else {
		go outForward.Run()
	}

	// start watcher && in_tail
	if len(config.Logs) > 0 {
		watcher, err := hydra.NewWatcher()
		if err != nil {
			log.Println("[error]", err)
		}
		for _, configLogfile := range config.Logs {
			tail, err := hydra.NewInTail(configLogfile, watcher, messageCh, monitorCh)
			if err != nil {
				log.Println("[error]", err)
			} else {
				go tail.Run()
			}
		}
		go watcher.Run()
	}

	// start in_forward
	if config.Receiver != nil {
		if runtime.GOMAXPROCS(0) < 2 {
			log.Println("[warning] When using Receiver, recommend to set GOMAXPROCS >= 2.")
		}
		inForward, err := hydra.NewInForward(config.Receiver, messageCh, monitorCh)
		if err != nil {
			log.Println("[error]", err)
		} else {
			go inForward.Run()
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package oauth

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"

	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/auth"
	"github.com/tsuru/tsuru/auth/native"
	tsuruErrors "github.com/tsuru/tsuru/errors"
	"github.com/tsuru/tsuru/log"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)

var (
	ErrMissingCodeError       = &tsuruErrors.ValidationError{Message: "You must provide code to login"}
	ErrMissingCodeRedirectUrl = &tsuruErrors.ValidationError{Message: "You must provide the used redirect url to login"}
	ErrEmptyAccessToken       = &tsuruErrors.NotAuthorizedError{Message: "Couldn't convert code to access token."}
	ErrEmptyUserEmail         = &tsuruErrors.NotAuthorizedError{Message: "Couldn't parse user email."}

	requestLatencies = prometheus.NewHistogram(prometheus.HistogramOpts{
		Name: "tsuru_oath_request_duration_seconds",
		Help: "The oath requests latency distributions.",
	})
	requestErrors = prometheus.NewCounter(prometheus.CounterOpts{
		Name: "tsuru_oath_request_errors_total",
		Help: "The total number of oath request errors.",
	})
)

type OAuthParser interface {
	Parse(infoResponse *http.Response) (string, error)
}

type OAuthScheme struct {
	BaseConfig   oauth2.Config
	InfoUrl      string
	CallbackPort int
	Parser       OAuthParser
}

func init() {
	auth.RegisterScheme("oauth", &OAuthScheme{})
	prometheus.MustRegister(requestLatencies)
	prometheus.MustRegister(requestErrors)
}

// This method loads basic config and returns a copy of the
// config object.
func (s *OAuthScheme) loadConfig() (oauth2.Config, error) {
	if s.BaseConfig.ClientID != "" {
		return s.BaseConfig, nil
	}
	if s.Parser == nil {
		s.Parser = s
	}
	var emptyConfig oauth2.Config
	clientId, err := config.GetString("auth:oauth:client-id")
	if err != nil {
		return emptyConfig, err
	}
	clientSecret, err := config.GetString("auth:oauth:client-secret")
	if err != nil {
		return emptyConfig, err
	}
	scope, err := config.GetString("auth:oauth:scope")
	if err != nil {
		return emptyConfig, err
	}
	authURL, err := config.GetString("auth:oauth:auth-url")
	if err != nil {
		return emptyConfig, err
	}
	tokenURL, err := config.GetString("auth:oauth:token-url")
	if err != nil {
		return emptyConfig, err
	}
	infoURL, err := config.GetString("auth:oauth:info-url")
	if err != nil {
		return emptyConfig, err
	}
	callbackPort, err := config.GetInt("auth:oauth:callback-port")
	if err != nil {
		log.Debugf("auth:oauth:callback-port not found using random port: %s", err)
	}
	s.InfoUrl = infoURL
	s.CallbackPort = callbackPort
	s.BaseConfig = oauth2.Config{
		ClientID:     clientId,
		ClientSecret: clientSecret,
		Scopes:       []string{scope},
		Endpoint: oauth2.Endpoint{
			AuthURL:  authURL,
			TokenURL: tokenURL,
		},
	}
	return s.BaseConfig, nil
}

func (s *OAuthScheme) Login(params map[string]string) (auth.Token, error) {
	conf, err := s.loadConfig()
	if err != nil {
		return nil, err
	}
	code, ok := params["code"]
	if !ok {
		return nil, ErrMissingCodeError
	}
	redirectUrl, ok := params["redirectUrl"]
	if !ok {
		return nil, ErrMissingCodeRedirectUrl
	}
	conf.RedirectURL = redirectUrl
	oauthToken, err := conf.Exchange(context.Background(), code)
	if err != nil {
		return nil, err
	}
	return s.handleToken(oauthToken)
}

func (s *OAuthScheme) handleToken(t *oauth2.Token) (*Token, error) {
	if t.AccessToken == "" {
		return nil, ErrEmptyAccessToken
	}
	conf, err := s.loadConfig()
	if err != nil {
		return nil, err
	}
	client := conf.Client(context.Background(), t)
	t0 := time.Now()
	response, err := client.Get(s.InfoUrl)
	requestLatencies.Observe(time.Since(t0).Seconds())
	if err != nil {
		requestErrors.Inc()
		return nil, err
	}
	defer response.Body.Close()
	email, err := s.Parser.Parse(response)
	if err != nil {
		return nil, err
	}
	if email == "" {
		return nil, ErrEmptyUserEmail
	}
	_, err = auth.GetUserByEmail(email)
	if err != nil {
		if err != auth.ErrUserNotFound {
			return nil, err
		}
		registrationEnabled, _ := config.GetBool("auth:user-registration")
		if !registrationEnabled {
			return nil, err
		}
		user := &auth.User{Email: email}
		err = user.Create()
		if err != nil {
			return nil, err
		}
	}
	token := Token{*t, email}
	err = token.save()
	if err != nil {
		return nil, err
	}
	return &token, nil
}

func (s *OAuthScheme) AppLogin(appName string) (auth.Token, error) {
	nativeScheme := native.NativeScheme{}
	return nativeScheme.AppLogin(appName)
}

func (s *OAuthScheme) AppLogout(token string) error {
	nativeScheme := native.NativeScheme{}
	return nativeScheme.AppLogout(token)
}

func (s *OAuthScheme) Logout(token string) error {
	return deleteToken(token)
}

func (s *OAuthScheme) Auth(header string) (auth.Token, error) {
	token, err := getToken(header)
	if err != nil {
		nativeScheme := native.NativeScheme{}
		token, nativeErr := nativeScheme.Auth(header)
		if nativeErr == nil && token.IsAppToken() {
			return token, nil
		}
		return nil, err
	}
	config, err := s.loadConfig()
	if err != nil {
		return nil, err
	}
	client := config.Client(context.Background(), &token.Token)
	t0 := time.Now()
	rsp, err := client.Get(s.InfoUrl)
	requestLatencies.Observe(time.Since(t0).Seconds())
	if err != nil {
		requestErrors.Inc()
		return nil, err
	}
	defer rsp.Body.Close()
	return token, nil
}

func (s *OAuthScheme) Name() string {
	return "oauth"
}

func (s *OAuthScheme) Info() (auth.SchemeInfo, error) {
	config, err := s.loadConfig()
	if err != nil {
		return nil, err
	}
	config.RedirectURL = "__redirect_url__"
	return auth.SchemeInfo{"authorizeUrl": config.AuthCodeURL(""), "port": strconv.Itoa(s.CallbackPort)}, nil
}

func (s *OAuthScheme) Parse(infoResponse *http.Response) (string, error) {
	user := struct {
		Email string `json:"email"`
	}{}
	data, err := ioutil.ReadAll(infoResponse.Body)
	if err != nil {
		return "", errors.Wrap(err, "unable to read user data response")
	}
	if infoResponse.StatusCode != http.StatusOK {
		return "", errors.Errorf("unexpected user data response %d: %s", infoResponse.StatusCode, data)
	}
	err = json.Unmarshal(data, &user)
	if err != nil {
		return "", errors.Wrapf(err, "unable to parse user data: %s", data)
	}
	return user.Email, nil
}

func (s *OAuthScheme) Create(user *auth.User) (*auth.User, error) {
	user.Password = ""
	err := user.Create()
	if err != nil {
		return nil, err
	}
	return user, nil
}

func (s *OAuthScheme) Remove(u *auth.User) error {
	err := deleteAllTokens(u.Email)
	if err != nil {
		return err
	}
	return u.Delete()
}
<commit_msg>auth: typo oath -> oauth<commit_after>// Copyright 2014 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package oauth

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"

	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/auth"
	"github.com/tsuru/tsuru/auth/native"
	tsuruErrors "github.com/tsuru/tsuru/errors"
	"github.com/tsuru/tsuru/log"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)

var (
	ErrMissingCodeError       = &tsuruErrors.ValidationError{Message: "You must provide code to login"}
	ErrMissingCodeRedirectUrl = &tsuruErrors.ValidationError{Message: "You must provide the used redirect url to login"}
	ErrEmptyAccessToken       = &tsuruErrors.NotAuthorizedError{Message: "Couldn't convert code to access token."}
	ErrEmptyUserEmail         = &tsuruErrors.NotAuthorizedError{Message: "Couldn't parse user email."}

	requestLatencies = prometheus.NewHistogram(prometheus.HistogramOpts{
		Name: "tsuru_oauth_request_duration_seconds",
		Help: "The oauth requests latency distributions.",
	})
	requestErrors = prometheus.NewCounter(prometheus.CounterOpts{
		Name: "tsuru_oauth_request_errors_total",
		Help: "The total number of oauth request errors.",
	})
)

type OAuthParser interface {
	Parse(infoResponse *http.Response) (string, error)
}

type OAuthScheme struct {
	BaseConfig   oauth2.Config
	InfoUrl      string
	CallbackPort int
	Parser       OAuthParser
}

func init() {
	auth.RegisterScheme("oauth", &OAuthScheme{})
	prometheus.MustRegister(requestLatencies)
	prometheus.MustRegister(requestErrors)
}

// This method loads basic config and returns a copy of the
// config object.
func (s *OAuthScheme) loadConfig() (oauth2.Config, error) {
	if s.BaseConfig.ClientID != "" {
		return s.BaseConfig, nil
	}
	if s.Parser == nil {
		s.Parser = s
	}
	var emptyConfig oauth2.Config
	clientId, err := config.GetString("auth:oauth:client-id")
	if err != nil {
		return emptyConfig, err
	}
	clientSecret, err := config.GetString("auth:oauth:client-secret")
	if err != nil {
		return emptyConfig, err
	}
	scope, err := config.GetString("auth:oauth:scope")
	if err != nil {
		return emptyConfig, err
	}
	authURL, err := config.GetString("auth:oauth:auth-url")
	if err != nil {
		return emptyConfig, err
	}
	tokenURL, err := config.GetString("auth:oauth:token-url")
	if err != nil {
		return emptyConfig, err
	}
	infoURL, err := config.GetString("auth:oauth:info-url")
	if err != nil {
		return emptyConfig, err
	}
	callbackPort, err := config.GetInt("auth:oauth:callback-port")
	if err != nil {
		log.Debugf("auth:oauth:callback-port not found using random port: %s", err)
	}
	s.InfoUrl = infoURL
	s.CallbackPort = callbackPort
	s.BaseConfig = oauth2.Config{
		ClientID:     clientId,
		ClientSecret: clientSecret,
		Scopes:       []string{scope},
		Endpoint: oauth2.Endpoint{
			AuthURL:  authURL,
			TokenURL: tokenURL,
		},
	}
	return s.BaseConfig, nil
}

func (s *OAuthScheme) Login(params map[string]string) (auth.Token, error) {
	conf, err := s.loadConfig()
	if err != nil {
		return nil, err
	}
	code, ok := params["code"]
	if !ok {
		return nil, ErrMissingCodeError
	}
	redirectUrl, ok := params["redirectUrl"]
	if !ok {
		return nil, ErrMissingCodeRedirectUrl
	}
	conf.RedirectURL = redirectUrl
	oauthToken, err := conf.Exchange(context.Background(), code)
	if err != nil {
		return nil, err
	}
	return s.handleToken(oauthToken)
}

func (s *OAuthScheme) handleToken(t *oauth2.Token) (*Token, error) {
	if t.AccessToken == "" {
		return nil, ErrEmptyAccessToken
	}
	conf, err := s.loadConfig()
	if err != nil {
		return nil, err
	}
	client := conf.Client(context.Background(), t)
	t0 := time.Now()
	response, err := client.Get(s.InfoUrl)
	requestLatencies.Observe(time.Since(t0).Seconds())
	if err != nil {
		requestErrors.Inc()
		return nil, err
	}
	defer response.Body.Close()
	email, err := s.Parser.Parse(response)
	if err != nil {
		return nil, err
	}
	if email == "" {
		return nil, ErrEmptyUserEmail
	}
	_, err = auth.GetUserByEmail(email)
	if err != nil {
		if err != auth.ErrUserNotFound {
			return nil, err
		}
		registrationEnabled, _ := config.GetBool("auth:user-registration")
		if !registrationEnabled {
			return nil, err
		}
		user := &auth.User{Email: email}
		err = user.Create()
		if err != nil {
			return nil, err
		}
	}
	token := Token{*t, email}
	err = token.save()
	if err != nil {
		return nil, err
	}
	return &token, nil
}

func (s *OAuthScheme) AppLogin(appName string) (auth.Token, error) {
	nativeScheme := native.NativeScheme{}
	return nativeScheme.AppLogin(appName)
}

func (s *OAuthScheme) AppLogout(token string) error {
	nativeScheme := native.NativeScheme{}
	return nativeScheme.AppLogout(token)
}

func (s *OAuthScheme) Logout(token string) error {
	return deleteToken(token)
}

func (s *OAuthScheme) Auth(header string) (auth.Token, error) {
	token, err := getToken(header)
	if err != nil {
		nativeScheme := native.NativeScheme{}
		token, nativeErr := nativeScheme.Auth(header)
		if nativeErr == nil && token.IsAppToken() {
			return token, nil
		}
		return nil, err
	}
	config, err := s.loadConfig()
	if err != nil {
		return nil, err
	}
	client := config.Client(context.Background(), &token.Token)
	t0 := time.Now()
	rsp, err := client.Get(s.InfoUrl)
	requestLatencies.Observe(time.Since(t0).Seconds())
	if err != nil {
		requestErrors.Inc()
		return nil, err
	}
	defer rsp.Body.Close()
	return token, nil
}

func (s *OAuthScheme) Name() string {
	return "oauth"
}

func (s *OAuthScheme) Info() (auth.SchemeInfo, error) {
	config, err := s.loadConfig()
	if err != nil {
		return nil, err
	}
	config.RedirectURL = "__redirect_url__"
	return auth.SchemeInfo{"authorizeUrl": config.AuthCodeURL(""), "port": strconv.Itoa(s.CallbackPort)}, nil
}

func (s *OAuthScheme) Parse(infoResponse *http.Response) (string, error) {
	user := struct {
		Email string `json:"email"`
	}{}
	data, err := ioutil.ReadAll(infoResponse.Body)
	if err != nil {
		return "", errors.Wrap(err, "unable to read user data response")
	}
	if infoResponse.StatusCode != http.StatusOK {
		return "", errors.Errorf("unexpected user data response %d: %s", infoResponse.StatusCode, data)
	}
	err = json.Unmarshal(data, &user)
	if err != nil {
		return "", errors.Wrapf(err, "unable to parse user data: %s", data)
	}
	return user.Email, nil
}

func (s *OAuthScheme) Create(user *auth.User) (*auth.User, error) {
	user.Password = ""
	err := user.Create()
	if err != nil {
		return nil, err
	}
	return user, nil
}

func (s *OAuthScheme) Remove(u *auth.User) error {
	err := deleteAllTokens(u.Email)
	if err != nil {
		return err
	}
	return u.Delete()
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Skippbox, Ltd.

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 controller

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/skippbox/kubewatch/config"
	"github.com/skippbox/kubewatch/pkg/handlers"
	"github.com/skippbox/kubewatch/pkg/utils"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/apimachinery/pkg/util/wait"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	api_v1 "k8s.io/client-go/pkg/api/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
	"k8s.io/client-go/kubernetes"
)

const maxRetries = 5

// Controller object
type Controller struct {
	logger       *logrus.Entry
	clientset    kubernetes.Interface
	queue        workqueue.RateLimitingInterface
	informer     cache.SharedIndexInformer
	eventHandler handlers.Handler
}

func Start(conf *config.Config, eventHandler handlers.Handler) {
	kubeClient := utils.GetClientOutOfCluster()

	if conf.Resource.Pod {
		c := newControllerPod(kubeClient, eventHandler)
		stopCh := make(chan struct{})
		defer close(stopCh)

		go c.Run(stopCh)

		sigterm := make(chan os.Signal, 1)
		signal.Notify(sigterm, syscall.SIGTERM)
		signal.Notify(sigterm, syscall.SIGINT)
		<-sigterm
	}

	//if conf.Resource.Services {
	//	watchServices(kubeClient, eventHandler)
	//}
	//
	//if conf.Resource.ReplicationController {
	//	watchReplicationControllers(kubeClient, eventHandler)
	//}
	//
	//if conf.Resource.Deployment {
	//	watchDeployments(kubeExtensionsClient, eventHandler)
	//}
	//
	//if conf.Resource.Job {
	//	watchJobs(kubeExtensionsClient, eventHandler)
	//}
	//
	//if conf.Resource.PersistentVolume {
	//	var servicesStore cache.Store
	//	servicesStore = watchPersistenVolumes(kubeClient, servicesStore, eventHandler)
	//}

	//logrus.Fatal(http.ListenAndServe(":8081", nil))
}

func newControllerPod(client kubernetes.Interface, eventHandler handlers.Handler) *Controller {
	queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())

	informer := cache.NewSharedIndexInformer(
		&cache.ListWatch{
			ListFunc: func(options meta_v1.ListOptions) (runtime.Object, error) {
				return client.CoreV1().Pods(meta_v1.NamespaceAll).List(options)
			},
			WatchFunc: func(options meta_v1.ListOptions) (watch.Interface, error) {
				return client.CoreV1().Pods(meta_v1.NamespaceAll).Watch(options)
			},
		},
		&api_v1.Pod{},
		0, //Skip resync
		cache.Indexers{},
	)

	informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			key, err := cache.MetaNamespaceKeyFunc(obj)
			if err == nil {
				queue.Add(key)
			}
		},
		UpdateFunc: func(old, new interface{}) {
			key, err := cache.MetaNamespaceKeyFunc(new)
			if err == nil {
				queue.Add(key)
			}
		},
		DeleteFunc: func(obj interface{}) {
			key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
			if err == nil {
				queue.Add(key)
			}
		},
	})

	return &Controller{
		logger:       logrus.WithField("pkg", "kubewatch-pod"),
		clientset:    client,
		informer:     informer,
		queue:        queue,
		eventHandler: eventHandler,
	}
}

// Run starts the kubewatch controller
func (c *Controller) Run(stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	defer c.queue.ShutDown()

	c.logger.Info("Starting kubewatch controller")

	go c.informer.Run(stopCh)

	if !cache.WaitForCacheSync(stopCh, c.HasSynced) {
		utilruntime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
		return
	}

	c.logger.Info("Kubewatch controller synced and ready")

	wait.Until(c.runWorker, time.Second, stopCh)
}

// HasSynced is required for the cache.Controller interface.
func (c *Controller) HasSynced() bool {
	return c.informer.HasSynced()
}

// LastSyncResourceVersion is required for the cache.Controller interface.
func (c *Controller) LastSyncResourceVersion() string {
	return c.informer.LastSyncResourceVersion()
}

func (c *Controller) runWorker() {
	for c.processNextItem() {
		// continue looping
	}
}

func (c *Controller) processNextItem() bool {
	key, quit := c.queue.Get()
	if quit {
		return false
	}
	defer c.queue.Done(key)

	err := c.processItem(key.(string))
	if err == nil {
		// No error, reset the ratelimit counters
		c.queue.Forget(key)
	} else if c.queue.NumRequeues(key) < maxRetries {
		c.logger.Errorf("Error processing %s (will retry): %v", key, err)
		c.queue.AddRateLimited(key)
	} else {
		// err != nil and too many retries
		c.logger.Errorf("Error processing %s (giving up): %v", key, err)
		c.queue.Forget(key)
		utilruntime.HandleError(err)
	}

	return true
}

func (c *Controller) processItem(key string) error {
	c.logger.Infof("Processing change to Pod %s", key)

	obj, exists, err := c.informer.GetIndexer().GetByKey(key)
	if err != nil {
		return fmt.Errorf("Error fetching object with key %s from store: %v", key, err)
	}

	if !exists {
		c.eventHandler.ObjectDeleted(obj)
		return nil
	}

	c.eventHandler.ObjectCreated(obj)
	return nil
}

//
//func watchServices(client *client.Client, eventHandler handlers.Handler) cache.Store {
//	//Define what we want to look for (Services)
//	watchlist := cache.NewListWatchFromClient(client, "services", api.NamespaceAll, fields.Everything())
//
//	resyncPeriod := 30 * time.Minute
//
//	//Setup an informer to call functions when the watchlist changes
//	eStore, eController := framework.NewInformer(
//		watchlist,
//		&api.Service{},
//		resyncPeriod,
//		framework.ResourceEventHandlerFuncs{
//			AddFunc:    eventHandler.ObjectCreated,
//			DeleteFunc: eventHandler.ObjectDeleted,
//			UpdateFunc: eventHandler.ObjectUpdated,
//		},
//	)
//
//	//Run the controller as a goroutine
//	go eController.Run(wait.NeverStop)
//
//	return eStore
//}
//
//func watchReplicationControllers(client *client.Client, eventHandler handlers.Handler) cache.Store {
//	//Define what we want to look for (ReplicationControllers)
//	watchlist := cache.NewListWatchFromClient(client, "replicationcontrollers", api.NamespaceAll, fields.Everything())
//
//	resyncPeriod := 30 * time.Minute
//
//	//Setup an informer to call functions when the watchlist changes
//	eStore, eController := framework.NewInformer(
//		watchlist,
//		&api.ReplicationController{},
//		resyncPeriod,
//		framework.ResourceEventHandlerFuncs{
//			AddFunc:    eventHandler.ObjectCreated,
//			DeleteFunc: eventHandler.ObjectDeleted,
//		},
//	)
//
//	//Run the controller as a goroutine
//	go eController.Run(wait.NeverStop)
//
//	return eStore
//}
//
//func watchDeployments(client *client.ExtensionsClient, eventHandler handlers.Handler) cache.Store {
//	//Define what we want to look for (Deployments)
//	watchlist := cache.NewListWatchFromClient(client, "deployments", api.NamespaceAll, fields.Everything())
//
//	resyncPeriod := 30 * time.Minute
//
//	//Setup an informer to call functions when the watchlist changes
//	eStore, eController := framework.NewInformer(
//		watchlist,
//		&v1beta1.Deployment{},
//		resyncPeriod,
//		framework.ResourceEventHandlerFuncs{
//			AddFunc:    eventHandler.ObjectCreated,
//			DeleteFunc: eventHandler.ObjectDeleted,
//		},
//	)
//
//	//Run the controller as a goroutine
//	go eController.Run(wait.NeverStop)
//
//	return eStore
//}
//
//func watchJobs(client *client.ExtensionsClient, eventHandler handlers.Handler) cache.Store {
//	//Define what we want to look for (Jobs)
//	watchlist := cache.NewListWatchFromClient(client, "jobs", api.NamespaceAll, fields.Everything())
//
//	resyncPeriod := 30 * time.Minute
//
//	//Setup an informer to call functions when the watchlist changes
//	eStore, eController := framework.NewInformer(
//		watchlist,
//		&v1beta1.Job{},
//		resyncPeriod,
//		framework.ResourceEventHandlerFuncs{
//			AddFunc:    eventHandler.ObjectCreated,
//			DeleteFunc: eventHandler.ObjectDeleted,
//		},
//	)
//
//	//Run the controller as a goroutine
//	go eController.Run(wait.NeverStop)
//
//	return eStore
//}
//
//func watchPersistenVolumes(client *client.Client, store cache.Store, eventHandler handlers.Handler) cache.Store {
//	//Define what we want to look for (PersistenVolumes)
//	watchlist := cache.NewListWatchFromClient(client, "persistentvolumes", api.NamespaceAll, fields.Everything())
//
//	resyncPeriod := 30 * time.Minute
//
//	//Setup an informer to call functions when the watchlist changes
//	eStore, eController := framework.NewInformer(
//		watchlist,
//		&api.PersistentVolume{},
//		resyncPeriod,
//		framework.ResourceEventHandlerFuncs{
//			AddFunc:    eventHandler.ObjectCreated,
//			DeleteFunc: eventHandler.ObjectDeleted,
//		},
//	)
//
//	//Run the controller as a goroutine
//	go eController.Run(wait.NeverStop)
//
//	return eStore
//}
<commit_msg>Re-factoring to watch all needed object types<commit_after>/*
Copyright 2016 Skippbox, Ltd.

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 controller

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/skippbox/kubewatch/config"
	"github.com/skippbox/kubewatch/pkg/handlers"
	"github.com/skippbox/kubewatch/pkg/utils"

	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	api_v1 "k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/apps/v1beta1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
)

const maxRetries = 5

// Controller object
type Controller struct {
	logger       *logrus.Entry
	clientset    kubernetes.Interface
	queue        workqueue.RateLimitingInterface
	informer     cache.SharedIndexInformer
	eventHandler handlers.Handler
}

// QueueMessage - type of stuff stored on event queue
type QueueMessage struct {
	oldObject interface{}
	newObject interface{}
}

func Start(conf *config.Config, eventHandler handlers.Handler) {
	kubeClient := utils.GetClientOutOfCluster()

	stopCh := make(chan struct{})
	defer close(stopCh)

	if conf.Resource.Pod {
		c := newControllerPod(kubeClient, eventHandler)
		go c.Run(stopCh)
	}

	if conf.Resource.Services {
		c := newControllerServices(kubeClient, eventHandler)
		go c.Run(stopCh)
	}

	if conf.Resource.ReplicationController {
		c := newControllerRC(kubeClient, eventHandler)
		go c.Run(stopCh)
	}

	if conf.Resource.Deployment {
		c := newControllerDeployment(kubeClient, eventHandler)
		go c.Run(stopCh)
	}

	sigterm := make(chan os.Signal, 1)
	signal.Notify(sigterm, syscall.SIGTERM)
	signal.Notify(sigterm, syscall.SIGINT)
	<-sigterm

	//if conf.Resource.Deployment {
	//	watchDeployments(kubeExtensionsClient, eventHandler)
	//}
	//
	//if conf.Resource.Job {
	//	watchJobs(kubeExtensionsClient, eventHandler)
	//}
	//
	//if conf.Resource.PersistentVolume {
	//	var servicesStore cache.Store
	//	servicesStore = watchPersistenVolumes(kubeClient, servicesStore, eventHandler)
	//}

}

func newControllerPod(client kubernetes.Interface, eventHandler handlers.Handler) *Controller {
	listFunc := func(options meta_v1.ListOptions) (runtime.Object, error) {
		return client.CoreV1().Pods(meta_v1.NamespaceAll).List(options)
	}
	watchFunc := func(options meta_v1.ListOptions) (watch.Interface, error) {
		return client.CoreV1().Pods(meta_v1.NamespaceAll).Watch(options)
	}
	return newControllerGeneric(client, eventHandler, listFunc, watchFunc, &api_v1.Pod{})
}

func newControllerServices(client kubernetes.Interface, eventHandler handlers.Handler) *Controller {
	listFunc := func(options meta_v1.ListOptions) (runtime.Object, error) {
		return client.CoreV1().Services(meta_v1.NamespaceAll).List(options)
	}
	watchFunc := func(options meta_v1.ListOptions) (watch.Interface, error) {
		return client.CoreV1().Services(meta_v1.NamespaceAll).Watch(options)
	}
	return newControllerGeneric(client, eventHandler, listFunc, watchFunc, &api_v1.Service{})
}

func newControllerRC(client kubernetes.Interface, eventHandler handlers.Handler) *Controller {
	listFunc := func(options meta_v1.ListOptions) (runtime.Object, error) {
		return client.CoreV1().ReplicationControllers(meta_v1.NamespaceAll).List(options)
	}
	watchFunc := func(options meta_v1.ListOptions) (watch.Interface, error) {
		return client.CoreV1().ReplicationControllers(meta_v1.NamespaceAll).Watch(options)
	}
	return newControllerGeneric(client, eventHandler, listFunc, watchFunc, &api_v1.ReplicationController{})
}

func newControllerDeployment(client kubernetes.Interface, eventHandler handlers.Handler) *Controller {
	listFunc := func(options meta_v1.ListOptions) (runtime.Object, error) {
		return client.AppsV1beta1().Deployments(meta_v1.NamespaceAll).List(options)
	}
	watchFunc := func(options meta_v1.ListOptions) (watch.Interface, error) {
		return client.AppsV1beta1().Deployments(meta_v1.NamespaceAll).Watch(options)
	}
	return newControllerGeneric(client, eventHandler, listFunc, watchFunc, &v1beta1.Deployment{})
}

func newControllerGeneric(client kubernetes.Interface, eventHandler handlers.Handler, listFunc cache.ListFunc, watchFunc cache.WatchFunc, objType runtime.Object) *Controller {
	queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())

	informer := cache.NewSharedIndexInformer(
		&cache.ListWatch{
			ListFunc:  listFunc,
			WatchFunc: watchFunc,
		},
		objType,
		0, //Skip resync
		cache.Indexers{},
	)

	informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			queue.Add(&QueueMessage{
				newObject: obj,
			})
		},
		UpdateFunc: func(old, new interface{}) {
			queue.Add(&QueueMessage{
				newObject: new,
				oldObject: old,
			})
		},
		DeleteFunc: func(obj interface{}) {
			queue.Add(&QueueMessage{
				oldObject: obj,
			})
		},
	})

	return &Controller{
		logger:       logrus.WithField("pkg", "kubewatch-controller"),
		clientset:    client,
		informer:     informer,
		queue:        queue,
		eventHandler: eventHandler,
	}
}

// Run starts the kubewatch controller
func (c *Controller) Run(stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	defer c.queue.ShutDown()

	c.logger.Info("Starting kubewatch controller")

	go c.informer.Run(stopCh)

	if !cache.WaitForCacheSync(stopCh, c.HasSynced) {
		utilruntime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
		return
	}

	c.logger.Info("Kubewatch controller synced and ready")

	wait.Until(c.runWorker, time.Second, stopCh)
}

// HasSynced is required for the cache.Controller interface.
func (c *Controller) HasSynced() bool {
	return c.informer.HasSynced()
}

// LastSyncResourceVersion is required for the cache.Controller interface.
func (c *Controller) LastSyncResourceVersion() string {
	return c.informer.LastSyncResourceVersion()
}

func (c *Controller) runWorker() {
	for c.processNextItem() {
		// continue looping
	}
}

func (c *Controller) processNextItem() bool {
	key, quit := c.queue.Get()
	if quit {
		return false
	}
	defer c.queue.Done(key)

	err := c.processItem(key.(*QueueMessage))
	if err == nil {
		// No error, reset the ratelimit counters
		c.queue.Forget(key)
	} else if c.queue.NumRequeues(key) < maxRetries {
		c.logger.Errorf("Error processing %s (will retry): %v", key, err)
		c.queue.AddRateLimited(key)
	} else {
		// err != nil and too many retries
		c.logger.Errorf("Error processing %s (giving up): %v", key, err)
		c.queue.Forget(key)
		utilruntime.HandleError(err)
	}

	return true
}

func (c *Controller) processItem(msg *QueueMessage) error {
	c.logger.Infof("Processing change")

	if msg.oldObject == nil {
		c.eventHandler.ObjectCreated(msg.newObject)
	} else if msg.newObject == nil {
		c.eventHandler.ObjectDeleted(msg.oldObject)
	} else {
		c.eventHandler.ObjectUpdated(msg.oldObject, msg.newObject)
	}

	return nil
}

//
//func watchDeployments(client *client.ExtensionsClient, eventHandler handlers.Handler) cache.Store {
//	//Define what we want to look for (Deployments)
//	watchlist := cache.NewListWatchFromClient(client, "deployments", api.NamespaceAll, fields.Everything())
//
//	resyncPeriod := 30 * time.Minute
//
//	//Setup an informer to call functions when the watchlist changes
//	eStore, eController := framework.NewInformer(
//		watchlist,
//		&v1beta1.Deployment{},
//		resyncPeriod,
//		framework.ResourceEventHandlerFuncs{
//			AddFunc:    eventHandler.ObjectCreated,
//			DeleteFunc: eventHandler.ObjectDeleted,
//		},
//	)
//
//	//Run the controller as a goroutine
//	go eController.Run(wait.NeverStop)
//
//	return eStore
//}
//
//func watchJobs(client *client.ExtensionsClient, eventHandler handlers.Handler) cache.Store {
//	//Define what we want to look for (Jobs)
//	watchlist := cache.NewListWatchFromClient(client, "jobs", api.NamespaceAll, fields.Everything())
//
//	resyncPeriod := 30 * time.Minute
//
//	//Setup an informer to call functions when the watchlist changes
//	eStore, eController := framework.NewInformer(
//		watchlist,
//		&v1beta1.Job{},
//		resyncPeriod,
//		framework.ResourceEventHandlerFuncs{
//			AddFunc:    eventHandler.ObjectCreated,
//			DeleteFunc: eventHandler.ObjectDeleted,
//		},
//	)
//
//	//Run the controller as a goroutine
//	go eController.Run(wait.NeverStop)
//
//	return eStore
//}
//
//func watchPersistenVolumes(client *client.Client, store cache.Store, eventHandler handlers.Handler) cache.Store {
//	//Define what we want to look for (PersistenVolumes)
//	watchlist := cache.NewListWatchFromClient(client, "persistentvolumes", api.NamespaceAll, fields.Everything())
//
//	resyncPeriod := 30 * time.Minute
//
//	//Setup an informer to call functions when the watchlist changes
//	eStore, eController := framework.NewInformer(
//		watchlist,
//		&api.PersistentVolume{},
//		resyncPeriod,
//		framework.ResourceEventHandlerFuncs{
//			AddFunc:    eventHandler.ObjectCreated,
//			DeleteFunc: eventHandler.ObjectDeleted,
//		},
//	)
//
//	//Run the controller as a goroutine
//	go eController.Run(wait.NeverStop)
//
//	return eStore
//}
<|endoftext|>


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

package cloud_test

import (
	"cloud.google.com/go/datastore"
	"golang.org/x/net/context"
	"google.golang.org/api/option"
)

func Example_applicationDefaultCredentials() {
	// Google Application Default Credentials is the recommended way to authorize
	// and authenticate clients.
	//
	// See the following link on how to create and obtain Application Default Credentials:
	// https://developers.google.com/identity/protocols/application-default-credentials.
	client, err := datastore.NewClient(context.Background(), "project-id")
	if err != nil {
		// TODO: handle error.
	}
	_ = client // Use the client.
}

func Example_serviceAccountFile() {
	// Use a JSON key file associated with a Google service account to
	// authenticate and authorize. Service Account keys can be created and
	// downloaded from https://console.developers.google.com/permissions/serviceaccounts.
	//
	// Note: This example uses the datastore client, but the same steps apply to
	// the other client libraries underneath this package.
	client, err := datastore.NewClient(context.Background(),
		"project-id", option.WithServiceAccountFile("/path/to/service-account-key.json"))
	if err != nil {
		// TODO: handle error.
	}
	_ = client // Use the client.
}
<commit_msg>examples: add an example for WithCredentials<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cloud_test

import (
	"cloud.google.com/go/datastore"
	"cloud.google.com/go/pubsub"
	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/option"
)

// Google Application Default Credentials is the recommended way to authorize
// and authenticate clients.
//
// For information on how to create and obtain Application Default Credentials, see
// https://developers.google.com/identity/protocols/application-default-credentials.
func Example_applicationDefaultCredentials() {
	client, err := datastore.NewClient(context.Background(), "project-id")
	if err != nil {
		// TODO: handle error.
	}
	_ = client // Use the client.
}

// You can use a file with credentials to authenticate and authorize, such as a JSON
// key file associated with a Google service account. Service Account keys can be
// created and downloaded from
// https://console.developers.google.com/permissions/serviceaccounts.
//
// This example uses the Datastore client, but the same steps apply to
// the other client libraries underneath this package.
func Example_credentialsFile() {
	client, err := datastore.NewClient(context.Background(),
		"project-id", option.WithCredentialsFile("/path/to/service-account-key.json"))
	if err != nil {
		// TODO: handle error.
	}
	_ = client // Use the client.
}

// In some cases (for instance, you don't want to store secrets on disk), you can
// create credentials from in-memory JSON and use the WithCredentials option.
//
// The google package in this example is at golang.org/x/oauth2/google.
//
// This example uses the PubSub client, but the same steps apply to
// the other client libraries underneath this package.
func Example_credentialsFromJSON() {
	ctx := context.Background()
	creds, err := google.CredentialsFromJSON(ctx, []byte("JSON creds"), pubsub.ScopePubSub)
	if err != nil {
		// TODO: handle error.
	}
	client, err := pubsub.NewClient(ctx, "project-id", option.WithCredentials(creds))
	if err != nil {
		// TODO: handle error.
	}
	_ = client // Use the client.
}
<|endoftext|>


<commit_before>package authkit

import (
	"html/template"
	"strings"
)

var funcMap = template.FuncMap{
	"join": strings.Join,
}

var loginTemplate = template.Must(template.New("login").Funcs(funcMap).Parse(loginJS))
var loginJS = `
var __authkit__state__ = {
  authCallback : "__authkit__"+parseInt(Math.random()*1e12,10).toString(36),
  providers : {}
};

{{ $base := .Base }}
{{ range $key, $value := .Providers }}
__authkit__state__.providers["{{ $value.Network }}"] = {
  network : "{{ $value.Network }}",
  authurl : "{{ $value.AuthURL }}",
  client_id : "{{ $value.ClientID }}",
  scopes : "{{ join $value.Scopes " " }}",
  redirect: "{{ $base }}redirect"
};
{{ end }}

var authkit = {
  providers : function () {
    return __authkit__state__.providers;
  },
  provider : function (p) {
    return __authkit__state__.providers[p];
  },
  login : function (provider,base) {
    var p = authkit.provider(provider);
    if (!p) return;
    if (!base) base = window.location.origin;
    var scopes = encodeURIComponent(p.scopes);
    var redir = encodeURIComponent(base+p.redirect);
    var state = encodeURIComponent(JSON.stringify({network:p.network,redirect_uri:redir,cbid:__authkit__state__.authCallback}));
    var authU = p.authurl+"?redirect_uri="+redir+"&response_type=code&client_id="+p.client_id+"&state="+state;
    if (p.scopes) {
      authU = authU + "&scope="+scopes;
    }
    var self = this;
    self.user = function (cb) { this.authCB = cb; };
		self.cbid = __authkit__state__.authCallback;
    popup(authU, 500, 600, this);
    return self;
  }
};

function popup (u, w, h, target) {
  var documentElement = document.documentElement;

	// Multi Screen Popup Positioning (http://stackoverflow.com/a/16861050)
	//   Credit: http://www.xtf.dk/2011/08/center-new-popup-window-even-on.html
	// Fixes dual-screen position                         Most browsers      Firefox
	var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
	var dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;

	var width = window.innerWidth || documentElement.clientWidth || screen.width;
	var height = window.innerHeight || documentElement.clientHeight || screen.height;

	var left = ((width - w) / 2) + dualScreenLeft;
	var top  = ((height - h) / 2) + dualScreenTop;
	var feat = "resizeable=true,height=" + h + ",width=" + w + ",left=" + left + ",top="  + top
	win = window.open(u, "_blank", feat);
	// use polling because of an ugly IOS/chrome bug where window.opener is null
	poll = setInterval(function() {
      if (win && win.closed) {
          clearInterval(poll);
          returnOauth(target);
      }
  } , 300);
}

function returnOauth (target) {
	var cbid = target.cbid;
	var token = localStorage.getItem(cbid+"-token");
	var usr = JSON.parse(localStorage.getItem(cbid+"-user"));

	localStorage.removeItem(cbid+"-token");
	localStorage.removeItem(cbid+"-user");

	if (target.authCB) {
		target.authCB(usr, token);
	}
}
`
var redirectTemplate = template.Must(template.New("redirect").Funcs(funcMap).Parse(redirect))
var redirect = `
<html>
<body>
  <script>
	  // First, parse the query string
    var params = {}, queryString = location.search.substring(1),
        regex = /([^&=]+)=([^&]*)/g, m;
    while (m = regex.exec(queryString)) {
      params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
    }

    var req = new XMLHttpRequest();
    var cbid = JSON.parse(params.state).cbid;
    req.open('GET', '{{ .Base }}auth?code='+params.code+"&state="+params.state, true);

    req.onreadystatechange = function (e) {
      if (req.readyState == 4) {
        if(req.status == 200){
           var tok = req.getResponseHeader("Authorization");
           var usr = req.responseText;
					 localStorage.setItem(cbid+"-token", tok);
					 localStorage.setItem(cbid+"-user", usr);
           window.close();
        }
        else if(req.status == 400) {
            console.log('There was an error processing the access code:',req.responseText)
            window.close();
        }
        else {
          console.log('something other than 200 was returned:',req.responseText)
          window.close();
        }
      }
    };
    req.send(null);
	</script>
</body>
</html>
`
<commit_msg>transport error<commit_after>package authkit

import (
	"html/template"
	"strings"
)

var funcMap = template.FuncMap{
	"join": strings.Join,
}

var loginTemplate = template.Must(template.New("login").Funcs(funcMap).Parse(loginJS))
var loginJS = `
var __authkit__state__ = {
  authCallback : "__authkit__"+parseInt(Math.random()*1e12,10).toString(36),
  providers : {}
};

{{ $base := .Base }}
{{ range $key, $value := .Providers }}
__authkit__state__.providers["{{ $value.Network }}"] = {
  network : "{{ $value.Network }}",
  authurl : "{{ $value.AuthURL }}",
  client_id : "{{ $value.ClientID }}",
  scopes : "{{ join $value.Scopes " " }}",
  redirect: "{{ $base }}redirect"
};
{{ end }}

var authkit = {
  providers : function () {
    return __authkit__state__.providers;
  },
  provider : function (p) {
    return __authkit__state__.providers[p];
  },
  login : function (provider,base) {
    var p = authkit.provider(provider);
    if (!p) return;
    if (!base) base = window.location.origin;
    var scopes = encodeURIComponent(p.scopes);
    var redir = encodeURIComponent(base+p.redirect);
    var state = encodeURIComponent(JSON.stringify({network:p.network,redirect_uri:redir,cbid:__authkit__state__.authCallback}));
    var authU = p.authurl+"?redirect_uri="+redir+"&response_type=code&client_id="+p.client_id+"&state="+state;
    if (p.scopes) {
      authU = authU + "&scope="+scopes;
    }
    var self = this;
    self.user = function (cb) { this.authCB = cb; };
		self.cbid = __authkit__state__.authCallback;
    popup(authU, 500, 600, this);
    return self;
  }
};

function popup (u, w, h, target) {
  var documentElement = document.documentElement;

	// Multi Screen Popup Positioning (http://stackoverflow.com/a/16861050)
	//   Credit: http://www.xtf.dk/2011/08/center-new-popup-window-even-on.html
	// Fixes dual-screen position                         Most browsers      Firefox
	var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
	var dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;

	var width = window.innerWidth || documentElement.clientWidth || screen.width;
	var height = window.innerHeight || documentElement.clientHeight || screen.height;

	var left = ((width - w) / 2) + dualScreenLeft;
	var top  = ((height - h) / 2) + dualScreenTop;
	var feat = "resizeable=true,height=" + h + ",width=" + w + ",left=" + left + ",top="  + top
	win = window.open(u, "_blank", feat);
	// use polling because of an ugly IOS/chrome bug where window.opener is null
	poll = setInterval(function() {
      if (win && win.closed) {
          clearInterval(poll);
          returnOauth(target);
      }
  } , 300);
}

function returnOauth (target) {
	var cbid = target.cbid;
	var token = localStorage.getItem(cbid+"-token");
	var usr = JSON.parse(localStorage.getItem(cbid+"-user"));
	var err = localStorage.getItem(cbid+"-error");

	localStorage.removeItem(cbid+"-token");
	localStorage.removeItem(cbid+"-user");
	localStorage.removeItem(cbid+"-error");

	if (token && usr) {
		if (target.authCB) {
			target.authCB(usr, token);
		}
	} else if (err) {
		if (target.authCB) {
			target.authCB(null, null, err);
		}
	}
}
`
var redirectTemplate = template.Must(template.New("redirect").Funcs(funcMap).Parse(redirect))
var redirect = `
<html>
<body>
  <script>
	  // First, parse the query string
    var params = {}, queryString = location.search.substring(1),
        regex = /([^&=]+)=([^&]*)/g, m;
    while (m = regex.exec(queryString)) {
      params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
    }

    var req = new XMLHttpRequest();
    var cbid = JSON.parse(params.state).cbid;
    req.open('GET', '{{ .Base }}auth?code='+params.code+"&state="+params.state, true);
		console.log("code: ", params.code, "state: ", params.state);

    req.onreadystatechange = function (e) {
      if (req.readyState == 4) {
        if(req.status == 200){
           var tok = req.getResponseHeader("Authorization");
           var usr = req.responseText;
					 localStorage.setItem(cbid+"-token", tok);
					 localStorage.setItem(cbid+"-user", usr);
           window.close();
        }
        else if(req.status == 400) {
            console.log('There was an error processing the access code:',req.responseText)
						localStorage.setItem(cbid+"-error", req.responseText);
            window.close();
        }
        else {
          console.log('something other than 200 was returned:',req.responseText)
					localStorage.setItem(cbid+"-error", req.responseText);
          window.close();
        }
      }
    };
    req.send(null);
	</script>
</body>
</html>
`
<|endoftext|>