<commit_before>package main

import (
	"database/sql"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/version"
)

// XXX: backward compatible declarations, introduced when the db code was
//      extracted to its own package. We should eventually clean this up.
const (
	storagePoolVolumeTypeContainer = db.StoragePoolVolumeTypeContainer
	storagePoolVolumeTypeImage     = db.StoragePoolVolumeTypeImage
	storagePoolVolumeTypeCustom    = db.StoragePoolVolumeTypeCustom
)

const (
	storagePoolVolumeTypeNameContainer = db.StoragePoolVolumeTypeNameContainer
	storagePoolVolumeTypeNameImage     = db.StoragePoolVolumeTypeNameImage
	storagePoolVolumeTypeNameCustom    = db.StoragePoolVolumeTypeNameCustom
)

// Leave the string type in here! This guarantees that go treats this is as a
// typed string constant. Removing it causes go to treat these as untyped string
// constants which is not what we want.
const (
	storagePoolVolumeAPIEndpointContainers string = "containers"
	storagePoolVolumeAPIEndpointImages     string = "images"
	storagePoolVolumeAPIEndpointCustom     string = "custom"
)

var supportedVolumeTypes = []int{storagePoolVolumeTypeContainer, storagePoolVolumeTypeImage, storagePoolVolumeTypeCustom}

func storagePoolVolumeTypeNameToType(volumeTypeName string) (int, error) {
	switch volumeTypeName {
	case storagePoolVolumeTypeNameContainer:
		return storagePoolVolumeTypeContainer, nil
	case storagePoolVolumeTypeNameImage:
		return storagePoolVolumeTypeImage, nil
	case storagePoolVolumeTypeNameCustom:
		return storagePoolVolumeTypeCustom, nil
	}

	return -1, fmt.Errorf("invalid storage volume type name")
}

func storagePoolVolumeTypeNameToAPIEndpoint(volumeTypeName string) (string, error) {
	switch volumeTypeName {
	case storagePoolVolumeTypeNameContainer:
		return storagePoolVolumeAPIEndpointContainers, nil
	case storagePoolVolumeTypeNameImage:
		return storagePoolVolumeAPIEndpointImages, nil
	case storagePoolVolumeTypeNameCustom:
		return storagePoolVolumeAPIEndpointCustom, nil
	}

	return "", fmt.Errorf("invalid storage volume type name")
}

func storagePoolVolumeTypeToName(volumeType int) (string, error) {
	return db.StoragePoolVolumeTypeToName(volumeType)
}

func storagePoolVolumeTypeToAPIEndpoint(volumeType int) (string, error) {
	switch volumeType {
	case storagePoolVolumeTypeContainer:
		return storagePoolVolumeAPIEndpointContainers, nil
	case storagePoolVolumeTypeImage:
		return storagePoolVolumeAPIEndpointImages, nil
	case storagePoolVolumeTypeCustom:
		return storagePoolVolumeAPIEndpointCustom, nil
	}

	return "", fmt.Errorf("invalid storage volume type")
}

func storagePoolVolumeUpdate(state *state.State, poolName string, volumeName string, volumeType int, newDescription string, newConfig map[string]string) error {
	s, err := storagePoolVolumeInit(state, poolName, volumeName, volumeType)
	if err != nil {
		return err
	}

	oldWritable := s.GetStoragePoolVolumeWritable()
	newWritable := oldWritable

	// Backup the current state
	oldDescription := oldWritable.Description
	oldConfig := map[string]string{}
	err = shared.DeepCopy(&oldWritable.Config, &oldConfig)
	if err != nil {
		return err
	}

	// Define a function which reverts everything.  Defer this function
	// so that it doesn't need to be explicitly called in every failing
	// return path. Track whether or not we want to undo the changes
	// using a closure.
	undoChanges := true
	defer func() {
		if undoChanges {
			s.SetStoragePoolVolumeWritable(&oldWritable)
		}
	}()

	// Diff the configurations
	changedConfig := []string{}
	userOnly := true
	for key := range oldConfig {
		if oldConfig[key] != newConfig[key] {
			if !strings.HasPrefix(key, "user.") {
				userOnly = false
			}

			if !shared.StringInSlice(key, changedConfig) {
				changedConfig = append(changedConfig, key)
			}
		}
	}

	for key := range newConfig {
		if oldConfig[key] != newConfig[key] {
			if !strings.HasPrefix(key, "user.") {
				userOnly = false
			}

			if !shared.StringInSlice(key, changedConfig) {
				changedConfig = append(changedConfig, key)
			}
		}
	}

	// Apply config changes if there are any
	if len(changedConfig) != 0 {
		newWritable.Description = newDescription
		newWritable.Config = newConfig

		// Update the storage pool
		if !userOnly {
			err = s.StoragePoolVolumeUpdate(&newWritable, changedConfig)
			if err != nil {
				return err
			}
		}

		// Apply the new configuration
		s.SetStoragePoolVolumeWritable(&newWritable)
	}

	poolID, err := db.StoragePoolGetID(state.DB, poolName)
	if err != nil {
		return err
	}

	// Update the database if something changed
	if len(changedConfig) != 0 || newDescription != oldDescription {
		err = db.StoragePoolVolumeUpdate(state.DB, volumeName, volumeType, poolID, newDescription, newConfig)
		if err != nil {
			return err
		}
	}

	// Success, update the closure to mark that the changes should be kept.
	undoChanges = false

	return nil
}

func storagePoolVolumeUsedByContainersGet(s *state.State, volumeName string,
	volumeTypeName string) ([]string, error) {
	cts, err := db.ContainersList(s.DB, db.CTypeRegular)
	if err != nil {
		return []string{}, err
	}

	ctsUsingVolume := []string{}
	volumeNameWithType := fmt.Sprintf("%s/%s", volumeTypeName, volumeName)
	for _, ct := range cts {
		c, err := containerLoadByName(s, ct)
		if err != nil {
			continue
		}

		for _, dev := range c.LocalDevices() {
			if dev["type"] != "disk" {
				continue
			}

			// Make sure that we don't compare against stuff like
			// "container////bla" but only against "container/bla".
			cleanSource := filepath.Clean(dev["source"])
			if cleanSource == volumeName || cleanSource == volumeNameWithType {
				ctsUsingVolume = append(ctsUsingVolume, ct)
			}
		}
	}

	return ctsUsingVolume, nil
}

// volumeUsedBy = append(volumeUsedBy, fmt.Sprintf("/%s/containers/%s", version.APIVersion, ct))
func storagePoolVolumeUsedByGet(s *state.State, volumeName string, volumeTypeName string) ([]string, error) {
	// Handle container volumes
	if volumeTypeName == "container" {
		cName, sName, snap := containerGetParentAndSnapshotName(volumeName)

		if snap {
			return []string{fmt.Sprintf("/%s/containers/%s/snapshots/%s", version.APIVersion, cName, sName)}, nil
		}

		return []string{fmt.Sprintf("/%s/containers/%s", version.APIVersion, cName)}, nil
	}

	// Handle image volumes
	if volumeTypeName == "image" {
		return []string{fmt.Sprintf("/%s/images/%s", version.APIVersion, volumeName)}, nil
	}

	// Look for containers using this volume
	ctsUsingVolume, err := storagePoolVolumeUsedByContainersGet(s,
		volumeName, volumeTypeName)
	if err != nil {
		return []string{}, err
	}

	volumeUsedBy := []string{}
	for _, ct := range ctsUsingVolume {
		volumeUsedBy = append(volumeUsedBy,
			fmt.Sprintf("/%s/containers/%s", version.APIVersion, ct))
	}

	profiles, err := profilesUsingPoolVolumeGetNames(s.DB, volumeName, volumeTypeName)
	if err != nil {
		return []string{}, err
	}

	if len(volumeUsedBy) == 0 && len(profiles) == 0 {
		return []string{}, err
	}

	for _, pName := range profiles {
		volumeUsedBy = append(volumeUsedBy, fmt.Sprintf("/%s/profiles/%s", version.APIVersion, pName))
	}

	return volumeUsedBy, nil
}

func profilesUsingPoolVolumeGetNames(dbObj *sql.DB, volumeName string, volumeType string) ([]string, error) {
	usedBy := []string{}

	profiles, err := db.Profiles(dbObj)
	if err != nil {
		return usedBy, err
	}

	for _, pName := range profiles {
		_, profile, err := db.ProfileGet(dbObj, pName)
		if err != nil {
			return usedBy, err
		}

		volumeNameWithType := fmt.Sprintf("%s/%s", volumeType, volumeName)
		for _, v := range profile.Devices {
			if v["type"] != "disk" {
				continue
			}

			// Can't be a storage volume.
			if filepath.IsAbs(v["source"]) {
				continue
			}

			// Make sure that we don't compare against stuff
			// like "container////bla" but only against
			// "container/bla".
			cleanSource := filepath.Clean(v["source"])
			if cleanSource == volumeName || cleanSource == volumeNameWithType {
				usedBy = append(usedBy, pName)
			}
		}
	}

	return usedBy, nil
}

func storagePoolVolumeDBCreate(s *state.State, poolName string, volumeName, volumeDescription string, volumeTypeName string, volumeConfig map[string]string) error {
	// Check that the name of the new storage volume is valid. (For example.
	// zfs pools cannot contain "/" in their names.)
	err := storageValidName(volumeName)
	if err != nil {
		return err
	}

	// Convert the volume type name to our internal integer representation.
	volumeType, err := storagePoolVolumeTypeNameToType(volumeTypeName)
	if err != nil {
		return err
	}

	// Load storage pool the volume will be attached to.
	poolID, poolStruct, err := db.StoragePoolGet(s.DB, poolName)
	if err != nil {
		return err
	}

	// Check that a storage volume of the same storage volume type does not
	// already exist.
	volumeID, _ := db.StoragePoolVolumeGetTypeID(s.DB, volumeName, volumeType, poolID)
	if volumeID > 0 {
		return fmt.Errorf("a storage volume of type %s does already exist", volumeTypeName)
	}

	// Make sure that we don't pass a nil to the next function.
	if volumeConfig == nil {
		volumeConfig = map[string]string{}
	}

	// Validate the requested storage volume configuration.
	err = storageVolumeValidateConfig(poolName, volumeConfig, poolStruct)
	if err != nil {
		return err
	}

	err = storageVolumeFillDefault(poolName, volumeConfig, poolStruct)
	if err != nil {
		return err
	}

	// Create the database entry for the storage volume.
	_, err = db.StoragePoolVolumeCreate(s.DB, volumeName, volumeDescription, volumeType, poolID, volumeConfig)
	if err != nil {
		return fmt.Errorf("Error inserting %s of type %s into database: %s", poolName, volumeTypeName, err)
	}

	return nil
}

func storagePoolVolumeCreateInternal(state *state.State, poolName string, volumeName, volumeDescription string, volumeTypeName string, volumeConfig map[string]string) error {
	err := storagePoolVolumeDBCreate(state, poolName, volumeName, volumeDescription, volumeTypeName, volumeConfig)
	if err != nil {
		return err
	}

	// Convert the volume type name to our internal integer representation.
	volumeType, err := storagePoolVolumeTypeNameToType(volumeTypeName)
	if err != nil {
		return err
	}

	s, err := storagePoolVolumeInit(state, poolName, volumeName, volumeType)
	if err != nil {
		return err
	}

	poolID, _, _ := s.GetContainerPoolInfo()

	// Create storage volume.
	err = s.StoragePoolVolumeCreate()
	if err != nil {
		db.StoragePoolVolumeDelete(state.DB, volumeName, volumeType, poolID)
		return err
	}

	return nil
}
<commit_msg>storage: have "usedby" functions return empty slice<commit_after>package main

import (
	"database/sql"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/version"
)

// XXX: backward compatible declarations, introduced when the db code was
//      extracted to its own package. We should eventually clean this up.
const (
	storagePoolVolumeTypeContainer = db.StoragePoolVolumeTypeContainer
	storagePoolVolumeTypeImage     = db.StoragePoolVolumeTypeImage
	storagePoolVolumeTypeCustom    = db.StoragePoolVolumeTypeCustom
)

const (
	storagePoolVolumeTypeNameContainer = db.StoragePoolVolumeTypeNameContainer
	storagePoolVolumeTypeNameImage     = db.StoragePoolVolumeTypeNameImage
	storagePoolVolumeTypeNameCustom    = db.StoragePoolVolumeTypeNameCustom
)

// Leave the string type in here! This guarantees that go treats this is as a
// typed string constant. Removing it causes go to treat these as untyped string
// constants which is not what we want.
const (
	storagePoolVolumeAPIEndpointContainers string = "containers"
	storagePoolVolumeAPIEndpointImages     string = "images"
	storagePoolVolumeAPIEndpointCustom     string = "custom"
)

var supportedVolumeTypes = []int{storagePoolVolumeTypeContainer, storagePoolVolumeTypeImage, storagePoolVolumeTypeCustom}

func storagePoolVolumeTypeNameToType(volumeTypeName string) (int, error) {
	switch volumeTypeName {
	case storagePoolVolumeTypeNameContainer:
		return storagePoolVolumeTypeContainer, nil
	case storagePoolVolumeTypeNameImage:
		return storagePoolVolumeTypeImage, nil
	case storagePoolVolumeTypeNameCustom:
		return storagePoolVolumeTypeCustom, nil
	}

	return -1, fmt.Errorf("invalid storage volume type name")
}

func storagePoolVolumeTypeNameToAPIEndpoint(volumeTypeName string) (string, error) {
	switch volumeTypeName {
	case storagePoolVolumeTypeNameContainer:
		return storagePoolVolumeAPIEndpointContainers, nil
	case storagePoolVolumeTypeNameImage:
		return storagePoolVolumeAPIEndpointImages, nil
	case storagePoolVolumeTypeNameCustom:
		return storagePoolVolumeAPIEndpointCustom, nil
	}

	return "", fmt.Errorf("invalid storage volume type name")
}

func storagePoolVolumeTypeToName(volumeType int) (string, error) {
	return db.StoragePoolVolumeTypeToName(volumeType)
}

func storagePoolVolumeTypeToAPIEndpoint(volumeType int) (string, error) {
	switch volumeType {
	case storagePoolVolumeTypeContainer:
		return storagePoolVolumeAPIEndpointContainers, nil
	case storagePoolVolumeTypeImage:
		return storagePoolVolumeAPIEndpointImages, nil
	case storagePoolVolumeTypeCustom:
		return storagePoolVolumeAPIEndpointCustom, nil
	}

	return "", fmt.Errorf("invalid storage volume type")
}

func storagePoolVolumeUpdate(state *state.State, poolName string, volumeName string, volumeType int, newDescription string, newConfig map[string]string) error {
	s, err := storagePoolVolumeInit(state, poolName, volumeName, volumeType)
	if err != nil {
		return err
	}

	oldWritable := s.GetStoragePoolVolumeWritable()
	newWritable := oldWritable

	// Backup the current state
	oldDescription := oldWritable.Description
	oldConfig := map[string]string{}
	err = shared.DeepCopy(&oldWritable.Config, &oldConfig)
	if err != nil {
		return err
	}

	// Define a function which reverts everything.  Defer this function
	// so that it doesn't need to be explicitly called in every failing
	// return path. Track whether or not we want to undo the changes
	// using a closure.
	undoChanges := true
	defer func() {
		if undoChanges {
			s.SetStoragePoolVolumeWritable(&oldWritable)
		}
	}()

	// Diff the configurations
	changedConfig := []string{}
	userOnly := true
	for key := range oldConfig {
		if oldConfig[key] != newConfig[key] {
			if !strings.HasPrefix(key, "user.") {
				userOnly = false
			}

			if !shared.StringInSlice(key, changedConfig) {
				changedConfig = append(changedConfig, key)
			}
		}
	}

	for key := range newConfig {
		if oldConfig[key] != newConfig[key] {
			if !strings.HasPrefix(key, "user.") {
				userOnly = false
			}

			if !shared.StringInSlice(key, changedConfig) {
				changedConfig = append(changedConfig, key)
			}
		}
	}

	// Apply config changes if there are any
	if len(changedConfig) != 0 {
		newWritable.Description = newDescription
		newWritable.Config = newConfig

		// Update the storage pool
		if !userOnly {
			err = s.StoragePoolVolumeUpdate(&newWritable, changedConfig)
			if err != nil {
				return err
			}
		}

		// Apply the new configuration
		s.SetStoragePoolVolumeWritable(&newWritable)
	}

	poolID, err := db.StoragePoolGetID(state.DB, poolName)
	if err != nil {
		return err
	}

	// Update the database if something changed
	if len(changedConfig) != 0 || newDescription != oldDescription {
		err = db.StoragePoolVolumeUpdate(state.DB, volumeName, volumeType, poolID, newDescription, newConfig)
		if err != nil {
			return err
		}
	}

	// Success, update the closure to mark that the changes should be kept.
	undoChanges = false

	return nil
}

func storagePoolVolumeUsedByContainersGet(s *state.State, volumeName string,
	volumeTypeName string) ([]string, error) {
	cts, err := db.ContainersList(s.DB, db.CTypeRegular)
	if err != nil {
		return []string{}, err
	}

	ctsUsingVolume := []string{}
	volumeNameWithType := fmt.Sprintf("%s/%s", volumeTypeName, volumeName)
	for _, ct := range cts {
		c, err := containerLoadByName(s, ct)
		if err != nil {
			continue
		}

		for _, dev := range c.LocalDevices() {
			if dev["type"] != "disk" {
				continue
			}

			// Make sure that we don't compare against stuff like
			// "container////bla" but only against "container/bla".
			cleanSource := filepath.Clean(dev["source"])
			if cleanSource == volumeName || cleanSource == volumeNameWithType {
				ctsUsingVolume = append(ctsUsingVolume, ct)
			}
		}
	}

	return ctsUsingVolume, nil
}

// volumeUsedBy = append(volumeUsedBy, fmt.Sprintf("/%s/containers/%s", version.APIVersion, ct))
func storagePoolVolumeUsedByGet(s *state.State, volumeName string, volumeTypeName string) ([]string, error) {
	// Handle container volumes
	if volumeTypeName == "container" {
		cName, sName, snap := containerGetParentAndSnapshotName(volumeName)

		if snap {
			return []string{fmt.Sprintf("/%s/containers/%s/snapshots/%s", version.APIVersion, cName, sName)}, nil
		}

		return []string{fmt.Sprintf("/%s/containers/%s", version.APIVersion, cName)}, nil
	}

	// Handle image volumes
	if volumeTypeName == "image" {
		return []string{fmt.Sprintf("/%s/images/%s", version.APIVersion, volumeName)}, nil
	}

	// Look for containers using this volume
	ctsUsingVolume, err := storagePoolVolumeUsedByContainersGet(s,
		volumeName, volumeTypeName)
	if err != nil {
		return []string{}, err
	}

	volumeUsedBy := []string{}
	for _, ct := range ctsUsingVolume {
		volumeUsedBy = append(volumeUsedBy,
			fmt.Sprintf("/%s/containers/%s", version.APIVersion, ct))
	}

	profiles, err := profilesUsingPoolVolumeGetNames(s.DB, volumeName, volumeTypeName)
	if err != nil {
		return []string{}, err
	}

	if len(volumeUsedBy) == 0 && len(profiles) == 0 {
		return []string{}, nil
	}

	for _, pName := range profiles {
		volumeUsedBy = append(volumeUsedBy, fmt.Sprintf("/%s/profiles/%s", version.APIVersion, pName))
	}

	return volumeUsedBy, nil
}

func profilesUsingPoolVolumeGetNames(dbObj *sql.DB, volumeName string, volumeType string) ([]string, error) {
	usedBy := []string{}

	profiles, err := db.Profiles(dbObj)
	if err != nil {
		return usedBy, err
	}

	for _, pName := range profiles {
		_, profile, err := db.ProfileGet(dbObj, pName)
		if err != nil {
			return usedBy, err
		}

		volumeNameWithType := fmt.Sprintf("%s/%s", volumeType, volumeName)
		for _, v := range profile.Devices {
			if v["type"] != "disk" {
				continue
			}

			// Can't be a storage volume.
			if filepath.IsAbs(v["source"]) {
				continue
			}

			// Make sure that we don't compare against stuff
			// like "container////bla" but only against
			// "container/bla".
			cleanSource := filepath.Clean(v["source"])
			if cleanSource == volumeName || cleanSource == volumeNameWithType {
				usedBy = append(usedBy, pName)
			}
		}
	}

	return usedBy, nil
}

func storagePoolVolumeDBCreate(s *state.State, poolName string, volumeName, volumeDescription string, volumeTypeName string, volumeConfig map[string]string) error {
	// Check that the name of the new storage volume is valid. (For example.
	// zfs pools cannot contain "/" in their names.)
	err := storageValidName(volumeName)
	if err != nil {
		return err
	}

	// Convert the volume type name to our internal integer representation.
	volumeType, err := storagePoolVolumeTypeNameToType(volumeTypeName)
	if err != nil {
		return err
	}

	// Load storage pool the volume will be attached to.
	poolID, poolStruct, err := db.StoragePoolGet(s.DB, poolName)
	if err != nil {
		return err
	}

	// Check that a storage volume of the same storage volume type does not
	// already exist.
	volumeID, _ := db.StoragePoolVolumeGetTypeID(s.DB, volumeName, volumeType, poolID)
	if volumeID > 0 {
		return fmt.Errorf("a storage volume of type %s does already exist", volumeTypeName)
	}

	// Make sure that we don't pass a nil to the next function.
	if volumeConfig == nil {
		volumeConfig = map[string]string{}
	}

	// Validate the requested storage volume configuration.
	err = storageVolumeValidateConfig(poolName, volumeConfig, poolStruct)
	if err != nil {
		return err
	}

	err = storageVolumeFillDefault(poolName, volumeConfig, poolStruct)
	if err != nil {
		return err
	}

	// Create the database entry for the storage volume.
	_, err = db.StoragePoolVolumeCreate(s.DB, volumeName, volumeDescription, volumeType, poolID, volumeConfig)
	if err != nil {
		return fmt.Errorf("Error inserting %s of type %s into database: %s", poolName, volumeTypeName, err)
	}

	return nil
}

func storagePoolVolumeCreateInternal(state *state.State, poolName string, volumeName, volumeDescription string, volumeTypeName string, volumeConfig map[string]string) error {
	err := storagePoolVolumeDBCreate(state, poolName, volumeName, volumeDescription, volumeTypeName, volumeConfig)
	if err != nil {
		return err
	}

	// Convert the volume type name to our internal integer representation.
	volumeType, err := storagePoolVolumeTypeNameToType(volumeTypeName)
	if err != nil {
		return err
	}

	s, err := storagePoolVolumeInit(state, poolName, volumeName, volumeType)
	if err != nil {
		return err
	}

	poolID, _, _ := s.GetContainerPoolInfo()

	// Create storage volume.
	err = s.StoragePoolVolumeCreate()
	if err != nil {
		db.StoragePoolVolumeDelete(state.DB, volumeName, volumeType, poolID)
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// pprof封装.

package ghttp

import (
    "strings"
    runpprof "runtime/pprof"
    netpprof "net/http/pprof"
    "gitee.com/johng/gf/g/frame/gins"
)

// 用于pprof的对象
type utilpprof struct {}

func (p *utilpprof) Index(r *Request) {
    profiles := runpprof.Profiles()
    action   := r.Get("action")
    data     := map[string]interface{}{
        "uri"      : strings.TrimRight(r.URL.Path, "/") + "/",
        "profiles" : profiles,
    }
    if len(action) == 0 {
        view      := gins.View()
        buffer, _ := view.ParseContent("ghttp_pprof", `

                <title>gf ghttp pprof</title>

            {{$uri := .uri}}

                profiles:

                    {{range .profiles}}
{{.Count}}{{.Name}}{{end}}

full goroutine stack dump

            `, data)
        r.Response.Write(buffer)
        return
    }
    for _, p := range profiles {
        if p.Name() == action {
            p.WriteTo(r.Response.ResponseWriter, r.GetRequestInt("debug"))
            break
        }
    }
}

func (p *utilpprof) Cmdline(r *Request) {
    netpprof.Cmdline(r.Response.ResponseWriter, &r.Request)
}

func (p *utilpprof) Profile(r *Request) {
    netpprof.Profile(r.Response.ResponseWriter, &r.Request)
}

func (p *utilpprof) Symbol(r *Request) {
    netpprof.Symbol(r.Response.ResponseWriter, &r.Request)
}

func (p *utilpprof) Trace(r *Request) {
    netpprof.Trace(r.Response.ResponseWriter, &r.Request)
}

// 开启pprof支持
func (s *Server) EnablePprof(pattern string) {
    up := &utilpprof{}
    _, _, uri, _ := s.parsePattern(pattern)
    uri = strings.TrimRight(uri, "/")
    s.BindHandler(uri + "/*action", up.Index)
    s.BindHandler(uri + "/cmdline", up.Cmdline)
    s.BindHandler(uri + "/profile", up.Profile)
    s.BindHandler(uri + "/symbol",  up.Symbol)
    s.BindHandler(uri + "/trace",   up.Trace)
}<commit_msg>ghttp包增加pprof支持<commit_after>// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// pprof封装.

package ghttp

import (
    "strings"
    runpprof "runtime/pprof"
    netpprof "net/http/pprof"
    "gitee.com/johng/gf/g/frame/gins"
)

// 用于pprof的对象
type utilpprof struct {}

func (p *utilpprof) Index(r *Request) {
    profiles := runpprof.Profiles()
    action   := r.Get("action")
    data     := map[string]interface{}{
        "uri"      : strings.TrimRight(r.URL.Path, "/") + "/",
        "profiles" : profiles,
    }
    if len(action) == 0 {
        view      := gins.View()
        buffer, _ := view.ParseContent("ghttp_pprof", `

                <title>gf ghttp pprof</title>

            {{$uri := .uri}}

                profiles:

                    {{range .profiles}}
{{.Count}}{{.Name}}{{end}}

full goroutine stack dump

            `, data)
        r.Response.Write(buffer)
        return
    }
    for _, p := range profiles {
        if p.Name() == action {
            p.WriteTo(r.Response.ResponseWriter, r.GetRequestInt("debug"))
            break
        }
    }
}

func (p *utilpprof) Cmdline(r *Request) {
    netpprof.Cmdline(r.Response.ResponseWriter, &r.Request)
}

func (p *utilpprof) Profile(r *Request) {
    netpprof.Profile(r.Response.ResponseWriter, &r.Request)
}

func (p *utilpprof) Symbol(r *Request) {
    netpprof.Symbol(r.Response.ResponseWriter, &r.Request)
}

func (p *utilpprof) Trace(r *Request) {
    netpprof.Trace(r.Response.ResponseWriter, &r.Request)
}

// 开启pprof支持
func (s *Server) EnablePprof(pattern...string) {
    p := "/debug/pprof"
    if len(pattern) > 0 {
        p = pattern[0]
    }
    up := &utilpprof{}
    _, _, uri, _ := s.parsePattern(p)
    uri = strings.TrimRight(uri, "/")
    s.BindHandler(uri + "/*action", up.Index)
    s.BindHandler(uri + "/cmdline", up.Cmdline)
    s.BindHandler(uri + "/profile", up.Profile)
    s.BindHandler(uri + "/symbol",  up.Symbol)
    s.BindHandler(uri + "/trace",   up.Trace)
}<|endoftext|>


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

package server

import (
	"io/ioutil"
	"os"
	"path"
	"testing"

	. "github.com/onsi/gomega"

	"istio.io/istio/galley/pkg/server/settings"
)

func TestServer(t *testing.T) {
	g := NewGomegaWithT(t)

	rootDir, err := ioutil.TempDir(os.TempDir(), t.Name())
	g.Expect(err).To(BeNil())

	meshDir := path.Join(rootDir, "meshcfg")
	meshFile := path.Join(meshDir, "mesh.yaml")
	configDir := path.Join(rootDir, "cfg")
	accessListDir := path.Join(rootDir, "access")
	accessListFile := path.Join(accessListDir, "access.yaml")

	err = os.Mkdir(meshDir, os.ModePerm)
	g.Expect(err).To(BeNil())

	err = os.Mkdir(configDir, os.ModePerm)
	g.Expect(err).To(BeNil())

	err = os.Mkdir(accessListDir, os.ModePerm)
	g.Expect(err).To(BeNil())

	err = ioutil.WriteFile(meshFile, nil, os.ModePerm)
	g.Expect(err).To(BeNil())

	a := settings.DefaultArgs()
	a.MeshConfigFile = meshFile
	a.ConfigPath = configDir
	a.AccessListFile = accessListFile
	a.Insecure = true
	a.ValidationArgs.EnableValidation = false
	a.EnableProfiling = true

	s := New(a)

	g.Expect(s.Address()).To(BeNil())

	err = s.Start()
	g.Expect(err).To(BeNil())

	g.Expect(s.Address()).NotTo(BeNil())
	s.Stop()
	g.Expect(s.Address()).To(BeNil())

	s.Stop() // Do not panic
}
<commit_msg>Reduce Galley unit test flakes (#15667)<commit_after>// Copyright 2018 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"io/ioutil"
	"os"
	"path"
	"testing"

	. "github.com/onsi/gomega"

	"istio.io/istio/galley/pkg/server/settings"
)

func TestServer(t *testing.T) {
	g := NewGomegaWithT(t)

	rootDir, err := ioutil.TempDir(os.TempDir(), t.Name())
	g.Expect(err).To(BeNil())

	meshDir := path.Join(rootDir, "meshcfg")
	meshFile := path.Join(meshDir, "mesh.yaml")
	configDir := path.Join(rootDir, "cfg")
	accessListDir := path.Join(rootDir, "access")
	accessListFile := path.Join(accessListDir, "access.yaml")

	err = os.Mkdir(meshDir, os.ModePerm)
	g.Expect(err).To(BeNil())

	err = os.Mkdir(configDir, os.ModePerm)
	g.Expect(err).To(BeNil())

	err = os.Mkdir(accessListDir, os.ModePerm)
	g.Expect(err).To(BeNil())

	err = ioutil.WriteFile(meshFile, nil, os.ModePerm)
	g.Expect(err).To(BeNil())

	a := settings.DefaultArgs()
	// If the default port is used it may run into conflicts during testing
	a.IntrospectionOptions.Port = 0
	a.MeshConfigFile = meshFile
	a.ConfigPath = configDir
	a.AccessListFile = accessListFile
	a.Insecure = true
	a.ValidationArgs.EnableValidation = false
	a.EnableProfiling = true

	s := New(a)

	g.Expect(s.Address()).To(BeNil())

	err = s.Start()
	g.Expect(err).To(BeNil())

	g.Expect(s.Address()).NotTo(BeNil())
	s.Stop()
	g.Expect(s.Address()).To(BeNil())

	s.Stop() // Do not panic
}
<|endoftext|>


<commit_before>package util

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

func Mkdir(path string) error {
    dir := filepath.Dir(path)
    if FileExists(dir) {
        return nil
    }
    return os.Mkdir(dir, 0700)
}

func FileExists(path string) bool {
    _, err := os.Stat(path)
    return err == nil
}

func IsRegularFile(path string) (bool, error) {
    f, err := os.Open(path)
    if err != nil {
        return false, err
    }

    info, err := f.Stat()
    if err != nil {
        return false, err
    }

    return info.Mode().IsRegular(), nil
}

type File struct {
    Name string
    Content string
}

func ReadFiles(path string) ([]*File, error) {
    isRegular, err := IsRegularFile(path)
    if err != nil {
        return nil, err
    } else if !isRegular {
        return nil, fmt.Errorf("File (%s) is not a regular file", path)
    }

    root := filepath.Dir(path)

    paths, err := collectPaths(root)
    if err != nil {
        return nil, err
    }

    // Ensure that the given file is first in the paths list
    paths = ensureFirst(path, paths)

    files, err := pathsToFiles(paths)
    if err != nil {
        return nil, err
    }

    return files, err
}

func collectPaths(root string) ([]string, error) {
    var paths = make([]string, 0)

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

        // Skip directories
        if info.IsDir() {
            return nil
        }

        // Skip hidden files
        if strings.HasPrefix(info.Name(), ".") {
            return nil
        }

        paths = append(paths, path)

        return nil
    })

    return paths, err
}

func pathsToFiles(paths []string) ([]*File, error) {
    var files = make([]*File, 0)

    for _, path := range paths {
        // Read file content
        bytes, err := ioutil.ReadFile(path)
        if err != nil {
            return nil, err
        }

        files = append(files, &File{
            Name: path,
            Content: string(bytes),
        })
    }

    return files, nil
}

func ensureFirst(firstItem string, items []string) []string {
    newItems := []string{firstItem}

    // Append the rest of the items
    for _, item := range items {
        if item != firstItem {
            newItems = append(newItems, item)
        }
    }

    return newItems
}
<commit_msg>Check start of path<commit_after>package util

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

func Mkdir(path string) error {
    dir := filepath.Dir(path)
    if FileExists(dir) {
        return nil
    }
    return os.Mkdir(dir, 0700)
}

func FileExists(path string) bool {
    _, err := os.Stat(path)
    return err == nil
}

func IsRegularFile(path string) (bool, error) {
    f, err := os.Open(path)
    if err != nil {
        return false, err
    }

    info, err := f.Stat()
    if err != nil {
        return false, err
    }

    return info.Mode().IsRegular(), nil
}

type File struct {
    Name string
    Content string
}

func ReadFiles(path string) ([]*File, error) {
    isRegular, err := IsRegularFile(path)
    if err != nil {
        return nil, err
    } else if !isRegular {
        return nil, fmt.Errorf("File (%s) is not a regular file", path)
    }

    root := filepath.Dir(path)

    paths, err := collectPaths(root)
    if err != nil {
        return nil, err
    }

    // Ensure that the given file is first in the paths list
    paths = ensureFirst(path, paths)

    files, err := pathsToFiles(paths)
    if err != nil {
        return nil, err
    }

    return files, err
}

func collectPaths(root string) ([]string, error) {
    var paths = make([]string, 0)

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

        // Skip directories
        if info.IsDir() {
            return nil
        }

        // Skip hidden files
        if strings.HasPrefix(path, ".") {
            return nil
        }

        paths = append(paths, path)

        return nil
    })

    return paths, err
}

func pathsToFiles(paths []string) ([]*File, error) {
    var files = make([]*File, 0)

    for _, path := range paths {
        // Read file content
        bytes, err := ioutil.ReadFile(path)
        if err != nil {
            return nil, err
        }

        files = append(files, &File{
            Name: path,
            Content: string(bytes),
        })
    }

    return files, nil
}

func ensureFirst(firstItem string, items []string) []string {
    newItems := []string{firstItem}

    // Append the rest of the items
    for _, item := range items {
        if item != firstItem {
            newItems = append(newItems, item)
        }
    }

    return newItems
}
<|endoftext|>


<commit_before>package util

//A utility stack implementation for arbitrary data types with Push(), Pop() and Len() functions
type Stack struct {
	top  *stackElement
	size int
}

type stackElement struct {
	value interface{}
	next  *stackElement
}

func (s *Stack) Len() int {
	return s.size
}

func (s *Stack) Push(value interface{}) {
	s.top = &stackElement{value, s.top}
	s.size++
}

func (s *Stack) Pop() interface{} {
	if s.size > 0 {
		value := s.top.value
		s.top = s.top.next
		s.size--
		return value
	}
	return nil
}
<commit_msg>Add exported methods docs<commit_after>package util

// Stack implementation for arbitrary data types with Push(), Pop() and Len() functions
type Stack struct {
	top  *stackElement
	size int
}

type stackElement struct {
	value interface{}
	next  *stackElement
}

// Len returns the size of stack
func (s *Stack) Len() int {
	return s.size
}

// Push a new value onto the stack
func (s *Stack) Push(value interface{}) {
	s.top = &stackElement{value, s.top}
	s.size++
}

// Pop the most recently pushed value from the stack
func (s *Stack) Pop() interface{} {
	if s.size > 0 {
		value := s.top.value
		s.top = s.top.next
		s.size--
		return value
	}
	return nil
}
<|endoftext|>


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

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

const (
	// Base URL to fetch the token info
	googleTokenInfoURLPrefix = "https://www.googleapis.com/oauth2/v3/tokeninfo/?access_token="
)

// Supported output formats
const (
	formatJson         = "json"
	formatJsonCompact  = "json_compact"
	formatPretty       = "pretty"
	formatHeader       = "header"
	formatBare         = "bare"
	formatRefreshToken = "refresh_token"
)

// Credentials file types.
// If type is not one of the below, it means the file is a
// Google Client ID JSON.
const (
	serviceAccountKey  = "service_account"
	userCredentialsKey = "authorized_user"
	externalAccountKey = "external_account"
)

// An extensible structure that holds the settings
// used by different oauth2l tasks.
// These settings are used by oauth2l only
// and are not part of GUAC settings.
type TaskSettings struct {
	// AuthType determines which auth tool to use (sso vs sgauth)
	AuthType string
	// Output format for Fetch task
	Format string
	// CurlCli override for Curl task
	CurlCli string
	// Url endpoint for Curl task
	Url string
	// Extra args for Curl task
	ExtraArgs []string
	// SsoCli override for Sso task
	SsoCli string
	// Refresh expired access token in cache
	Refresh bool
}

// Fetches and prints the token in plain text with the given settings
// using Google Authenticator.
func Fetch(settings *Settings, taskSettings *TaskSettings) {
	token := fetchToken(settings, taskSettings)
	printToken(token, taskSettings.Format, settings)
}

// Fetches and prints the token in header format with the given settings
// using Google Authenticator.
func Header(settings *Settings, taskSettings *TaskSettings) {
	taskSettings.Format = formatHeader
	Fetch(settings, taskSettings)
}

// Fetches token with the given settings using Google Authenticator
// and use the token as header to make curl request.
func Curl(settings *Settings, taskSettings *TaskSettings) {
	token := fetchToken(settings, taskSettings)
	if token != nil {
		header := BuildHeader(token.TokenType, token.AccessToken)
		curlcli := taskSettings.CurlCli
		url := taskSettings.Url
		extraArgs := taskSettings.ExtraArgs
		CurlCommand(curlcli, header, url, extraArgs...)
	}
}

// Fetches the information of the given token.
func Info(token string) int {
	info, err := getTokenInfo(token)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Println(info)
	}
	return 0
}

// Tests the given token. Returns 0 for valid tokens.
// Otherwise returns 1.
func Test(token string) int {
	_, err := getTokenInfo(token)
	if err != nil {
		fmt.Println(1)
		return 1
	} else {
		fmt.Println(0)
		return 0
	}
}

// Resets the cache.
func Reset() {
	err := ClearCache()
	if err != nil {
		fmt.Print(err)
	}
}

// Returns the given token in standard header format.
func BuildHeader(tokenType string, token string) string {
	return fmt.Sprintf("Authorization: %s %s", tokenType, token)
}

func getTokenInfo(token string) (string, error) {
	c := http.DefaultClient
	resp, err := c.Get(googleTokenInfoURLPrefix + token)
	if err != nil {
		return "", err
	}
	data, err := ioutil.ReadAll(resp.Body)
	if resp.StatusCode != 200 {
		return "", errors.New(string(data))
	}
	return string(data), err
}

// fetchToken attempts to fetch and cache an access token.
//
// If SSO is specified, obtain token via SSOFetch instead of FetchToken.
//
// If cached token is expired and refresh is requested,
// attempt to obtain new token via RefreshToken instead
// of default OAuth flow.
//
// If STS is requested, we will perform an STS exchange
// after the original access token has been fetched.
func fetchToken(settings *Settings, taskSettings *TaskSettings) *oauth2.Token {
	token, err := LookupCache(settings)
	tokenExpired := isTokenExpired(token)
	if token == nil || tokenExpired {
		if taskSettings.AuthType == "sso" {
			token, err = SSOFetch(taskSettings.SsoCli, settings.Email, settings.Scope)
			if err != nil {
				fmt.Println(err)
				return nil
			}
		} else {
			fetchSettings := settings
			if tokenExpired && taskSettings.Refresh {
				// If creds cannot be retrieved here, which is unexpected, we will ignore
				// the error and let FetchToken return a standardized error message
				// in the subsequent step.
				creds, _ := FindJSONCredentials(context.Background(), settings)
				refreshTokenJSON := BuildRefreshTokenJSON(token.RefreshToken, creds)
				if refreshTokenJSON != "" {
					refreshSettings := *settings // Make a shallow copy
					refreshSettings.CredentialsJSON = refreshTokenJSON
					fetchSettings = &refreshSettings
				}
			}
			token, err = FetchToken(context.Background(), fetchSettings)
			if err != nil {
				fmt.Println(err)
				return nil
			}
		}
		if settings.ServiceAccount != "" {
			token, err = GenerateServiceAccountAccessToken(token.AccessToken, settings.ServiceAccount, settings.Scope)
			if err != nil {
				fmt.Println(err)
				return nil
			}
		}
		if settings.Sts {
			token, err = StsExchange(token.AccessToken, EncodeClaims(settings))
			if err != nil {
				fmt.Println(err)
				return nil
			}
		}
		err = InsertCache(settings, token)
		if err != nil {
			fmt.Println(err)
			return nil
		}
	}
	return token
}

func isTokenExpired(token *oauth2.Token) bool {
	// SSO and STS tokens currently do not have expiration, as indicated by empty Expiry.
	return token != nil && !token.Expiry.IsZero() && time.Now().After(token.Expiry)
}

func getCredentialType(creds *google.Credentials) string {
	var m map[string]string
	err := json.Unmarshal(creds.JSON, &m)
	if err != nil && m["type"] != "" {
		return m["type"]
	}
	return ""
}

// Marshals the given oauth2.Token into a JSON bytearray and include Extra
// fields that normally would be omitted with default marshalling.
func marshalWithExtras(token *oauth2.Token, indent string) ([]byte, error) {
	data, err := json.Marshal(token)
	if err != nil {
		return nil, err
	}
	var m map[string]string
	err = json.Unmarshal(data, &m)
	if err != nil {
		return nil, err
	}
	if token.Extra("issued_token_type") != nil {
		m["issued_token_type"] = token.Extra("issued_token_type").(string)
	}
	return json.MarshalIndent(m, "", indent)
}

// Prints the token with the specified format.
func printToken(token *oauth2.Token, format string, settings *Settings) {
	if token != nil {
		switch format {
		case formatBare:
			fmt.Println(token.AccessToken)
		case formatHeader:
			printHeader(token.TokenType, token.AccessToken)
		case formatJson:
			printJson(token, "  ")
		case formatJsonCompact:
			printJson(token, "")
		case formatPretty:
			creds, err := FindJSONCredentials(context.Background(), settings)
			if err != nil {
				log.Fatal(err.Error())
			}
			fmt.Printf("Fetched credentials of type:\n  %s\n"+
				"Access Token:\n  %s\n",
				getCredentialType(creds), token.AccessToken)
		case formatRefreshToken:
			creds, err := FindJSONCredentials(context.Background(), settings)
			if err != nil {
				log.Fatal(err.Error())
			}
			credsType := getCredentialType(creds)
			if credsType == serviceAccountKey {
				log.Fatalf("Refresh token output format is not supported for Service Account credentials type")
			}
			if credsType == externalAccountKey {
				log.Fatalf("Refresh token output format is not supported for External Account credentials type")
			}
			if credsType == userCredentialsKey {
				fmt.Print(string(creds.JSON)) // The input credential is already in refresh token format.
			}
			fmt.Println(BuildRefreshTokenJSON(token.RefreshToken, creds))
		default:
			log.Fatalf("Invalid output_format: '%s'", format)
		}
	}
}

func printHeader(tokenType string, token string) {
	fmt.Println(BuildHeader(tokenType, token))
}

func printJson(token *oauth2.Token, indent string) {
	data, err := marshalWithExtras(token, indent)
	if err != nil {
		log.Fatal(err.Error())
		return
	}
	fmt.Println(string(data))
}
<commit_msg>Print "id_token" from the JWT token if presented (#120)<commit_after>//
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package util

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

const (
	// Base URL to fetch the token info
	googleTokenInfoURLPrefix = "https://www.googleapis.com/oauth2/v3/tokeninfo/?access_token="
)

// Supported output formats
const (
	formatJson         = "json"
	formatJsonCompact  = "json_compact"
	formatPretty       = "pretty"
	formatHeader       = "header"
	formatBare         = "bare"
	formatRefreshToken = "refresh_token"
)

// Credentials file types.
// If type is not one of the below, it means the file is a
// Google Client ID JSON.
const (
	serviceAccountKey  = "service_account"
	userCredentialsKey = "authorized_user"
	externalAccountKey = "external_account"
)

// An extensible structure that holds the settings
// used by different oauth2l tasks.
// These settings are used by oauth2l only
// and are not part of GUAC settings.
type TaskSettings struct {
	// AuthType determines which auth tool to use (sso vs sgauth)
	AuthType string
	// Output format for Fetch task
	Format string
	// CurlCli override for Curl task
	CurlCli string
	// Url endpoint for Curl task
	Url string
	// Extra args for Curl task
	ExtraArgs []string
	// SsoCli override for Sso task
	SsoCli string
	// Refresh expired access token in cache
	Refresh bool
}

// Fetches and prints the token in plain text with the given settings
// using Google Authenticator.
func Fetch(settings *Settings, taskSettings *TaskSettings) {
	token := fetchToken(settings, taskSettings)
	printToken(token, taskSettings.Format, settings)
}

// Fetches and prints the token in header format with the given settings
// using Google Authenticator.
func Header(settings *Settings, taskSettings *TaskSettings) {
	taskSettings.Format = formatHeader
	Fetch(settings, taskSettings)
}

// Fetches token with the given settings using Google Authenticator
// and use the token as header to make curl request.
func Curl(settings *Settings, taskSettings *TaskSettings) {
	token := fetchToken(settings, taskSettings)
	if token != nil {
		header := BuildHeader(token.TokenType, token.AccessToken)
		curlcli := taskSettings.CurlCli
		url := taskSettings.Url
		extraArgs := taskSettings.ExtraArgs
		CurlCommand(curlcli, header, url, extraArgs...)
	}
}

// Fetches the information of the given token.
func Info(token string) int {
	info, err := getTokenInfo(token)
	if err != nil {
		fmt.Print(err)
	} else {
		fmt.Println(info)
	}
	return 0
}

// Tests the given token. Returns 0 for valid tokens.
// Otherwise returns 1.
func Test(token string) int {
	_, err := getTokenInfo(token)
	if err != nil {
		fmt.Println(1)
		return 1
	} else {
		fmt.Println(0)
		return 0
	}
}

// Resets the cache.
func Reset() {
	err := ClearCache()
	if err != nil {
		fmt.Print(err)
	}
}

// Returns the given token in standard header format.
func BuildHeader(tokenType string, token string) string {
	return fmt.Sprintf("Authorization: %s %s", tokenType, token)
}

func getTokenInfo(token string) (string, error) {
	c := http.DefaultClient
	resp, err := c.Get(googleTokenInfoURLPrefix + token)
	if err != nil {
		return "", err
	}
	data, err := ioutil.ReadAll(resp.Body)
	if resp.StatusCode != 200 {
		return "", errors.New(string(data))
	}
	return string(data), err
}

// fetchToken attempts to fetch and cache an access token.
//
// If SSO is specified, obtain token via SSOFetch instead of FetchToken.
//
// If cached token is expired and refresh is requested,
// attempt to obtain new token via RefreshToken instead
// of default OAuth flow.
//
// If STS is requested, we will perform an STS exchange
// after the original access token has been fetched.
func fetchToken(settings *Settings, taskSettings *TaskSettings) *oauth2.Token {
	token, err := LookupCache(settings)
	tokenExpired := isTokenExpired(token)
	if token == nil || tokenExpired {
		if taskSettings.AuthType == "sso" {
			token, err = SSOFetch(taskSettings.SsoCli, settings.Email, settings.Scope)
			if err != nil {
				fmt.Println(err)
				return nil
			}
		} else {
			fetchSettings := settings
			if tokenExpired && taskSettings.Refresh {
				// If creds cannot be retrieved here, which is unexpected, we will ignore
				// the error and let FetchToken return a standardized error message
				// in the subsequent step.
				creds, _ := FindJSONCredentials(context.Background(), settings)
				refreshTokenJSON := BuildRefreshTokenJSON(token.RefreshToken, creds)
				if refreshTokenJSON != "" {
					refreshSettings := *settings // Make a shallow copy
					refreshSettings.CredentialsJSON = refreshTokenJSON
					fetchSettings = &refreshSettings
				}
			}
			token, err = FetchToken(context.Background(), fetchSettings)
			if err != nil {
				fmt.Println(err)
				return nil
			}
		}
		if settings.ServiceAccount != "" {
			token, err = GenerateServiceAccountAccessToken(token.AccessToken, settings.ServiceAccount, settings.Scope)
			if err != nil {
				fmt.Println(err)
				return nil
			}
		}
		if settings.Sts {
			token, err = StsExchange(token.AccessToken, EncodeClaims(settings))
			if err != nil {
				fmt.Println(err)
				return nil
			}
		}
		err = InsertCache(settings, token)
		if err != nil {
			fmt.Println(err)
			return nil
		}
	}
	return token
}

func isTokenExpired(token *oauth2.Token) bool {
	// SSO and STS tokens currently do not have expiration, as indicated by empty Expiry.
	return token != nil && !token.Expiry.IsZero() && time.Now().After(token.Expiry)
}

func getCredentialType(creds *google.Credentials) string {
	var m map[string]string
	err := json.Unmarshal(creds.JSON, &m)
	if err != nil && m["type"] != "" {
		return m["type"]
	}
	return ""
}

// Marshals the given oauth2.Token into a JSON bytearray and include Extra
// fields that normally would be omitted with default marshalling.
func marshalWithExtras(token *oauth2.Token, indent string) ([]byte, error) {
	data, err := json.Marshal(token)
	if err != nil {
		return nil, err
	}
	var m map[string]string
	err = json.Unmarshal(data, &m)
	if err != nil {
		return nil, err
	}
	if token.Extra("issued_token_type") != nil {
		m["issued_token_type"] = token.Extra("issued_token_type").(string)
	}
	if token.Extra("id_token") != nil {
		m["id_token"] = token.Extra("id_token").(string)
	}
	return json.MarshalIndent(m, "", indent)
}

// Prints the token with the specified format.
func printToken(token *oauth2.Token, format string, settings *Settings) {
	if token != nil {
		switch format {
		case formatBare:
			fmt.Println(token.AccessToken)
		case formatHeader:
			printHeader(token.TokenType, token.AccessToken)
		case formatJson:
			printJson(token, "  ")
		case formatJsonCompact:
			printJson(token, "")
		case formatPretty:
			creds, err := FindJSONCredentials(context.Background(), settings)
			if err != nil {
				log.Fatal(err.Error())
			}
			fmt.Printf("Fetched credentials of type:\n  %s\n"+
				"Access Token:\n  %s\n",
				getCredentialType(creds), token.AccessToken)
		case formatRefreshToken:
			creds, err := FindJSONCredentials(context.Background(), settings)
			if err != nil {
				log.Fatal(err.Error())
			}
			credsType := getCredentialType(creds)
			if credsType == serviceAccountKey {
				log.Fatalf("Refresh token output format is not supported for Service Account credentials type")
			}
			if credsType == externalAccountKey {
				log.Fatalf("Refresh token output format is not supported for External Account credentials type")
			}
			if credsType == userCredentialsKey {
				fmt.Print(string(creds.JSON)) // The input credential is already in refresh token format.
			}
			fmt.Println(BuildRefreshTokenJSON(token.RefreshToken, creds))
		default:
			log.Fatalf("Invalid output_format: '%s'", format)
		}
	}
}

func printHeader(tokenType string, token string) {
	fmt.Println(BuildHeader(tokenType, token))
}

func printJson(token *oauth2.Token, indent string) {
	data, err := marshalWithExtras(token, indent)
	if err != nil {
		log.Fatal(err.Error())
		return
	}
	fmt.Println(string(data))
}
<|endoftext|>


<commit_before>package kite

import (
	"encoding/json"
	"fmt"
	"github.com/streadway/amqp"
	"io"
	"koding/tools/config"
	"koding/tools/dnode"
	"koding/tools/log"
	"koding/tools/utils"
	"os"
	"os/exec"
	"os/signal"
	"os/user"
	"strconv"
	"syscall"
)

type Kite struct {
	Name     string
	Handlers map[string]Handler
}

type Handler struct {
	Concurrent bool
	Callback   func(args *dnode.Partial, session *Session) (interface{}, error)
}

func New(name string) *Kite {
	return &Kite{
		Name:     name,
		Handlers: make(map[string]Handler),
	}
}

func (k *Kite) Handle(method string, concurrent bool, callback func(args *dnode.Partial, session *Session) (interface{}, error)) {
	k.Handlers[method] = Handler{concurrent, callback}
}

func (k *Kite) Run() {
	utils.RunStatusLogger()

	sigtermChannel := make(chan os.Signal)
	signal.Notify(sigtermChannel, syscall.SIGTERM)

	consumeConn := utils.CreateAmqpConnection(k.Name + "-kite")
	defer consumeConn.Close()

	publishConn := utils.CreateAmqpConnection(k.Name + "-kite")
	defer publishConn.Close()

	routeMap := make(map[string](chan<- []byte))
	defer func() {
		for _, channel := range routeMap {
			close(channel)
		}
	}()

	publishChannel := utils.CreateAmqpChannel(publishConn)
	defer publishChannel.Close()

	consumeChannel := utils.CreateAmqpChannel(consumeConn)
	utils.DeclareAmqpPresenceExchange(consumeChannel, "services-presence", "kite", "kite-"+k.Name, "kite-"+k.Name)
	stream := utils.DeclareBindConsumeAmqpQueue(consumeChannel, "fanout", "kite-"+k.Name, "")

	for {
		select {
		case message, ok := <-stream:
			if !ok {
				return
			}

			switch message.RoutingKey {
			case "auth.join":
				var client struct {
					Username   string
					RoutingKey string
				}
				err := json.Unmarshal(message.Body, &client)
				if err != nil || client.Username == "" || client.RoutingKey == "" {
					log.Err("Invalid auth.join message.", message.Body)
					continue
				}

				if _, found := routeMap[client.RoutingKey]; found {
					continue // duplicate key
				}
				channel := make(chan []byte, 1024)
				routeMap[client.RoutingKey] = channel

				go func() {
					defer log.RecoverAndLog()

					utils.ChangeNumClients <- 1
					log.Debug("Client connected: " + client.Username)
					defer func() {
						utils.ChangeNumClients <- -1
						log.Debug("Client disconnected: " + client.Username)
					}()

					session := NewSession(client.Username)
					defer session.Close()

					d := dnode.New()
					defer d.Close()
					d.OnRootMethod = func(method string, args *dnode.Partial) {
						defer log.RecoverAndLog()

						if method == "ping" {
							d.Send("pong")
							return
						}

						var partials []*dnode.Partial
						err := args.Unmarshal(&partials)
						if err != nil {
							panic(err)
						}

						var options struct {
							WithArgs *dnode.Partial
						}
						err = partials[0].Unmarshal(&options)
						if err != nil {
							panic(err)
						}
						var resultCallback dnode.Callback
						err = partials[1].Unmarshal(&resultCallback)
						if err != nil {
							panic(err)
						}

						handler, found := k.Handlers[method]
						if !found {
							resultCallback(fmt.Sprintf("Method '%v' not known.", method), nil)
							return
						}

						execHandler := func() {
							result, err := handler.Callback(options.WithArgs, session)
							if err != nil {
								resultCallback(err.Error(), result)
							} else if result != nil {
								resultCallback(nil, result)
							}
						}
						if handler.Concurrent {
							go func() {
								defer log.RecoverAndLog()
								execHandler()
							}()
						} else {
							execHandler()
						}
					}

					go func() {
						defer log.RecoverAndLog()
						for data := range d.SendChan {
							log.Debug("Write", client.RoutingKey, data)
							err := publishChannel.Publish("broker", client.RoutingKey, false, false, amqp.Publishing{Body: data})
							if err != nil {
								log.LogError(err, 0)
							}
						}
					}()

					d.Send("ready", "kite-"+k.Name)

					for message := range channel {
						log.Debug("Read", client.RoutingKey, message)
						d.ProcessMessage(message)
					}
				}()

			case "auth.leave":
				var client struct {
					RoutingKey string
				}
				err := json.Unmarshal(message.Body, &client)
				if err != nil || client.RoutingKey == "" {
					log.Err("Invalid auth.leave message.", message.Body)
					continue
				}

				channel, found := routeMap[client.RoutingKey]
				if found {
					close(channel)
					delete(routeMap, client.RoutingKey)
				}

			default:
				channel, found := routeMap[message.RoutingKey]
				if found {
					select {
					case channel <- message.Body:
						// successful
					default:
						close(channel)
						delete(routeMap, message.RoutingKey)
						log.Warn("Dropped client because of message buffer overflow.")
					}
				}
			}

		case <-sigtermChannel:
			log.Info("Received TERM signal. Beginning shutdown...")
			utils.BeginShutdown()
			consumeChannel.Close()
		}
	}
}

type Session struct {
	User, Home string
	Uid, Gid   int
	closers    []io.Closer
}

func NewSession(username string) *Session {
	userData, err := user.Lookup(username)
	if err != nil {
		panic(err)
	}
	uid, err := strconv.Atoi(userData.Uid)
	if err != nil {
		panic(err)
	}
	gid, err := strconv.Atoi(userData.Gid)
	if err != nil {
		panic(err)
	}
	if uid == 0 || gid == 0 {
		panic("SECURITY BREACH: User lookup returned root.")
	}
	return &Session{
		User: username,
		Home: config.Current.GoConfig.HomePrefix + username,
		Uid:  uid,
		Gid:  gid,
	}
}

func (session *Session) CloseOnDisconnect(closer io.Closer) {
	session.closers = append(session.closers, closer)
}

func (session *Session) Close() {
	for _, closer := range session.closers {
		closer.Close()
	}
	session.closers = nil
}

func (session *Session) CreateCommand(command ...string) *exec.Cmd {
	var cmd *exec.Cmd
	if config.Current.GoConfig.UseLVE {
		cmd = exec.Command("/bin/lve_exec", command...)
	} else {
		cmd = exec.Command(command[0], command[1:]...)
	}
	cmd.Dir = session.Home
	cmd.Env = []string{
		"USER=" + session.User,
		"LOGNAME=" + session.User,
		"HOME=" + session.Home,
		"SHELL=/bin/bash",
		"TERM=xterm",
		"LANG=en_US.UTF-8",
		"PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:" + session.Home + "/bin",
	}
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Credential: &syscall.Credential{
			Uid: uint32(session.Uid),
			Gid: uint32(session.Gid),
		},
	}

	return cmd
}
<commit_msg> We have users on rabbitmq with kite-* instead *-kite<commit_after>package kite

import (
	"encoding/json"
	"fmt"
	"github.com/streadway/amqp"
	"io"
	"koding/tools/config"
	"koding/tools/dnode"
	"koding/tools/log"
	"koding/tools/utils"
	"os"
	"os/exec"
	"os/signal"
	"os/user"
	"strconv"
	"syscall"
)

type Kite struct {
	Name     string
	Handlers map[string]Handler
}

type Handler struct {
	Concurrent bool
	Callback   func(args *dnode.Partial, session *Session) (interface{}, error)
}

func New(name string) *Kite {
	return &Kite{
		Name:     name,
		Handlers: make(map[string]Handler),
	}
}

func (k *Kite) Handle(method string, concurrent bool, callback func(args *dnode.Partial, session *Session) (interface{}, error)) {
	k.Handlers[method] = Handler{concurrent, callback}
}

func (k *Kite) Run() {
	utils.RunStatusLogger()

	sigtermChannel := make(chan os.Signal)
	signal.Notify(sigtermChannel, syscall.SIGTERM)

	consumeConn := utils.CreateAmqpConnection("kite-" + k.Name)
	defer consumeConn.Close()

	publishConn := utils.CreateAmqpConnection("kite-" + k.Name)
	defer publishConn.Close()

	routeMap := make(map[string](chan<- []byte))
	defer func() {
		for _, channel := range routeMap {
			close(channel)
		}
	}()

	publishChannel := utils.CreateAmqpChannel(publishConn)
	defer publishChannel.Close()

	consumeChannel := utils.CreateAmqpChannel(consumeConn)
	utils.DeclareAmqpPresenceExchange(consumeChannel, "services-presence", "kite", "kite-"+k.Name, "kite-"+k.Name)
	stream := utils.DeclareBindConsumeAmqpQueue(consumeChannel, "fanout", "kite-"+k.Name, "")

	for {
		select {
		case message, ok := <-stream:
			if !ok {
				return
			}

			switch message.RoutingKey {
			case "auth.join":
				var client struct {
					Username   string
					RoutingKey string
				}
				err := json.Unmarshal(message.Body, &client)
				if err != nil || client.Username == "" || client.RoutingKey == "" {
					log.Err("Invalid auth.join message.", message.Body)
					continue
				}

				if _, found := routeMap[client.RoutingKey]; found {
					continue // duplicate key
				}
				channel := make(chan []byte, 1024)
				routeMap[client.RoutingKey] = channel

				go func() {
					defer log.RecoverAndLog()

					utils.ChangeNumClients <- 1
					log.Debug("Client connected: " + client.Username)
					defer func() {
						utils.ChangeNumClients <- -1
						log.Debug("Client disconnected: " + client.Username)
					}()

					session := NewSession(client.Username)
					defer session.Close()

					d := dnode.New()
					defer d.Close()
					d.OnRootMethod = func(method string, args *dnode.Partial) {
						defer log.RecoverAndLog()

						if method == "ping" {
							d.Send("pong")
							return
						}

						var partials []*dnode.Partial
						err := args.Unmarshal(&partials)
						if err != nil {
							panic(err)
						}

						var options struct {
							WithArgs *dnode.Partial
						}
						err = partials[0].Unmarshal(&options)
						if err != nil {
							panic(err)
						}
						var resultCallback dnode.Callback
						err = partials[1].Unmarshal(&resultCallback)
						if err != nil {
							panic(err)
						}

						handler, found := k.Handlers[method]
						if !found {
							resultCallback(fmt.Sprintf("Method '%v' not known.", method), nil)
							return
						}

						execHandler := func() {
							result, err := handler.Callback(options.WithArgs, session)
							if err != nil {
								resultCallback(err.Error(), result)
							} else if result != nil {
								resultCallback(nil, result)
							}
						}
						if handler.Concurrent {
							go func() {
								defer log.RecoverAndLog()
								execHandler()
							}()
						} else {
							execHandler()
						}
					}

					go func() {
						defer log.RecoverAndLog()
						for data := range d.SendChan {
							log.Debug("Write", client.RoutingKey, data)
							err := publishChannel.Publish("broker", client.RoutingKey, false, false, amqp.Publishing{Body: data})
							if err != nil {
								log.LogError(err, 0)
							}
						}
					}()

					d.Send("ready", "kite-"+k.Name)

					for message := range channel {
						log.Debug("Read", client.RoutingKey, message)
						d.ProcessMessage(message)
					}
				}()

			case "auth.leave":
				var client struct {
					RoutingKey string
				}
				err := json.Unmarshal(message.Body, &client)
				if err != nil || client.RoutingKey == "" {
					log.Err("Invalid auth.leave message.", message.Body)
					continue
				}

				channel, found := routeMap[client.RoutingKey]
				if found {
					close(channel)
					delete(routeMap, client.RoutingKey)
				}

			default:
				channel, found := routeMap[message.RoutingKey]
				if found {
					select {
					case channel <- message.Body:
						// successful
					default:
						close(channel)
						delete(routeMap, message.RoutingKey)
						log.Warn("Dropped client because of message buffer overflow.")
					}
				}
			}

		case <-sigtermChannel:
			log.Info("Received TERM signal. Beginning shutdown...")
			utils.BeginShutdown()
			consumeChannel.Close()
		}
	}
}

type Session struct {
	User, Home string
	Uid, Gid   int
	closers    []io.Closer
}

func NewSession(username string) *Session {
	userData, err := user.Lookup(username)
	if err != nil {
		panic(err)
	}
	uid, err := strconv.Atoi(userData.Uid)
	if err != nil {
		panic(err)
	}
	gid, err := strconv.Atoi(userData.Gid)
	if err != nil {
		panic(err)
	}
	if uid == 0 || gid == 0 {
		panic("SECURITY BREACH: User lookup returned root.")
	}
	return &Session{
		User: username,
		Home: config.Current.GoConfig.HomePrefix + username,
		Uid:  uid,
		Gid:  gid,
	}
}

func (session *Session) CloseOnDisconnect(closer io.Closer) {
	session.closers = append(session.closers, closer)
}

func (session *Session) Close() {
	for _, closer := range session.closers {
		closer.Close()
	}
	session.closers = nil
}

func (session *Session) CreateCommand(command ...string) *exec.Cmd {
	var cmd *exec.Cmd
	if config.Current.GoConfig.UseLVE {
		cmd = exec.Command("/bin/lve_exec", command...)
	} else {
		cmd = exec.Command(command[0], command[1:]...)
	}
	cmd.Dir = session.Home
	cmd.Env = []string{
		"USER=" + session.User,
		"LOGNAME=" + session.User,
		"HOME=" + session.Home,
		"SHELL=/bin/bash",
		"TERM=xterm",
		"LANG=en_US.UTF-8",
		"PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:" + session.Home + "/bin",
	}
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Credential: &syscall.Credential{
			Uid: uint32(session.Uid),
			Gid: uint32(session.Gid),
		},
	}

	return cmd
}
<|endoftext|>


<commit_before>package rest

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"net/url"
	"socialapi/models"
	"socialapi/request"
	"strconv"
	"time"

	"github.com/google/go-querystring/query"
)

func GetHistory(channelId int64, q *request.Query, token string) (*models.HistoryResponse, error) {
	v, err := query.Values(q)
	if err != nil {
		return nil, err
	}

	url := fmt.Sprintf("/channel/%d/history?%s", channelId, v.Encode())
	res, err := sendRequestWithAuth("GET", url, nil, token)
	if err != nil {
		return nil, err
	}

	var history models.HistoryResponse
	err = json.Unmarshal(res, &history)
	if err != nil {
		return nil, err
	}

	return &history, nil
}

func CountHistory(channelId int64) (*models.CountResponse, error) {
	c := models.NewChannelMessageList()
	c.ChannelId = channelId

	url := fmt.Sprintf("/channel/%d/history/count", channelId)
	res, err := marshallAndSendRequest("GET", url, c)
	if err != nil {
		return nil, err
	}

	var count models.CountResponse
	err = json.Unmarshal(res, &count)
	if err != nil {
		return nil, err
	}

	return &count, nil
}

func FetchChannelsByQuery(accountId int64, q *request.Query) ([]*models.Channel, error) {
	v, err := query.Values(q)
	if err != nil {
		return nil, err
	}

	url := fmt.Sprintf("/account/%d/channels?%s", accountId, v.Encode())
	res, err := sendRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	var ccs []*models.ChannelContainer
	err = json.Unmarshal(res, &ccs)
	if err != nil {
		return nil, err
	}

	channels := make([]*models.Channel, len(ccs))
	for i, cc := range ccs {
		channels[i] = cc.Channel
	}

	return channels, nil
}

func FetchChannelByName(accountId int64, name, groupName, typeConstant, token string) (*models.Channel, error) {
	ccs, err := FetchChannelContainerByName(accountId, name, groupName, typeConstant, token)
	if err != nil {
		return nil, err
	}

	return ccs.Channel, nil
}

func FetchChannelContainerByName(accountId int64, name, groupName, typeConstant, token string) (*models.ChannelContainer, error) {
	url := fmt.Sprintf("/channel/name/%s?groupName=%s&type=%s&accountId=%d", name, groupName, typeConstant, accountId)
	// res, err := sendRequest("GET", url, nil)
	res, err := sendRequestWithAuth("GET", url, nil, token)
	if err != nil {
		return nil, err
	}

	ccs := &models.ChannelContainer{}
	err = json.Unmarshal(res, ccs)
	if err != nil {
		return nil, err
	}

	return ccs, nil
}

func FetchChannelsByParticipants(accountIds []int64, typeConstant, token string) ([]models.ChannelContainer, error) {
	v := url.Values{}
	v.Add("type", typeConstant)
	for _, id := range accountIds {
		v.Add("id", fmt.Sprintf("%d", id))
	}

	url := fmt.Sprintf("/channel/by/participants?%s", v.Encode())
	res, err := sendRequestWithAuth("GET", url, nil, token)
	if err != nil {
		return nil, err
	}

	var ccs []models.ChannelContainer
	if err := json.Unmarshal(res, &ccs); err != nil {
		return nil, err
	}

	return ccs, nil
}

func DeleteChannel(creatorId, channelId int64) error {
	c := models.NewChannel()
	c.CreatorId = creatorId
	c.Id = channelId

	url := fmt.Sprintf("/channel/%d/delete", channelId)
	_, err := sendModel("POST", url, c)
	if err != nil {
		return err
	}
	return nil
}

// buildChannelWithRandomGroup creates a channel with group name "koding[randonnumber]"
func buildChannelWithRandomGroup(creatorId int64) *models.Channel {
	c := models.NewChannel()
	rand.Seed(time.Now().UnixNano())
	c.GroupName = c.GroupName + strconv.Itoa(rand.Intn(100000000))

	return c
}

func CreateChannelByGroupNameAndType(creatorId int64, groupName, typeConstant, token string) (*models.Channel, error) {
	c := models.NewChannel()
	c.GroupName = groupName
	c.CreatorId = creatorId
	c.TypeConstant = typeConstant
	c.PrivacyConstant = models.Channel_PRIVACY_PUBLIC
	c.Name = c.Name + strconv.Itoa(rand.Intn(100000000))
	res, err := marshallAndSendRequestWithAuth("POST", "/channel", c, token)
	if err != nil {
		return nil, err
	}

	cc := models.NewChannelContainer()
	err = json.Unmarshal(res, cc)
	if err != nil {
		return nil, err
	}

	return cc.Channel, nil
}

func UpdateChannel(cm *models.Channel, token string) (*models.Channel, error) {
	url := fmt.Sprintf("/channel/%d/update", cm.Id)

	res, err := marshallAndSendRequestWithAuth("POST", url, cm, token)
	if err != nil {
		return nil, err
	}

	cc := models.NewChannelContainer()
	err = json.Unmarshal(res, cc)
	if err != nil {
		return nil, err
	}

	return cc.Channel, nil
}

func GetChannel(id int64) (*models.Channel, error) {
	cc, err := GetChannelContainer(id)
	if err != nil {
		return nil, err
	}

	return cc.Channel, nil
}

func GetChannelWithAccountId(id, accountId int64) (*models.Channel, error) {
	cc, err := GetChannelContainerWithAccountId(id, accountId)
	if err != nil {
		return nil, err
	}

	return cc.Channel, nil
}

func GetChannelContainer(id int64) (*models.ChannelContainer, error) {
	url := fmt.Sprintf("/channel/%d", id)
	cc := models.NewChannelContainer()
	cmI, err := sendModel("GET", url, cc)
	if err != nil {
		return nil, err
	}

	return cmI.(*models.ChannelContainer), nil
}

func GetChannelContainerWithAccountId(id, accountId int64) (*models.ChannelContainer, error) {
	url := fmt.Sprintf("/channel/%d?accountId=%d", id, accountId)
	cc := models.NewChannelContainer()
	cmI, err := sendModel("GET", url, cc)
	if err != nil {
		return nil, err
	}

	return cmI.(*models.ChannelContainer), nil
}

func SearchChannels(q *request.Query) ([]*models.Channel, error) {
	v, err := query.Values(q)
	if err != nil {
		return nil, err
	}

	url := fmt.Sprintf("/channel/search?%s", v.Encode())
	res, err := sendRequestWithAuth("GET", url, nil, "")
	if err != nil {
		return nil, err
	}

	channels := make([]*models.Channel, 0)
	err = json.Unmarshal(res, &channels)
	if err != nil {
		return nil, err
	}

	return channels, nil
}

func CreateGroupActivityChannel(creatorId int64, groupName string) (*models.Channel, error) {
	c := models.NewChannel()
	c.GroupName = groupName
	c.CreatorId = creatorId
	c.TypeConstant = models.Channel_TYPE_GROUP
	c.Name = groupName

	cm, err := sendModel("POST", "/channel", c)
	if err != nil {
		return nil, err
	}

	return cm.(*models.Channel), nil
}
<commit_msg>socialapi/rest: add token for channel get function<commit_after>package rest

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"net/url"
	"socialapi/models"
	"socialapi/request"
	"strconv"
	"time"

	"github.com/google/go-querystring/query"
)

func GetHistory(channelId int64, q *request.Query, token string) (*models.HistoryResponse, error) {
	v, err := query.Values(q)
	if err != nil {
		return nil, err
	}

	url := fmt.Sprintf("/channel/%d/history?%s", channelId, v.Encode())
	res, err := sendRequestWithAuth("GET", url, nil, token)
	if err != nil {
		return nil, err
	}

	var history models.HistoryResponse
	err = json.Unmarshal(res, &history)
	if err != nil {
		return nil, err
	}

	return &history, nil
}

func CountHistory(channelId int64) (*models.CountResponse, error) {
	c := models.NewChannelMessageList()
	c.ChannelId = channelId

	url := fmt.Sprintf("/channel/%d/history/count", channelId)
	res, err := marshallAndSendRequest("GET", url, c)
	if err != nil {
		return nil, err
	}

	var count models.CountResponse
	err = json.Unmarshal(res, &count)
	if err != nil {
		return nil, err
	}

	return &count, nil
}

func FetchChannelsByQuery(accountId int64, q *request.Query) ([]*models.Channel, error) {
	v, err := query.Values(q)
	if err != nil {
		return nil, err
	}

	url := fmt.Sprintf("/account/%d/channels?%s", accountId, v.Encode())
	res, err := sendRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	var ccs []*models.ChannelContainer
	err = json.Unmarshal(res, &ccs)
	if err != nil {
		return nil, err
	}

	channels := make([]*models.Channel, len(ccs))
	for i, cc := range ccs {
		channels[i] = cc.Channel
	}

	return channels, nil
}

func FetchChannelByName(accountId int64, name, groupName, typeConstant, token string) (*models.Channel, error) {
	ccs, err := FetchChannelContainerByName(accountId, name, groupName, typeConstant, token)
	if err != nil {
		return nil, err
	}

	return ccs.Channel, nil
}

func FetchChannelContainerByName(accountId int64, name, groupName, typeConstant, token string) (*models.ChannelContainer, error) {
	url := fmt.Sprintf("/channel/name/%s?groupName=%s&type=%s&accountId=%d", name, groupName, typeConstant, accountId)
	// res, err := sendRequest("GET", url, nil)
	res, err := sendRequestWithAuth("GET", url, nil, token)
	if err != nil {
		return nil, err
	}

	ccs := &models.ChannelContainer{}
	err = json.Unmarshal(res, ccs)
	if err != nil {
		return nil, err
	}

	return ccs, nil
}

func FetchChannelsByParticipants(accountIds []int64, typeConstant, token string) ([]models.ChannelContainer, error) {
	v := url.Values{}
	v.Add("type", typeConstant)
	for _, id := range accountIds {
		v.Add("id", fmt.Sprintf("%d", id))
	}

	url := fmt.Sprintf("/channel/by/participants?%s", v.Encode())
	res, err := sendRequestWithAuth("GET", url, nil, token)
	if err != nil {
		return nil, err
	}

	var ccs []models.ChannelContainer
	if err := json.Unmarshal(res, &ccs); err != nil {
		return nil, err
	}

	return ccs, nil
}

func DeleteChannel(creatorId, channelId int64) error {
	c := models.NewChannel()
	c.CreatorId = creatorId
	c.Id = channelId

	url := fmt.Sprintf("/channel/%d/delete", channelId)
	_, err := sendModel("POST", url, c)
	if err != nil {
		return err
	}
	return nil
}

// buildChannelWithRandomGroup creates a channel with group name "koding[randonnumber]"
func buildChannelWithRandomGroup(creatorId int64) *models.Channel {
	c := models.NewChannel()
	rand.Seed(time.Now().UnixNano())
	c.GroupName = c.GroupName + strconv.Itoa(rand.Intn(100000000))

	return c
}

func CreateChannelByGroupNameAndType(creatorId int64, groupName, typeConstant, token string) (*models.Channel, error) {
	c := models.NewChannel()
	c.GroupName = groupName
	c.CreatorId = creatorId
	c.TypeConstant = typeConstant
	c.PrivacyConstant = models.Channel_PRIVACY_PUBLIC
	c.Name = c.Name + strconv.Itoa(rand.Intn(100000000))
	res, err := marshallAndSendRequestWithAuth("POST", "/channel", c, token)
	if err != nil {
		return nil, err
	}

	cc := models.NewChannelContainer()
	err = json.Unmarshal(res, cc)
	if err != nil {
		return nil, err
	}

	return cc.Channel, nil
}

func UpdateChannel(cm *models.Channel, token string) (*models.Channel, error) {
	url := fmt.Sprintf("/channel/%d/update", cm.Id)

	res, err := marshallAndSendRequestWithAuth("POST", url, cm, token)
	if err != nil {
		return nil, err
	}

	cc := models.NewChannelContainer()
	err = json.Unmarshal(res, cc)
	if err != nil {
		return nil, err
	}

	return cc.Channel, nil
}

func GetChannel(id int64) (*models.Channel, error) {
	cc, err := GetChannelContainer(id)
	if err != nil {
		return nil, err
	}

	return cc.Channel, nil
}

// GetChannelWithToken gets the channel of the account with fivent account's session data.

func GetChannelWithToken(id int64, token string) (*models.Channel, error) {
	cc, err := GetChannelContainerWithToken(id, token)
	if err != nil {
		return nil, err
	}

	return cc.Channel, nil
}

func GetChannelContainerWithToken(id int64, token string) (*models.ChannelContainer, error) {
	url := fmt.Sprintf("/channel/%d", id)
	cc := models.NewChannelContainer()
	cmI, err := sendModelWithAuth("GET", url, cc, token)
	if err != nil {
		return nil, err
	}

	return cmI.(*models.ChannelContainer), nil
}

func GetChannelContainer(id int64) (*models.ChannelContainer, error) {
	url := fmt.Sprintf("/channel/%d", id)
	cc := models.NewChannelContainer()
	cmI, err := sendModel("GET", url, cc)
	if err != nil {
		return nil, err
	}

	return cmI.(*models.ChannelContainer), nil
}

func SearchChannels(q *request.Query) ([]*models.Channel, error) {
	v, err := query.Values(q)
	if err != nil {
		return nil, err
	}

	url := fmt.Sprintf("/channel/search?%s", v.Encode())
	res, err := sendRequestWithAuth("GET", url, nil, "")
	if err != nil {
		return nil, err
	}

	channels := make([]*models.Channel, 0)
	err = json.Unmarshal(res, &channels)
	if err != nil {
		return nil, err
	}

	return channels, nil
}

func CreateGroupActivityChannel(creatorId int64, groupName string) (*models.Channel, error) {
	c := models.NewChannel()
	c.GroupName = groupName
	c.CreatorId = creatorId
	c.TypeConstant = models.Channel_TYPE_GROUP
	c.Name = groupName

	cm, err := sendModel("POST", "/channel", c)
	if err != nil {
		return nil, err
	}

	return cm.(*models.Channel), nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strings"
	"time"
)

// WIDTH represents number of char allocated for time zone name.
const WIDTH = 16

// srv represents time server connection with chan for reading time.
type srv struct {
	name string
	addr string
	time chan string
	conn net.Conn
}

// servers represents list of time srv and methods to with all of them.
type servers []*srv

func (s servers) printTitle() {
	var buf bytes.Buffer

	for _, ts := range s {
		name := ts.name
		if len(name) > WIDTH {
			name = fmt.Sprintf("%s...", name[:WIDTH-3])
		}
		buf.WriteString(fmt.Sprintf("%*s|", WIDTH, name))
	}

	// Don't care about non-ascii names.
	rowLen := buf.Len()
	buf.WriteRune('\n')

	plusIndex := WIDTH
	for i := 0; i < rowLen; i++ {
		if i == plusIndex {
			buf.WriteRune('+')
			plusIndex += (WIDTH + 1)
		} else {
			buf.WriteRune('-')
		}
	}
	fmt.Println(buf.String())
}

func (s servers) printTime(sleep time.Duration) {
	for {
		time.Sleep(sleep)
		var time string
		for _, ts := range s {
			t, ok := <-ts.time
			if !ok {
				t = "DISABLED"
			}
			time += fmt.Sprintf("%*s|", WIDTH, t)
		}
		fmt.Printf("\r%s", time)
	}
}

func (s servers) startFetching() {
	for _, ts := range s {
		go func(server *srv) {
			defer server.conn.Close()
			defer close(server.time)

			reader := bufio.NewReader(server.conn)

			for {
				line, _, err := reader.ReadLine()
				if err == io.EOF {
					break
				}
				if err != nil {
					log.Fatal(err)
				}
				server.time <- string(line)
			}
		}(ts)
	}
}

func (s servers) dialAll() {
	for _, ts := range s {
		conn, err := net.Dial("tcp", ts.addr)
		if err != nil {
			log.Fatal(err)
		}
		ts.conn = conn
	}
}

func main() {
	timeServers := make(servers, 0, len(os.Args)-1)

	for _, param := range os.Args[1:] {
		args := strings.Split(param, "=")
		timeServers = append(timeServers,
			&srv{name: args[0], addr: args[1], time: make(chan string)})
	}

	timeServers.dialAll()
	timeServers.startFetching()
	timeServers.printTitle()
	timeServers.printTime(time.Second)
}
<commit_msg>[8.1] add final solution<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strings"
	"time"
)

// WIDTH represents number of char allocated for time zone name.
const WIDTH = 16

// srv represents time server connection with chan for reading time.
type srv struct {
	name string
	addr string
	time chan string
	conn net.Conn
}

// servers represents list of time srv and methods to with all of them.
type servers []*srv

func (s servers) printTitle() {
	var buf bytes.Buffer

	for _, ts := range s {
		name := ts.name
		if len(name) > WIDTH {
			name = fmt.Sprintf("%s...", name[:WIDTH-3])
		}
		buf.WriteString(fmt.Sprintf("%*s|", WIDTH, name))
	}

	// Don't care about non-ascii names.
	rowLen := buf.Len()
	buf.WriteRune('\n')

	plusIndex := WIDTH
	for i := 0; i < rowLen; i++ {
		if i == plusIndex {
			buf.WriteRune('+')
			plusIndex += (WIDTH + 1)
		} else {
			buf.WriteRune('-')
		}
	}
	fmt.Println(buf.String())
}

func (s servers) printTime(sleep time.Duration) {
	for {
		time.Sleep(sleep)

		var down int
		var time string

		for _, ts := range s {
			t, ok := <-ts.time
			if !ok {
				t = "DISCONNECTED"
				down++
			}
			time += fmt.Sprintf("%*s|", WIDTH, t)
		}
		fmt.Printf("\r%s", time)
		if down == len(s) {
			fmt.Fprintln(os.Stderr, "\nall time servers are down! exiting...")
			os.Exit(1)
		}

	}
}

func (s servers) startFetching() {
	for _, ts := range s {
		go func(server *srv) {
			defer server.conn.Close()
			defer close(server.time)

			reader := bufio.NewReader(server.conn)

			for {
				line, _, err := reader.ReadLine()
				if err == io.EOF {
					break
				}
				if err != nil {
					log.Fatal(err)
				}
				server.time <- string(line)
			}
		}(ts)
	}
}

func (s servers) dialAll() {
	for _, ts := range s {
		conn, err := net.Dial("tcp", ts.addr)
		if err != nil {
			log.Fatal(err)
		}
		ts.conn = conn
	}
}

func main() {
	timeServers := make(servers, 0, len(os.Args)-1)

	for _, param := range os.Args[1:] {
		args := strings.Split(param, "=")
		timeServers = append(timeServers,
			&srv{name: args[0], addr: args[1], time: make(chan string)})
	}

	timeServers.dialAll()
	timeServers.startFetching()
	timeServers.printTitle()
	timeServers.printTime(time.Second)
}
<|endoftext|>