Update cadvisor dependency.

Godep also decided to remove unused appengine dependency.
This commit is contained in:
Rohit Jnagal
2015-06-12 22:59:13 +00:00
parent b65c321a87
commit 6849b381dc
141 changed files with 487 additions and 29214 deletions

77
Godeps/Godeps.json generated
View File

@@ -241,88 +241,93 @@
},
{
"ImportPath": "github.com/google/cadvisor/api",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/cache/memory",
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/collector",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/container",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/events",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/fs",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/healthz",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/http",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/info/v1",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/info/v2",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/manager",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/metrics",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/pages",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/storage",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/summary",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/utils",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/validate",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/cadvisor/version",
"Comment": "0.14.0-9-ga96f2f9",
"Rev": "a96f2f93256f34a1f330728d146374e43c6c87ac"
"Comment": "0.15.1",
"Rev": "ec588def40e1bb59f28f5a293b279f6762d13d44"
},
{
"ImportPath": "github.com/google/go-github/github",
@@ -534,10 +539,6 @@
"ImportPath": "golang.org/x/oauth2",
"Rev": "b5adcc2dcdf009d0391547edc6ecbaff889f5bb9"
},
{
"ImportPath": "google.golang.org/appengine",
"Rev": "6aa67407028217c352e215f5af320a429d0bcf5f"
},
{
"ImportPath": "google.golang.org/cloud/compute/metadata",
"Rev": "2e43671e4ad874a7bca65746ff3edb38e6e93762"

View File

@@ -426,8 +426,7 @@ func convertStats(cont *info.ContainerInfo) []v2.ContainerStats {
stat.Memory = val.Memory
}
if stat.HasNetwork {
// TODO(rjnagal): Return stats about all network interfaces.
stat.Network = append(stat.Network, val.Network)
stat.Network.Interfaces = val.Network.Interfaces
}
if stat.HasFilesystem {
stat.Filesystem = val.Filesystem

View File

@@ -25,16 +25,16 @@ import (
"github.com/google/cadvisor/utils"
)
// TODO(vmarmol): See about refactoring this class, we have an unecessary redirection of containerStorage and InMemoryStorage.
// containerStorage is used to store per-container information
type containerStorage struct {
// TODO(vmarmol): See about refactoring this class, we have an unecessary redirection of containerCache and InMemoryCache.
// containerCache is used to store per-container information
type containerCache struct {
ref info.ContainerReference
recentStats *utils.TimedStore
maxAge time.Duration
lock sync.RWMutex
}
func (self *containerStorage) AddStats(stats *info.ContainerStats) error {
func (self *containerCache) AddStats(stats *info.ContainerStats) error {
self.lock.Lock()
defer self.lock.Unlock()
@@ -43,7 +43,7 @@ func (self *containerStorage) AddStats(stats *info.ContainerStats) error {
return nil
}
func (self *containerStorage) RecentStats(start, end time.Time, maxStats int) ([]*info.ContainerStats, error) {
func (self *containerCache) RecentStats(start, end time.Time, maxStats int) ([]*info.ContainerStats, error) {
self.lock.RLock()
defer self.lock.RUnlock()
result := self.recentStats.InTimeRange(start, end, maxStats)
@@ -54,31 +54,31 @@ func (self *containerStorage) RecentStats(start, end time.Time, maxStats int) ([
return converted, nil
}
func newContainerStore(ref info.ContainerReference, maxAge time.Duration) *containerStorage {
return &containerStorage{
func newContainerStore(ref info.ContainerReference, maxAge time.Duration) *containerCache {
return &containerCache{
ref: ref,
recentStats: utils.NewTimedStore(maxAge, -1),
maxAge: maxAge,
}
}
type InMemoryStorage struct {
lock sync.RWMutex
containerStorageMap map[string]*containerStorage
maxAge time.Duration
backend storage.StorageDriver
type InMemoryCache struct {
lock sync.RWMutex
containerCacheMap map[string]*containerCache
maxAge time.Duration
backend storage.StorageDriver
}
func (self *InMemoryStorage) AddStats(ref info.ContainerReference, stats *info.ContainerStats) error {
var cstore *containerStorage
func (self *InMemoryCache) AddStats(ref info.ContainerReference, stats *info.ContainerStats) error {
var cstore *containerCache
var ok bool
func() {
self.lock.Lock()
defer self.lock.Unlock()
if cstore, ok = self.containerStorageMap[ref.Name]; !ok {
if cstore, ok = self.containerCacheMap[ref.Name]; !ok {
cstore = newContainerStore(ref, self.maxAge)
self.containerStorageMap[ref.Name] = cstore
self.containerCacheMap[ref.Name] = cstore
}
}()
@@ -93,13 +93,13 @@ func (self *InMemoryStorage) AddStats(ref info.ContainerReference, stats *info.C
return cstore.AddStats(stats)
}
func (self *InMemoryStorage) RecentStats(name string, start, end time.Time, maxStats int) ([]*info.ContainerStats, error) {
var cstore *containerStorage
func (self *InMemoryCache) RecentStats(name string, start, end time.Time, maxStats int) ([]*info.ContainerStats, error) {
var cstore *containerCache
var ok bool
err := func() error {
self.lock.RLock()
defer self.lock.RUnlock()
if cstore, ok = self.containerStorageMap[name]; !ok {
if cstore, ok = self.containerCacheMap[name]; !ok {
return fmt.Errorf("unable to find data for container %v", name)
}
return nil
@@ -111,9 +111,16 @@ func (self *InMemoryStorage) RecentStats(name string, start, end time.Time, maxS
return cstore.RecentStats(start, end, maxStats)
}
func (self *InMemoryStorage) Close() error {
func (self *InMemoryCache) Close() error {
self.lock.Lock()
self.containerStorageMap = make(map[string]*containerStorage, 32)
self.containerCacheMap = make(map[string]*containerCache, 32)
self.lock.Unlock()
return nil
}
func (self *InMemoryCache) RemoveContainer(containerName string) error {
self.lock.Lock()
delete(self.containerCacheMap, containerName)
self.lock.Unlock()
return nil
}
@@ -121,11 +128,11 @@ func (self *InMemoryStorage) Close() error {
func New(
maxAge time.Duration,
backend storage.StorageDriver,
) *InMemoryStorage {
ret := &InMemoryStorage{
containerStorageMap: make(map[string]*containerStorage, 32),
maxAge: maxAge,
backend: backend,
) *InMemoryCache {
ret := &InMemoryCache{
containerCacheMap: make(map[string]*containerCache, 32),
maxAge: maxAge,
backend: backend,
}
return ret
}

View File

@@ -40,58 +40,58 @@ func makeStat(i int) *info.ContainerStats {
}
}
func getRecentStats(t *testing.T, memoryStorage *InMemoryStorage, numStats int) []*info.ContainerStats {
stats, err := memoryStorage.RecentStats(containerName, zero, zero, numStats)
func getRecentStats(t *testing.T, memoryCache *InMemoryCache, numStats int) []*info.ContainerStats {
stats, err := memoryCache.RecentStats(containerName, zero, zero, numStats)
require.Nil(t, err)
return stats
}
func TestAddStats(t *testing.T) {
memoryStorage := New(60*time.Second, nil)
memoryCache := New(60*time.Second, nil)
assert := assert.New(t)
assert.Nil(memoryStorage.AddStats(containerRef, makeStat(0)))
assert.Nil(memoryStorage.AddStats(containerRef, makeStat(1)))
assert.Nil(memoryStorage.AddStats(containerRef, makeStat(2)))
assert.Nil(memoryStorage.AddStats(containerRef, makeStat(0)))
assert.Nil(memoryCache.AddStats(containerRef, makeStat(0)))
assert.Nil(memoryCache.AddStats(containerRef, makeStat(1)))
assert.Nil(memoryCache.AddStats(containerRef, makeStat(2)))
assert.Nil(memoryCache.AddStats(containerRef, makeStat(0)))
containerRef2 := info.ContainerReference{
Name: "/container2",
}
assert.Nil(memoryStorage.AddStats(containerRef2, makeStat(0)))
assert.Nil(memoryStorage.AddStats(containerRef2, makeStat(1)))
assert.Nil(memoryCache.AddStats(containerRef2, makeStat(0)))
assert.Nil(memoryCache.AddStats(containerRef2, makeStat(1)))
}
func TestRecentStatsNoRecentStats(t *testing.T) {
memoryStorage := makeWithStats(0)
memoryCache := makeWithStats(0)
_, err := memoryStorage.RecentStats(containerName, zero, zero, 60)
_, err := memoryCache.RecentStats(containerName, zero, zero, 60)
assert.NotNil(t, err)
}
// Make an instance of InMemoryStorage with n stats.
func makeWithStats(n int) *InMemoryStorage {
memoryStorage := New(60*time.Second, nil)
// Make an instance of InMemoryCache with n stats.
func makeWithStats(n int) *InMemoryCache {
memoryCache := New(60*time.Second, nil)
for i := 0; i < n; i++ {
memoryStorage.AddStats(containerRef, makeStat(i))
memoryCache.AddStats(containerRef, makeStat(i))
}
return memoryStorage
return memoryCache
}
func TestRecentStatsGetZeroStats(t *testing.T) {
memoryStorage := makeWithStats(10)
memoryCache := makeWithStats(10)
assert.Len(t, getRecentStats(t, memoryStorage, 0), 0)
assert.Len(t, getRecentStats(t, memoryCache, 0), 0)
}
func TestRecentStatsGetSomeStats(t *testing.T) {
memoryStorage := makeWithStats(10)
memoryCache := makeWithStats(10)
assert.Len(t, getRecentStats(t, memoryStorage, 5), 5)
assert.Len(t, getRecentStats(t, memoryCache, 5), 5)
}
func TestRecentStatsGetAllStats(t *testing.T) {
memoryStorage := makeWithStats(10)
memoryCache := makeWithStats(10)
assert.Len(t, getRecentStats(t, memoryStorage, -1), 10)
assert.Len(t, getRecentStats(t, memoryCache, -1), 10)
}

View File

@@ -24,7 +24,6 @@ import (
"sync"
"github.com/docker/libcontainer/cgroups"
"github.com/docker/libcontainer/cgroups/systemd"
"github.com/fsouza/go-dockerclient"
"github.com/golang/glog"
"github.com/google/cadvisor/container"
@@ -55,16 +54,19 @@ var check = sync.Once{}
func UseSystemd() bool {
check.Do(func() {
// run and initialize useSystemd
useSystemd = systemd.UseSystemd()
if !useSystemd {
// Second attempt at checking for systemd, check for a "name=systemd" cgroup.
mnt, err := cgroups.FindCgroupMountpoint("cpu")
useSystemd = false
// Check for system.slice in systemd and cpu cgroup.
for _, cgroupType := range []string{"name=systemd", "cpu"} {
mnt, err := cgroups.FindCgroupMountpoint(cgroupType)
if err == nil {
// systemd presence does not mean systemd controls cgroups.
// If system.slice cgroup exists, then systemd is taking control.
// This breaks if user creates system.slice manually :)
useSystemd = utils.FileExists(mnt + "/system.slice")
if utils.FileExists(path.Join(mnt, "system.slice")) {
useSystemd = true
break
}
}
}
})

View File

@@ -261,17 +261,10 @@ func (self *dockerContainerHandler) GetStats() (*info.ContainerStats, error) {
}
// TODO(rjnagal): Remove the conversion when network stats are read from libcontainer.
net := stats.Network
// Ingress for host veth is from the container.
// Hence tx_bytes stat on the host veth is actually number of bytes received by the container.
stats.Network.RxBytes = net.TxBytes
stats.Network.RxPackets = net.TxPackets
stats.Network.RxErrors = net.TxErrors
stats.Network.RxDropped = net.TxDropped
stats.Network.TxBytes = net.RxBytes
stats.Network.TxPackets = net.RxPackets
stats.Network.TxErrors = net.RxErrors
stats.Network.TxDropped = net.RxDropped
convertInterfaceStats(&stats.Network.InterfaceStats)
for i := range stats.Network.Interfaces {
convertInterfaceStats(&stats.Network.Interfaces[i])
}
// Get filesystem stats.
err = self.getFsStats(stats)
@@ -282,6 +275,21 @@ func (self *dockerContainerHandler) GetStats() (*info.ContainerStats, error) {
return stats, nil
}
func convertInterfaceStats(stats *info.InterfaceStats) {
net := stats
// Ingress for host veth is from the container.
// Hence tx_bytes stat on the host veth is actually number of bytes received by the container.
stats.RxBytes = net.TxBytes
stats.RxPackets = net.TxPackets
stats.RxErrors = net.TxErrors
stats.RxDropped = net.TxDropped
stats.TxBytes = net.RxBytes
stats.TxPackets = net.RxPackets
stats.TxErrors = net.RxErrors
stats.TxDropped = net.RxDropped
}
func (self *dockerContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
if self.name != "/docker" {
return []info.ContainerReference{}, nil

View File

@@ -84,15 +84,20 @@ func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string) (*info.
}
stats := toContainerStats(libcontainerStats)
if len(networkInterfaces) != 0 {
// ContainerStats only reports stat for one network device.
// TODO(rjnagal): Handle multiple physical network devices.
// TODO(rjnagal): Use networking stats directly from libcontainer.
stats.Network, err = sysinfo.GetNetworkStats(networkInterfaces[0])
// TODO(rjnagal): Use networking stats directly from libcontainer.
stats.Network.Interfaces = make([]info.InterfaceStats, len(networkInterfaces))
for i := range networkInterfaces {
interfaceStats, err := sysinfo.GetNetworkStats(networkInterfaces[i])
if err != nil {
return stats, err
}
stats.Network.Interfaces[i] = interfaceStats
}
// For backwards compatability.
if len(networkInterfaces) > 0 {
stats.Network.InterfaceStats = stats.Network.Interfaces[0]
}
return stats, nil
}
@@ -213,15 +218,25 @@ func toContainerStats2(s *cgroups.Stats, ret *info.ContainerStats) {
}
func toContainerStats3(libcontainerStats *libcontainer.Stats, ret *info.ContainerStats) {
// TODO(vmarmol): Handle multiple interfaces.
ret.Network.RxBytes = libcontainerStats.Interfaces[0].RxBytes
ret.Network.RxPackets = libcontainerStats.Interfaces[0].RxPackets
ret.Network.RxErrors = libcontainerStats.Interfaces[0].RxErrors
ret.Network.RxDropped = libcontainerStats.Interfaces[0].RxDropped
ret.Network.TxBytes = libcontainerStats.Interfaces[0].TxBytes
ret.Network.TxPackets = libcontainerStats.Interfaces[0].TxPackets
ret.Network.TxErrors = libcontainerStats.Interfaces[0].TxErrors
ret.Network.TxDropped = libcontainerStats.Interfaces[0].TxDropped
ret.Network.Interfaces = make([]info.InterfaceStats, len(libcontainerStats.Interfaces))
for i := range libcontainerStats.Interfaces {
ret.Network.Interfaces[i] = info.InterfaceStats{
Name: libcontainerStats.Interfaces[i].Name,
RxBytes: libcontainerStats.Interfaces[i].RxBytes,
RxPackets: libcontainerStats.Interfaces[i].RxPackets,
RxErrors: libcontainerStats.Interfaces[i].RxErrors,
RxDropped: libcontainerStats.Interfaces[i].RxDropped,
TxBytes: libcontainerStats.Interfaces[i].TxBytes,
TxPackets: libcontainerStats.Interfaces[i].TxPackets,
TxErrors: libcontainerStats.Interfaces[i].TxErrors,
TxDropped: libcontainerStats.Interfaces[i].TxDropped,
}
}
// Add to base struct for backwards compatability.
if len(ret.Network.Interfaces) > 0 {
ret.Network.InterfaceStats = ret.Network.Interfaces[0]
}
}
func toContainerStats(libcontainerStats *libcontainer.Stats) *info.ContainerStats {

View File

@@ -305,15 +305,13 @@ func (self *rawContainerHandler) getFsStats(stats *info.ContainerStats) error {
}
func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) {
var networkInterfaces []string
nd, err := self.GetRootNetworkDevices()
if err != nil {
return new(info.ContainerStats), err
}
if len(nd) != 0 {
// ContainerStats only reports stat for one network device.
// TODO(rjnagal): Handle multiple physical network devices.
networkInterfaces = []string{nd[0].Name}
networkInterfaces := make([]string, len(nd))
for i := range nd {
networkInterfaces[i] = nd[i].Name
}
stats, err := libcontainer.GetStats(self.cgroupManager, networkInterfaces)
if err != nil {

View File

@@ -75,8 +75,13 @@ func NewFsInfo(context Context) (FsInfo, error) {
partitions := make(map[string]partition, 0)
fsInfo := &RealFsInfo{}
fsInfo.labels = make(map[string]string, 0)
supportedFsType := map[string]bool{
// all ext systems are checked through prefix.
"btrfs": true,
"xfs": true,
}
for _, mount := range mounts {
if !strings.HasPrefix(mount.Fstype, "ext") && mount.Fstype != "btrfs" {
if !strings.HasPrefix(mount.Fstype, "ext") && !supportedFsType[mount.Fstype] {
continue
}
// Avoid bind mounts.

View File

@@ -312,7 +312,9 @@ type MemoryStatsMemoryData struct {
Pgmajfault uint64 `json:"pgmajfault"`
}
type NetworkStats struct {
type InterfaceStats struct {
// The name of the interface.
Name string `json:"name"`
// Cumulative count of bytes received.
RxBytes uint64 `json:"rx_bytes"`
// Cumulative count of packets received.
@@ -331,6 +333,11 @@ type NetworkStats struct {
TxDropped uint64 `json:"tx_dropped"`
}
type NetworkStats struct {
InterfaceStats `json:",inline"`
Interfaces []InterfaceStats `json:"interfaces,omitempty"`
}
type FsStats struct {
// The block device name associated with the filesystem.
Device string `json:"device,omitempty"`

View File

@@ -72,6 +72,11 @@ type ContainerSpec struct {
HasMemory bool `json:"has_memory"`
Memory MemorySpec `json:"memory,omitempty"`
// Following resources have no associated spec, but are being isolated.
HasNetwork bool `json:"has_network"`
HasFilesystem bool `json:"has_filesystem"`
HasDiskIo bool `json:"has_diskio"`
}
type ContainerStats struct {
@@ -87,8 +92,8 @@ type ContainerStats struct {
HasMemory bool `json:"has_memory"`
Memory v1.MemoryStats `json:"memory,omitempty"`
// Network statistics
HasNetwork bool `json:"has_network"`
Network []v1.NetworkStats `json:"network,omitempty"`
HasNetwork bool `json:"has_network"`
Network NetworkStats `json:"network,omitempty"`
// Filesystem statistics
HasFilesystem bool `json:"has_filesystem"`
Filesystem []v1.FsStats `json:"filesystem,omitempty"`
@@ -184,3 +189,8 @@ type ProcessInfo struct {
CgroupPath string `json:"cgroup_path"`
Cmd string `json:"cmd"`
}
type NetworkStats struct {
// Network stats by interface.
Interfaces []v1.InterfaceStats `json:"interfaces,omitempty"`
}

View File

@@ -28,11 +28,11 @@ import (
"github.com/docker/docker/pkg/units"
"github.com/golang/glog"
"github.com/google/cadvisor/cache/memory"
"github.com/google/cadvisor/collector"
"github.com/google/cadvisor/container"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/info/v2"
"github.com/google/cadvisor/storage/memory"
"github.com/google/cadvisor/summary"
"github.com/google/cadvisor/utils/cpuload"
)
@@ -56,7 +56,7 @@ type containerInfo struct {
type containerData struct {
handler container.ContainerHandler
info containerInfo
memoryStorage *memory.InMemoryStorage
memoryCache *memory.InMemoryCache
lock sync.Mutex
loadReader cpuload.CpuLoadReader
summaryReader *summary.StatsSummary
@@ -81,6 +81,10 @@ func (c *containerData) Start() error {
}
func (c *containerData) Stop() error {
err := c.memoryCache.RemoveContainer(c.info.Name)
if err != nil {
return err
}
c.stop <- true
return nil
}
@@ -132,26 +136,22 @@ func (c *containerData) getCgroupPath(cgroups string) (string, error) {
return string(matches[1]), nil
}
func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) {
func (c *containerData) GetProcessList(cadvisorContainer string, inHostNamespace bool) ([]v2.ProcessInfo, error) {
// report all processes for root.
isRoot := c.info.Name == "/"
pidMap := map[int]bool{}
if !isRoot {
pids, err := c.handler.ListProcesses(container.ListSelf)
if err != nil {
return nil, err
}
for _, pid := range pids {
pidMap[pid] = true
}
}
// TODO(rjnagal): Take format as an option?
format := "user,pid,ppid,stime,pcpu,pmem,rss,vsz,stat,time,comm,cgroup"
args := []string{"-e", "-o", format}
args := []string{}
command := "ps"
if !inHostNamespace {
command = "/usr/sbin/chroot"
args = append(args, "/rootfs", "ps")
}
args = append(args, "-e", "-o", format)
expectedFields := 12
out, err := exec.Command("ps", args...).Output()
out, err := exec.Command(command, args...).Output()
if err != nil {
return nil, fmt.Errorf("failed to execute ps command: %v", err)
return nil, fmt.Errorf("failed to execute %q command: %v", command, err)
}
processes := []v2.ProcessInfo{}
lines := strings.Split(string(out), "\n")
@@ -187,15 +187,21 @@ func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) {
if err != nil {
return nil, fmt.Errorf("invalid virtual size %q: %v", fields[7], err)
}
cgroup, err := c.getCgroupPath(fields[11])
if err != nil {
return nil, fmt.Errorf("could not parse cgroup path from %q: %v", fields[10], err)
}
// Remove the ps command we just ran from cadvisor container.
// Not necessary, but makes the cadvisor page look cleaner.
if !inHostNamespace && cadvisorContainer == cgroup && fields[10] == "ps" {
continue
}
var cgroupPath string
if isRoot {
cgroupPath, err = c.getCgroupPath(fields[11])
if err != nil {
return nil, fmt.Errorf("could not parse cgroup path from %q: %v", fields[10], err)
}
cgroupPath = cgroup
}
if isRoot || pidMap[pid] == true {
if isRoot || c.info.Name == cgroup {
processes = append(processes, v2.ProcessInfo{
User: fields[0],
Pid: pid,
@@ -215,8 +221,8 @@ func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) {
return processes, nil
}
func newContainerData(containerName string, memoryStorage *memory.InMemoryStorage, handler container.ContainerHandler, loadReader cpuload.CpuLoadReader, logUsage bool, collectorManager collector.CollectorManager) (*containerData, error) {
if memoryStorage == nil {
func newContainerData(containerName string, memoryCache *memory.InMemoryCache, handler container.ContainerHandler, loadReader cpuload.CpuLoadReader, logUsage bool, collectorManager collector.CollectorManager) (*containerData, error) {
if memoryCache == nil {
return nil, fmt.Errorf("nil memory storage")
}
if handler == nil {
@@ -229,7 +235,7 @@ func newContainerData(containerName string, memoryStorage *memory.InMemoryStorag
cont := &containerData{
handler: handler,
memoryStorage: memoryStorage,
memoryCache: memoryCache,
housekeepingInterval: *HousekeepingInterval,
loadReader: loadReader,
logUsage: logUsage,
@@ -256,7 +262,7 @@ func newContainerData(containerName string, memoryStorage *memory.InMemoryStorag
func (self *containerData) nextHousekeeping(lastHousekeeping time.Time) time.Time {
if *allowDynamicHousekeeping {
var empty time.Time
stats, err := self.memoryStorage.RecentStats(self.info.Name, empty, empty, 2)
stats, err := self.memoryCache.RecentStats(self.info.Name, empty, empty, 2)
if err != nil {
if self.allowErrorLogging() {
glog.Warningf("Failed to get RecentStats(%q) while determining the next housekeeping: %v", self.info.Name, err)
@@ -311,7 +317,7 @@ func (c *containerData) housekeeping() {
if c.logUsage {
const numSamples = 60
var empty time.Time
stats, err := c.memoryStorage.RecentStats(c.info.Name, empty, empty, numSamples)
stats, err := c.memoryCache.RecentStats(c.info.Name, empty, empty, numSamples)
if err != nil {
if c.allowErrorLogging() {
glog.Infof("[%s] Failed to get recent stats for logging usage: %v", c.info.Name, err)
@@ -434,7 +440,7 @@ func (c *containerData) updateStats() error {
}
return err
}
err = c.memoryStorage.AddStats(ref, stats)
err = c.memoryCache.AddStats(ref, stats)
if err != nil {
return err
}

View File

@@ -22,11 +22,11 @@ import (
"testing"
"time"
"github.com/google/cadvisor/cache/memory"
"github.com/google/cadvisor/collector"
"github.com/google/cadvisor/container"
info "github.com/google/cadvisor/info/v1"
itest "github.com/google/cadvisor/info/v1/test"
"github.com/google/cadvisor/storage/memory"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -34,25 +34,25 @@ import (
const containerName = "/container"
// Create a containerData instance for a test.
func setupContainerData(t *testing.T, spec info.ContainerSpec) (*containerData, *container.MockContainerHandler, *memory.InMemoryStorage) {
func setupContainerData(t *testing.T, spec info.ContainerSpec) (*containerData, *container.MockContainerHandler, *memory.InMemoryCache) {
mockHandler := container.NewMockContainerHandler(containerName)
mockHandler.On("GetSpec").Return(
spec,
nil,
)
memoryStorage := memory.New(60, nil)
ret, err := newContainerData(containerName, memoryStorage, mockHandler, nil, false, &collector.FakeCollectorManager{})
memoryCache := memory.New(60, nil)
ret, err := newContainerData(containerName, memoryCache, mockHandler, nil, false, &collector.FakeCollectorManager{})
if err != nil {
t.Fatal(err)
}
return ret, mockHandler, memoryStorage
return ret, mockHandler, memoryCache
}
// Create a containerData instance for a test and add a default GetSpec mock.
func newTestContainerData(t *testing.T) (*containerData, *container.MockContainerHandler, *memory.InMemoryStorage) {
func newTestContainerData(t *testing.T) (*containerData, *container.MockContainerHandler, *memory.InMemoryCache) {
spec := itest.GenerateRandomContainerSpec(4)
ret, mockHandler, memoryStorage := setupContainerData(t, spec)
return ret, mockHandler, memoryStorage
ret, mockHandler, memoryCache := setupContainerData(t, spec)
return ret, mockHandler, memoryCache
}
func TestUpdateSubcontainers(t *testing.T) {
@@ -116,9 +116,9 @@ func TestUpdateSubcontainersWithErrorOnDeadContainer(t *testing.T) {
mockHandler.AssertExpectations(t)
}
func checkNumStats(t *testing.T, memoryStorage *memory.InMemoryStorage, numStats int) {
func checkNumStats(t *testing.T, memoryCache *memory.InMemoryCache, numStats int) {
var empty time.Time
stats, err := memoryStorage.RecentStats(containerName, empty, empty, -1)
stats, err := memoryCache.RecentStats(containerName, empty, empty, -1)
require.Nil(t, err)
assert.Len(t, stats, numStats)
}
@@ -127,7 +127,7 @@ func TestUpdateStats(t *testing.T) {
statsList := itest.GenerateRandomStats(1, 4, 1*time.Second)
stats := statsList[0]
cd, mockHandler, memoryStorage := newTestContainerData(t)
cd, mockHandler, memoryCache := newTestContainerData(t)
mockHandler.On("GetStats").Return(
stats,
nil,
@@ -138,7 +138,7 @@ func TestUpdateStats(t *testing.T) {
t.Fatal(err)
}
checkNumStats(t, memoryStorage, 1)
checkNumStats(t, memoryCache, 1)
mockHandler.AssertExpectations(t)
}

View File

@@ -62,7 +62,12 @@ func getClockSpeed(procInfo []byte) (uint64, error) {
// Fall back to /proc/cpuinfo
matches := CpuClockSpeedMHz.FindSubmatch(procInfo)
if len(matches) != 2 {
return 0, fmt.Errorf("could not detect clock speed from output: %q", string(procInfo))
//Check if we are running on Power systems which have a different format
CpuClockSpeedMHz, _ = regexp.Compile("clock\\t*: +([0-9]+.[0-9]+)MHz")
matches = CpuClockSpeedMHz.FindSubmatch(procInfo)
if len(matches) != 2 {
return 0, fmt.Errorf("could not detect clock speed from output: %q", string(procInfo))
}
}
speed, err := strconv.ParseFloat(string(matches[1]), 64)
if err != nil {

View File

@@ -18,6 +18,7 @@ package manager
import (
"flag"
"fmt"
"os"
"path"
"regexp"
"strconv"
@@ -27,6 +28,7 @@ import (
"github.com/docker/libcontainer/cgroups"
"github.com/golang/glog"
"github.com/google/cadvisor/cache/memory"
"github.com/google/cadvisor/collector"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/docker"
@@ -35,7 +37,6 @@ import (
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/info/v2"
"github.com/google/cadvisor/storage/memory"
"github.com/google/cadvisor/utils/cpuload"
"github.com/google/cadvisor/utils/oomparser"
"github.com/google/cadvisor/utils/sysfs"
@@ -113,8 +114,8 @@ type Manager interface {
}
// New takes a memory storage and returns a new manager.
func New(memoryStorage *memory.InMemoryStorage, sysfs sysfs.SysFs) (Manager, error) {
if memoryStorage == nil {
func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs) (Manager, error) {
if memoryCache == nil {
return nil, fmt.Errorf("manager requires memory storage")
}
@@ -130,12 +131,20 @@ func New(memoryStorage *memory.InMemoryStorage, sysfs sysfs.SysFs) (Manager, err
if err != nil {
return nil, err
}
// If cAdvisor was started with host's rootfs mounted, assume that its running
// in its own namespaces.
inHostNamespace := false
if _, err := os.Stat("/rootfs/proc"); os.IsNotExist(err) {
inHostNamespace = true
}
newManager := &manager{
containers: make(map[namespacedContainerName]*containerData),
quitChannels: make([]chan error, 0, 2),
memoryStorage: memoryStorage,
memoryCache: memoryCache,
fsInfo: fsInfo,
cadvisorContainer: selfContainer,
inHostNamespace: inHostNamespace,
startupTime: time.Now(),
}
@@ -169,12 +178,13 @@ type namespacedContainerName struct {
type manager struct {
containers map[namespacedContainerName]*containerData
containersLock sync.RWMutex
memoryStorage *memory.InMemoryStorage
memoryCache *memory.InMemoryCache
fsInfo fs.FsInfo
machineInfo info.MachineInfo
versionInfo info.VersionInfo
quitChannels []chan error
cadvisorContainer string
inHostNamespace bool
dockerContainersRegexp *regexp.Regexp
loadReader cpuload.CpuLoadReader
eventHandler events.EventManager
@@ -217,7 +227,7 @@ func (self *manager) Start() error {
// Watch for OOMs.
err = self.watchForNewOoms()
if err != nil {
glog.Errorf("Failed to start OOM watcher, will not get OOM events: %v", err)
glog.Warningf("Could not configure a source for OOM detection, disabling OOM events: %v", err)
}
// If there are no factories, don't start any housekeeping and serve the information we do have.
@@ -361,9 +371,12 @@ func (self *manager) GetContainerSpec(containerName string, options v2.RequestOp
func (self *manager) getV2Spec(cinfo *containerInfo) v2.ContainerSpec {
specV1 := self.getAdjustedSpec(cinfo)
specV2 := v2.ContainerSpec{
CreationTime: specV1.CreationTime,
HasCpu: specV1.HasCpu,
HasMemory: specV1.HasMemory,
CreationTime: specV1.CreationTime,
HasCpu: specV1.HasCpu,
HasMemory: specV1.HasMemory,
HasFilesystem: specV1.HasFilesystem,
HasNetwork: specV1.HasNetwork,
HasDiskIo: specV1.HasDiskIo,
}
if specV1.HasCpu {
specV2.Cpu.Limit = specV1.Cpu.Limit
@@ -409,7 +422,7 @@ func (self *manager) containerDataToContainerInfo(cont *containerData, query *in
return nil, err
}
stats, err := self.memoryStorage.RecentStats(cinfo.Name, query.Start, query.End, query.NumStats)
stats, err := self.memoryCache.RecentStats(cinfo.Name, query.Start, query.End, query.NumStats)
if err != nil {
return nil, err
}
@@ -594,7 +607,7 @@ func (self *manager) getRequestedContainers(containerName string, options v2.Req
func (self *manager) GetFsInfo(label string) ([]v2.FsInfo, error) {
var empty time.Time
// Get latest data from filesystems hanging off root container.
stats, err := self.memoryStorage.RecentStats("/", empty, empty, 1)
stats, err := self.memoryCache.RecentStats("/", empty, empty, 1)
if err != nil {
return nil, err
}
@@ -668,7 +681,7 @@ func (m *manager) GetProcessList(containerName string, options v2.RequestOptions
// TODO(rjnagal): handle count? Only if we can do count by type (eg. top 5 cpu users)
ps := []v2.ProcessInfo{}
for _, cont := range conts {
ps, err = cont.GetProcessList()
ps, err = cont.GetProcessList(m.cadvisorContainer, m.inHostNamespace)
if err != nil {
return nil, err
}
@@ -693,7 +706,7 @@ func (m *manager) createContainer(containerName string) error {
return err
}
logUsage := *logCadvisorUsage && containerName == m.cadvisorContainer
cont, err := newContainerData(containerName, m.memoryStorage, handler, m.loadReader, logUsage, collectorManager)
cont, err := newContainerData(containerName, m.memoryCache, handler, m.loadReader, logUsage, collectorManager)
if err != nil {
return err
}

View File

@@ -22,19 +22,19 @@ import (
"testing"
"time"
"github.com/google/cadvisor/cache/memory"
"github.com/google/cadvisor/collector"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/docker"
info "github.com/google/cadvisor/info/v1"
itest "github.com/google/cadvisor/info/v1/test"
"github.com/google/cadvisor/storage/memory"
"github.com/google/cadvisor/utils/sysfs/fakesysfs"
)
// TODO(vmarmol): Refactor these tests.
func createManagerAndAddContainers(
memoryStorage *memory.InMemoryStorage,
memoryCache *memory.InMemoryCache,
sysfs *fakesysfs.FakeSysFs,
containers []string,
f func(*container.MockContainerHandler),
@@ -42,9 +42,9 @@ func createManagerAndAddContainers(
) *manager {
container.ClearContainerHandlerFactories()
mif := &manager{
containers: make(map[namespacedContainerName]*containerData),
quitChannels: make([]chan error, 0, 2),
memoryStorage: memoryStorage,
containers: make(map[namespacedContainerName]*containerData),
quitChannels: make([]chan error, 0, 2),
memoryCache: memoryCache,
}
for _, name := range containers {
mockHandler := container.NewMockContainerHandler(name)
@@ -53,7 +53,7 @@ func createManagerAndAddContainers(
spec,
nil,
).Once()
cont, err := newContainerData(name, memoryStorage, mockHandler, nil, false, &collector.FakeCollectorManager{})
cont, err := newContainerData(name, memoryCache, mockHandler, nil, false, &collector.FakeCollectorManager{})
if err != nil {
t.Fatal(err)
}
@@ -82,10 +82,10 @@ func expectManagerWithContainers(containers []string, query *info.ContainerInfoR
infosMap[container] = itest.GenerateRandomContainerInfo(container, 4, query, 1*time.Second)
}
memoryStorage := memory.New(time.Duration(query.NumStats)*time.Second, nil)
memoryCache := memory.New(time.Duration(query.NumStats)*time.Second, nil)
sysfs := &fakesysfs.FakeSysFs{}
m := createManagerAndAddContainers(
memoryStorage,
memoryCache,
sysfs,
containers,
func(h *container.MockContainerHandler) {
@@ -95,7 +95,7 @@ func expectManagerWithContainers(containers []string, query *info.ContainerInfoR
t.Error(err)
}
for _, stat := range cinfo.Stats {
err = memoryStorage.AddStats(ref, stat)
err = memoryCache.AddStats(ref, stat)
if err != nil {
t.Error(err)
}

View File

@@ -57,14 +57,17 @@ func (p testSubcontainersInfoProvider) SubcontainersInfo(string, *info.Container
},
},
Network: info.NetworkStats{
RxBytes: 14,
RxPackets: 15,
RxErrors: 16,
RxDropped: 17,
TxBytes: 18,
TxPackets: 19,
TxErrors: 20,
TxDropped: 21,
InterfaceStats: info.InterfaceStats{
Name: "eth0",
RxBytes: 14,
RxPackets: 15,
RxErrors: 16,
RxDropped: 17,
TxBytes: 18,
TxPackets: 19,
TxErrors: 20,
TxDropped: 21,
},
},
Filesystem: []info.FsStats{
{

View File

@@ -192,6 +192,16 @@ const containersHtmlTemplate = `
<div class="panel-heading">
<h3 class="panel-title">Network</h3>
</div>
<div class="panel-body">
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="network-selection-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span id="network-selection-text"></span>
<span class="caret"></span>
</button>
<ul id="network-selection" class="dropdown-menu" role="menu" aria-labelledby="network-selection-dropdown">
</ul>
</div>
</div>
<div class="panel-body">
<h4>Throughput</h4>
<div id="network-bytes-chart"></div>

View File

@@ -375,12 +375,32 @@ function drawMemoryUsage(elementId, machineInfo, containerInfo) {
drawLineChart(titles, data, elementId, "Megabytes");
}
// Get the index of the interface with the specified name.
function getNetworkInterfaceIndex(interfaceName, interfaces) {
for (var i = 0; i < interfaces.length; i++) {
if (interfaces[i].name == interfaceName) {
return i;
}
}
return -1;
}
// Draw the graph for network tx/rx bytes.
function drawNetworkBytes(elementId, machineInfo, stats) {
if (stats.spec.has_network && !hasResource(stats, "network")) {
return;
}
// Get interface index.
var interfaceIndex = -1;
if (stats.stats.length > 0) {
interfaceIndex = getNetworkInterfaceIndex(window.cadvisor.network.interface, stats.stats[0].network.interfaces);
}
if (interfaceIndex < 0) {
console.log("Unable to find interface\"", interfaceName, "\" in ", stats.stats.network);
return;
}
var titles = ["Time", "Tx bytes", "Rx bytes"];
var data = [];
for (var i = 1; i < stats.stats.length; i++) {
@@ -390,8 +410,8 @@ function drawNetworkBytes(elementId, machineInfo, stats) {
var elements = [];
elements.push(cur.timestamp);
elements.push((cur.network.tx_bytes - prev.network.tx_bytes) / intervalInSec);
elements.push((cur.network.rx_bytes - prev.network.rx_bytes) / intervalInSec);
elements.push((cur.network.interfaces[interfaceIndex].tx_bytes - prev.network.interfaces[interfaceIndex].tx_bytes) / intervalInSec);
elements.push((cur.network.interfaces[interfaceIndex].rx_bytes - prev.network.interfaces[interfaceIndex].rx_bytes) / intervalInSec);
data.push(elements);
}
drawLineChart(titles, data, elementId, "Bytes per second");
@@ -403,6 +423,16 @@ function drawNetworkErrors(elementId, machineInfo, stats) {
return;
}
// Get interface index.
var interfaceIndex = -1;
if (stats.stats.length > 0) {
interfaceIndex = getNetworkInterfaceIndex(window.cadvisor.network.interface, stats.stats[0].network.interfaces);
}
if (interfaceIndex < 0) {
console.log("Unable to find interface\"", interfaceName, "\" in ", stats.stats.network);
return;
}
var titles = ["Time", "Tx", "Rx"];
var data = [];
for (var i = 1; i < stats.stats.length; i++) {
@@ -412,8 +442,8 @@ function drawNetworkErrors(elementId, machineInfo, stats) {
var elements = [];
elements.push(cur.timestamp);
elements.push((cur.network.tx_errors - prev.network.tx_errors) / intervalInSec);
elements.push((cur.network.rx_errors - prev.network.rx_errors) / intervalInSec);
elements.push((cur.network.interfaces[interfaceIndex].tx_errors - prev.network.interfaces[interfaceIndex].tx_errors) / intervalInSec);
elements.push((cur.network.interfaces[interfaceIndex].rx_errors - prev.network.interfaces[interfaceIndex].rx_errors) / intervalInSec);
data.push(elements);
}
drawLineChart(titles, data, elementId, "Errors per second");
@@ -478,7 +508,7 @@ function drawProcesses(isRoot, rootDir, processInfo) {
var titleTypes = ['string', 'number', 'number', 'string', 'number', 'number', 'number', 'number', 'string', 'string', 'string'];
var sortIndex = 4
if (isRoot) {
titles.push("Cgroup");
titles.push("Container");
titleTypes.push('string');
}
var data = []
@@ -618,6 +648,60 @@ function drawCharts(machineInfo, containerInfo) {
stepExecute(steps);
}
function setNetwork(interfaceName) {
$("#network-selection-text")
.empty()
.append($("<span>").text("Interface: "))
.append($("<b>").text(interfaceName));
window.cadvisor.network.interface = interfaceName;
// Draw the new stats.
refreshStats();
}
// Creates the network selection dropdown.
function startNetwork(selectionElement, containerInfo) {
if (!hasResource(containerInfo, "network") || containerInfo.stats.length == 0
|| !containerInfo.stats[0].network.interfaces || containerInfo.stats[0].network.interfaces.length == 0) {
return;
}
window.cadvisor.network = {};
window.cadvisor.network.interface = "";
// Add all interfaces to the dropdown.
var el = $("#" + selectionElement);
for (var i = 0; i < containerInfo.stats[0].network.interfaces.length; i++) {
var interfaceName = containerInfo.stats[0].network.interfaces[i].name;
el.append($("<li>")
.attr("role", "presentation")
.append($("<a>")
.attr("role", "menuitem")
.attr("tabindex", -1)
.click(setNetwork.bind(null, interfaceName))
.text(interfaceName)));
}
setNetwork(containerInfo.stats[0].network.interfaces[0].name);
}
// Refresh the stats on the page.
function refreshStats() {
var machineInfo = window.cadvisor.machineInfo;
getStats(window.cadvisor.rootDir, window.cadvisor.containerName, function(containerInfo){
if (window.cadvisor.firstRun) {
window.cadvisor.firstRun = false;
if (containerInfo.spec.has_filesystem) {
startFileSystemUsage("filesystem-usage", machineInfo, containerInfo);
}
if (containerInfo.spec.has_network) {
startNetwork("network-selection", containerInfo);
}
}
drawCharts(machineInfo, containerInfo);
});
}
// Executed when the page finishes loading.
function startPage(containerName, hasCpu, hasMemory, rootDir, isRoot) {
// Don't fetch data if we don't have any resource.
@@ -628,6 +712,8 @@ function startPage(containerName, hasCpu, hasMemory, rootDir, isRoot) {
window.charts = {};
window.cadvisor = {};
window.cadvisor.firstRun = true;
window.cadvisor.rootDir = rootDir;
window.cadvisor.containerName = containerName;
// Draw process information at start and refresh every 60s.
getProcessInfo(rootDir, containerName, function(processInfo) {
@@ -641,15 +727,9 @@ function startPage(containerName, hasCpu, hasMemory, rootDir, isRoot) {
// Get machine info, then get the stats every 1s.
getMachineInfo(rootDir, function(machineInfo) {
window.cadvisor.machineInfo = machineInfo;
setInterval(function() {
getStats(rootDir, containerName, function(containerInfo){
if (window.cadvisor.firstRun && containerInfo.spec.has_filesystem) {
window.cadvisor.firstRun = false;
startFileSystemUsage("filesystem-usage", machineInfo, containerInfo);
}
drawCharts(machineInfo, containerInfo);
});
refreshStats();
}, 1000);
});
}

View File

@@ -18,8 +18,10 @@ package static
import (
"fmt"
"mime"
"net/http"
"net/url"
"path"
)
const StaticResource = "/static/"
@@ -46,6 +48,12 @@ func HandleRequest(w http.ResponseWriter, u *url.URL) error {
return fmt.Errorf("unknown static resource %q", resource)
}
// Set Content-Type if we were able to detect it.
contentType := mime.TypeByExtension(path.Ext(resource))
if contentType != "" {
w.Header().Set("Content-Type", contentType)
}
_, err := w.Write([]byte(content))
return err
}

View File

@@ -15,10 +15,6 @@
package bigquery
import (
"fmt"
"strconv"
"time"
bigquery "code.google.com/p/google-api-go-client/bigquery/v2"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/storage"
@@ -248,135 +244,6 @@ func (self *bigqueryStorage) containerFilesystemStatsToRows(
return rows
}
func convertToUint64(v interface{}) (uint64, error) {
if v == nil {
return 0, nil
}
switch x := v.(type) {
case uint64:
return x, nil
case int:
if x < 0 {
return 0, fmt.Errorf("negative value: %v", x)
}
return uint64(x), nil
case int32:
if x < 0 {
return 0, fmt.Errorf("negative value: %v", x)
}
return uint64(x), nil
case int64:
if x < 0 {
return 0, fmt.Errorf("negative value: %v", x)
}
return uint64(x), nil
case float64:
if x < 0 {
return 0, fmt.Errorf("negative value: %v", x)
}
return uint64(x), nil
case uint32:
return uint64(x), nil
case string:
return strconv.ParseUint(x, 10, 64)
}
return 0, fmt.Errorf("unknown type")
}
func (self *bigqueryStorage) valuesToContainerStats(columns []string, values []interface{}) (*info.ContainerStats, error) {
stats := &info.ContainerStats{
Filesystem: make([]info.FsStats, 0),
}
var err error
for i, col := range columns {
v := values[i]
switch {
case col == colTimestamp:
if t, ok := v.(time.Time); ok {
stats.Timestamp = t
}
case col == colMachineName:
if m, ok := v.(string); ok {
if m != self.machineName {
return nil, fmt.Errorf("different machine")
}
} else {
return nil, fmt.Errorf("machine name field is not a string: %v", v)
}
// Cumulative Cpu Usage
case col == colCpuCumulativeUsage:
stats.Cpu.Usage.Total, err = convertToUint64(v)
// Cumulative Cpu used by the system
case col == colCpuCumulativeUsageSystem:
stats.Cpu.Usage.System, err = convertToUint64(v)
// Cumulative Cpu Usage in user mode
case col == colCpuCumulativeUsageUser:
stats.Cpu.Usage.User, err = convertToUint64(v)
// Memory Usage
case col == colMemoryUsage:
stats.Memory.Usage, err = convertToUint64(v)
// Working set size
case col == colMemoryWorkingSet:
stats.Memory.WorkingSet, err = convertToUint64(v)
// container page fault
case col == colMemoryContainerPgfault:
stats.Memory.ContainerData.Pgfault, err = convertToUint64(v)
// container major page fault
case col == colMemoryContainerPgmajfault:
stats.Memory.ContainerData.Pgmajfault, err = convertToUint64(v)
// hierarchical page fault
case col == colMemoryHierarchicalPgfault:
stats.Memory.HierarchicalData.Pgfault, err = convertToUint64(v)
// hierarchical major page fault
case col == colMemoryHierarchicalPgmajfault:
stats.Memory.HierarchicalData.Pgmajfault, err = convertToUint64(v)
case col == colRxBytes:
stats.Network.RxBytes, err = convertToUint64(v)
case col == colRxErrors:
stats.Network.RxErrors, err = convertToUint64(v)
case col == colTxBytes:
stats.Network.TxBytes, err = convertToUint64(v)
case col == colTxErrors:
stats.Network.TxErrors, err = convertToUint64(v)
case col == colFsDevice:
device, ok := v.(string)
if !ok {
return nil, fmt.Errorf("filesystem name field is not a string: %+v", v)
}
if len(stats.Filesystem) == 0 {
stats.Filesystem = append(stats.Filesystem, info.FsStats{Device: device})
} else {
stats.Filesystem[0].Device = device
}
case col == colFsLimit:
limit, err := convertToUint64(v)
if err != nil {
return nil, fmt.Errorf("filesystem limit field %+v invalid: %s", v, err)
}
if len(stats.Filesystem) == 0 {
stats.Filesystem = append(stats.Filesystem, info.FsStats{Limit: limit})
} else {
stats.Filesystem[0].Limit = limit
}
case col == colFsUsage:
usage, err := convertToUint64(v)
if err != nil {
return nil, fmt.Errorf("filesystem usage field %+v invalid: %s", v, err)
}
if len(stats.Filesystem) == 0 {
stats.Filesystem = append(stats.Filesystem, info.FsStats{Usage: usage})
} else {
stats.Filesystem[0].Usage = usage
}
}
if err != nil {
return nil, fmt.Errorf("column %v has invalid value %v: %v", col, v, err)
}
}
return stats, nil
}
func (self *bigqueryStorage) AddStats(ref info.ContainerReference, stats *info.ContainerStats) error {
if stats == nil {
return nil
@@ -393,42 +260,6 @@ func (self *bigqueryStorage) AddStats(ref info.ContainerReference, stats *info.C
return nil
}
func (self *bigqueryStorage) getRecentRows(containerName string, numRows int) ([]string, [][]interface{}, error) {
tableName, err := self.client.GetTableName()
if err != nil {
return nil, nil, err
}
query := fmt.Sprintf("SELECT * FROM %v WHERE %v='%v' and %v='%v'", tableName, colContainerName, containerName, colMachineName, self.machineName)
if numRows > 0 {
query = fmt.Sprintf("%v LIMIT %v", query, numRows)
}
return self.client.Query(query)
}
func (self *bigqueryStorage) RecentStats(containerName string, numStats int) ([]*info.ContainerStats, error) {
if numStats == 0 {
return nil, nil
}
header, rows, err := self.getRecentRows(containerName, numStats)
if err != nil {
return nil, err
}
statsList := make([]*info.ContainerStats, 0, len(rows))
for _, row := range rows {
stats, err := self.valuesToContainerStats(header, row)
if err != nil {
return nil, err
}
if stats == nil {
continue
}
statsList = append(statsList, stats)
}
return statsList, nil
}
func (self *bigqueryStorage) Close() error {
self.client.Close()
self.client = nil

View File

@@ -232,56 +232,3 @@ func (c *Client) InsertRow(rowData map[string]interface{}) error {
}
return nil
}
// Returns a bigtable table name (format: datasetID.tableID)
func (c *Client) GetTableName() (string, error) {
if c.service == nil || c.datasetId == "" || c.tableId == "" {
return "", fmt.Errorf("table not setup")
}
return fmt.Sprintf("%s.%s", c.datasetId, c.tableId), nil
}
// Do a synchronous query on bigtable and return a header and data rows.
// Number of rows are capped to queryLimit.
func (c *Client) Query(query string) ([]string, [][]interface{}, error) {
service, err := c.getService()
if err != nil {
return nil, nil, err
}
datasetRef := &bigquery.DatasetReference{
DatasetId: c.datasetId,
ProjectId: *projectId,
}
queryRequest := &bigquery.QueryRequest{
DefaultDataset: datasetRef,
MaxResults: queryLimit,
Kind: "json",
Query: query,
}
results, err := service.Jobs.Query(*projectId, queryRequest).Do()
if err != nil {
return nil, nil, err
}
numRows := results.TotalRows
if numRows < 1 {
return nil, nil, fmt.Errorf("query returned no data")
}
headers := []string{}
for _, col := range results.Schema.Fields {
headers = append(headers, col.Name)
}
rows := [][]interface{}{}
numColumns := len(results.Schema.Fields)
for _, data := range results.Rows {
row := make([]interface{}, numColumns)
for c := 0; c < numColumns; c++ {
row[c] = data.F[c].V
}
rows = append(rows, row)
}
return headers, rows, nil
}

View File

@@ -84,25 +84,4 @@ func main() {
panic(err)
}
}
// Query
tableName, err := c.GetTableName()
if err != nil {
fmt.Printf("table not set")
panic(err)
}
query := "SELECT * FROM " + tableName + " ORDER BY Timestamp LIMIT 100"
header, rows, err := c.Query(query)
if err != nil {
fmt.Printf("Failed query")
panic(err)
}
fmt.Printf("Headers: %v", header)
for _, row := range rows {
for i, val := range row {
fmt.Printf("%s:%v ", header[i], val)
}
fmt.Printf("\n")
}
}

View File

@@ -139,114 +139,6 @@ func (self *influxdbStorage) containerStatsToValues(
return columns, values
}
func convertToUint64(v interface{}) (uint64, error) {
if v == nil {
return 0, nil
}
switch x := v.(type) {
case uint64:
return x, nil
case int:
if x < 0 {
return 0, fmt.Errorf("negative value: %v", x)
}
return uint64(x), nil
case int32:
if x < 0 {
return 0, fmt.Errorf("negative value: %v", x)
}
return uint64(x), nil
case int64:
if x < 0 {
return 0, fmt.Errorf("negative value: %v", x)
}
return uint64(x), nil
case float64:
if x < 0 {
return 0, fmt.Errorf("negative value: %v", x)
}
return uint64(x), nil
case uint32:
return uint64(x), nil
}
return 0, fmt.Errorf("unknown type")
}
func (self *influxdbStorage) valuesToContainerStats(columns []string, values []interface{}) (*info.ContainerStats, error) {
stats := &info.ContainerStats{
Filesystem: make([]info.FsStats, 0),
}
var err error
for i, col := range columns {
v := values[i]
switch {
case col == colTimestamp:
if f64sec, ok := v.(float64); ok && stats.Timestamp.IsZero() {
stats.Timestamp = time.Unix(int64(f64sec)/1E3, (int64(f64sec)%1E3)*1E6)
}
case col == colMachineName:
if m, ok := v.(string); ok {
if m != self.machineName {
return nil, fmt.Errorf("different machine")
}
} else {
return nil, fmt.Errorf("machine name field is not a string: %v", v)
}
// Cumulative Cpu Usage
case col == colCpuCumulativeUsage:
stats.Cpu.Usage.Total, err = convertToUint64(v)
// Memory Usage
case col == colMemoryUsage:
stats.Memory.Usage, err = convertToUint64(v)
// Working set size
case col == colMemoryWorkingSet:
stats.Memory.WorkingSet, err = convertToUint64(v)
case col == colRxBytes:
stats.Network.RxBytes, err = convertToUint64(v)
case col == colRxErrors:
stats.Network.RxErrors, err = convertToUint64(v)
case col == colTxBytes:
stats.Network.TxBytes, err = convertToUint64(v)
case col == colTxErrors:
stats.Network.TxErrors, err = convertToUint64(v)
case col == colFsDevice:
device, ok := v.(string)
if !ok {
return nil, fmt.Errorf("filesystem name field is not a string: %+v", v)
}
if len(stats.Filesystem) == 0 {
stats.Filesystem = append(stats.Filesystem, info.FsStats{Device: device})
} else {
stats.Filesystem[0].Device = device
}
case col == colFsLimit:
limit, err := convertToUint64(v)
if err != nil {
return nil, fmt.Errorf("filesystem limit field %+v invalid: %s", v, err)
}
if len(stats.Filesystem) == 0 {
stats.Filesystem = append(stats.Filesystem, info.FsStats{Limit: limit})
} else {
stats.Filesystem[0].Limit = limit
}
case col == colFsUsage:
usage, err := convertToUint64(v)
if err != nil {
return nil, fmt.Errorf("filesystem usage field %+v invalid: %s", v, err)
}
if len(stats.Filesystem) == 0 {
stats.Filesystem = append(stats.Filesystem, info.FsStats{Usage: usage})
} else {
stats.Filesystem[0].Usage = usage
}
}
if err != nil {
return nil, fmt.Errorf("column %v has invalid value %v: %v", col, v, err)
}
}
return stats, nil
}
func (self *influxdbStorage) OverrideReadyToFlush(readyToFlush func() bool) {
self.readyToFlush = readyToFlush
}
@@ -283,42 +175,6 @@ func (self *influxdbStorage) AddStats(ref info.ContainerReference, stats *info.C
return nil
}
func (self *influxdbStorage) RecentStats(containerName string, numStats int) ([]*info.ContainerStats, error) {
if numStats == 0 {
return nil, nil
}
// TODO(dengnan): select only columns that we need
// TODO(dengnan): escape names
query := fmt.Sprintf("select * from %v where %v='%v' and %v='%v'", self.tableName, colContainerName, containerName, colMachineName, self.machineName)
if numStats > 0 {
query = fmt.Sprintf("%v limit %v", query, numStats)
}
series, err := self.client.Query(query)
if err != nil {
return nil, err
}
statsList := make([]*info.ContainerStats, 0, len(series))
// By default, influxDB returns data in time descending order.
// RecentStats() requires stats in time increasing order,
// so we need to go through from the last one to the first one.
for i := len(series) - 1; i >= 0; i-- {
s := series[i]
for j := len(s.Points) - 1; j >= 0; j-- {
values := s.Points[j]
stats, err := self.valuesToContainerStats(s.Columns, values)
if err != nil {
return nil, err
}
if stats == nil {
continue
}
statsList = append(statsList, stats)
}
}
return statsList, nil
}
func (self *influxdbStorage) Close() error {
self.client = nil
return nil

View File

@@ -51,7 +51,7 @@ func (self *influxDbTestStorageDriver) AddStats(ref info.ContainerReference, sta
}
func (self *influxDbTestStorageDriver) RecentStats(containerName string, numStats int) ([]*info.ContainerStats, error) {
return self.base.RecentStats(containerName, numStats)
return nil, nil
}
func (self *influxDbTestStorageDriver) Percentiles(containerName string, cpuUsagePercentiles []int, memUsagePercentiles []int) (*info.ContainerStatsPercentiles, error) {

View File

@@ -89,12 +89,6 @@ func (self *redisStorage) AddStats(ref info.ContainerReference, stats *info.Cont
return nil
}
// We just need to push the data to the redis, do not need to pull from the redis,
//so we do not override RecentStats()
func (self *redisStorage) RecentStats(containerName string, numStats int) ([]*info.ContainerStats, error) {
return nil, nil
}
func (self *redisStorage) Close() error {
return self.conn.Close()
}

View File

@@ -0,0 +1,73 @@
// Copyright 2015 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 statsd
import (
"fmt"
"net"
"github.com/golang/glog"
)
type Client struct {
HostPort string
conn net.Conn
}
func (self *Client) Open() error {
conn, err := net.Dial("udp", self.HostPort)
if err != nil {
glog.Errorf("failed to open udp connection to %q: %v", self.HostPort, err)
return err
}
self.conn = conn
return nil
}
func (self *Client) Close() {
self.conn.Close()
}
func (self *Client) UpdateGauge(name, value string) error {
stats := make(map[string]string)
val := fmt.Sprintf("%s|g", value)
stats[name] = val
if err := self.send(stats); err != nil {
return err
}
return nil
}
// Simple send to statsd daemon without sampling.
func (self *Client) send(data map[string]string) error {
for k, v := range data {
formatted := fmt.Sprintf("%s:%s", k, v)
_, err := fmt.Fprintf(self.conn, formatted)
if err != nil {
glog.V(3).Infof("failed to send data %q: %v", formatted, err)
// return on first error.
return err
}
}
return nil
}
func New(hostPort string) (*Client, error) {
client := Client{HostPort: hostPort}
if err := client.Open(); err != nil {
return nil, err
}
return &client, nil
}

View File

@@ -19,13 +19,6 @@ import info "github.com/google/cadvisor/info/v1"
type StorageDriver interface {
AddStats(ref info.ContainerReference, stats *info.ContainerStats) error
// Read most recent stats. numStats indicates max number of stats
// returned. The returned stats must be consecutive observed stats. If
// numStats < 0, then return all stats stored in the storage. The
// returned stats should be sorted in time increasing order, i.e. Most
// recent stats should be the last.
RecentStats(containerName string, numStats int) ([]*info.ContainerStats, error)
// Close will clear the state of the storage driver. The elements
// stored in the underlying storage may or may not be deleted depending
// on the implementation of the storage driver.

View File

@@ -29,11 +29,6 @@ func (self *MockStorageDriver) AddStats(ref info.ContainerReference, stats *info
return args.Error(0)
}
func (self *MockStorageDriver) RecentStats(containerName string, numStats int) ([]*info.ContainerStats, error) {
args := self.Called(containerName, numStats)
return args.Get(0).([]*info.ContainerStats), args.Error(1)
}
func (self *MockStorageDriver) Close() error {
if self.MockCloseMethod {
args := self.Called()

View File

@@ -147,126 +147,3 @@ func StorageDriverFillRandomStatsFunc(
}
}
}
func StorageDriverTestRetrievePartialRecentStats(driver TestStorageDriver, t *testing.T) {
defer driver.Close()
N := 100
memTrace := make([]uint64, N)
cpuTrace := make([]uint64, N)
for i := 0; i < N; i++ {
memTrace[i] = uint64(i + 1)
cpuTrace[i] = uint64(1)
}
ref := info.ContainerReference{
Name: "container",
}
trace := buildTrace(cpuTrace, memTrace, 1*time.Second)
for _, stats := range trace {
driver.AddStats(ref, stats)
}
recentStats, err := driver.RecentStats(ref.Name, 10)
if err != nil {
t.Fatal(err)
}
if len(recentStats) == 0 {
t.Fatal("should at least store one stats")
}
if len(recentStats) > 10 {
t.Fatalf("returned %v stats, not 10.", len(recentStats))
}
actualRecentStats := trace[len(trace)-len(recentStats):]
// The returned stats should be sorted in time increasing order
for i, s := range actualRecentStats {
r := recentStats[i]
if !driver.StatsEq(s, r) {
t.Errorf("unexpected stats %+v with memory usage %v; should be %+v", r, r.Memory.Usage, s)
}
}
}
func StorageDriverTestRetrieveAllRecentStats(driver TestStorageDriver, t *testing.T) {
defer driver.Close()
N := 100
memTrace := make([]uint64, N)
cpuTrace := make([]uint64, N)
for i := 0; i < N; i++ {
memTrace[i] = uint64(i + 1)
cpuTrace[i] = uint64(1)
}
ref := info.ContainerReference{
Name: "container",
}
trace := buildTrace(cpuTrace, memTrace, 1*time.Second)
for _, stats := range trace {
driver.AddStats(ref, stats)
}
recentStats, err := driver.RecentStats(ref.Name, -1)
if err != nil {
t.Fatal(err)
}
if len(recentStats) == 0 {
t.Fatal("should at least store one stats")
}
if len(recentStats) > N {
t.Fatalf("returned %v stats, not %d.", len(recentStats), N)
}
actualRecentStats := trace[len(trace)-len(recentStats):]
// The returned stats should be sorted in time increasing order
for i, s := range actualRecentStats {
r := recentStats[i]
if !driver.StatsEq(s, r) {
t.Errorf("unexpected stats %+v with memory usage %v", r, r.Memory.Usage)
}
}
}
func StorageDriverTestNoRecentStats(driver TestStorageDriver, t *testing.T) {
defer driver.Close()
nonExistContainer := "somerandomecontainer"
stats, _ := driver.RecentStats(nonExistContainer, -1)
if len(stats) > 0 {
t.Errorf("RecentStats() returns %v stats on non exist container", len(stats))
}
}
func StorageDriverTestRetrieveZeroRecentStats(driver TestStorageDriver, t *testing.T) {
defer driver.Close()
N := 100
memTrace := make([]uint64, N)
cpuTrace := make([]uint64, N)
for i := 0; i < N; i++ {
memTrace[i] = uint64(i + 1)
cpuTrace[i] = uint64(1)
}
ref := info.ContainerReference{
Name: "container",
}
trace := buildTrace(cpuTrace, memTrace, 1*time.Second)
for _, stats := range trace {
driver.AddStats(ref, stats)
}
recentStats, err := driver.RecentStats(ref.Name, 0)
if err != nil {
t.Fatal(err)
}
if len(recentStats) > 0 {
t.Errorf("RecentStats() returns %v stats when requests for 0 stats", len(recentStats))
}
}

View File

@@ -19,7 +19,7 @@ import (
)
// Manages a buffer of usage samples.
// This is similar to stats buffer in storage/memory.
// This is similar to stats buffer in cache/memory.
// The main difference is that we do not pre-allocate the buffer as most containers
// won't live that long.
type SamplesBuffer struct {

View File

@@ -24,10 +24,11 @@ import (
)
const (
blockDir = "/sys/block"
cacheDir = "/sys/devices/system/cpu/cpu"
netDir = "/sys/class/net"
dmiDir = "/sys/class/dmi"
blockDir = "/sys/block"
cacheDir = "/sys/devices/system/cpu/cpu"
netDir = "/sys/class/net"
dmiDir = "/sys/class/dmi"
ppcDevTree = "/proc/device-tree"
)
type CacheInfo struct {
@@ -235,7 +236,15 @@ func (self *realSysFs) GetCacheInfo(id int, name string) (CacheInfo, error) {
func (self *realSysFs) GetSystemUUID() (string, error) {
id, err := ioutil.ReadFile(path.Join(dmiDir, "id", "product_uuid"))
if err != nil {
return "", err
//If running on baremetal Power then UID is /proc/device-tree/system-id
id, err = ioutil.ReadFile(path.Join(ppcDevTree, "system-id"))
if err != nil {
//If running on a KVM guest on Power then UUID is /proc/device-tree/vm,uuid
id, err = ioutil.ReadFile(path.Join(ppcDevTree, "vm,uuid"))
if err != nil {
return "", err
}
}
}
return strings.TrimSpace(string(id)), nil
}

View File

@@ -157,19 +157,19 @@ func GetCacheInfo(sysFs sysfs.SysFs, id int) ([]sysfs.CacheInfo, error) {
return info, nil
}
func GetNetworkStats(name string) (info.NetworkStats, error) {
stats := info.NetworkStats{}
func GetNetworkStats(name string) (info.InterfaceStats, error) {
// TODO(rjnagal): Take syfs as an argument.
sysFs, err := sysfs.NewRealSysFs()
if err != nil {
return stats, err
return info.InterfaceStats{}, err
}
return getNetworkStats(name, sysFs)
}
func getNetworkStats(name string, sysFs sysfs.SysFs) (info.NetworkStats, error) {
stats := info.NetworkStats{}
func getNetworkStats(name string, sysFs sysfs.SysFs) (info.InterfaceStats, error) {
var stats info.InterfaceStats
var err error
stats.Name = name
stats.RxBytes, err = sysFs.GetNetworkStatValue(name, "rx_bytes")
if err != nil {
return stats, err

View File

@@ -110,7 +110,8 @@ func TestGetCacheInfo(t *testing.T) {
}
func TestGetNetworkStats(t *testing.T) {
expected_stats := info.NetworkStats{
expected_stats := info.InterfaceStats{
Name: "eth0",
RxBytes: 1024,
RxPackets: 1024,
RxErrors: 1024,

View File

@@ -15,4 +15,4 @@
package version
// Version of cAdvisor.
const VERSION = "0.14.0"
const VERSION = "0.15.1"

View File

@@ -1,14 +0,0 @@
language: go
go:
- 1.4
install:
- export GOPATH="$HOME/gopath"
- mkdir -p "$GOPATH/src/google.golang.org"
- mv "$TRAVIS_BUILD_DIR" "$GOPATH/src/google.golang.org/appengine"
- go get -v -t -d google.golang.org/appengine/...
script:
- go test -v google.golang.org/appengine/...
- go test -v -race google.golang.org/appengine/...

View File

@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -1,65 +0,0 @@
# Go App Engine for Managed VMs
[![Build Status](https://travis-ci.org/golang/appengine.svg)](https://travis-ci.org/golang/appengine)
This repository supports the Go runtime for Managed VMs on App Engine.
It provides APIs for interacting with App Engine services.
Its canonical import path is `google.golang.org/appengine`.
See https://cloud.google.com/appengine/docs/go/managed-vms/
for more information.
## Directory structure
The top level directory of this repository is the `appengine` package. It
contains the
basic types (e.g. `appengine.Context`) that are used across APIs. Specific API
packages are in subdirectories (e.g. `datastore`).
There is an `internal` subdirectory that contains service protocol buffers,
plus packages required for connectivity to make API calls. App Engine apps
should not directly import any package under `internal`.
## Updating a Go App Engine app
This section describes how to update a traditional Go App Engine app to run on Managed VMs.
### 1. Update YAML files
The `app.yaml` file (and YAML files for modules) should have these new lines added:
```
vm: true
manual_scaling:
instances: 1
```
See https://cloud.google.com/appengine/docs/go/modules/#Go_Instance_scaling_and_class for details.
### 2. Update import paths
The import paths for App Engine packages are now fully qualified, based at `google.golang.org/appengine`.
You will need to update your code to use import paths starting with that; for instance,
code importing `appengine/datastore` will now need to import `google.golang.org/appengine/datastore`.
You can do that manually, or by running this command to recursively update all Go source files in the current directory:
(may require GNU sed)
```
sed -i '/"appengine/{s,"appengine,"google.golang.org/appengine,;s,appengine_,appengine/,}' \
$(find . -name '*.go')
```
### 3. Update code using deprecated, removed or modified APIs
Most App Engine services are available with exactly the same API.
A few APIs were cleaned up, and some are not available yet.
This list summarises the differences:
* `appengine.Datacenter` now takes an `appengine.Context` argument.
* `datastore.PropertyLoadSaver` has been simplified to use slices in place of channels.
* `search.FieldLoadSaver` now handles document metadata.
* `taskqueue.QueueStats` no longer takes a maxTasks argument. That argument has been
deprecated and unused for a long time.
* `appengine/aetest`, `appengine/blobstore`, `appengine/cloudsql`
and `appengine/runtime` have not been ported yet.
* `appengine.BackendHostname` and `appengine.BackendInstance` were for the deprecated backends feature.
Use `appengine.ModuleHostname`and `appengine.ModuleName` instead.
* `appengine.IsCapabilityDisabled` and `appengine/capability` are obsolete.
* Most of `appengine/file` is deprecated. Use [Google Cloud Storage](https://godoc.org/google.golang.org/cloud/storage) instead.
* `appengine/socket` is deprecated. Use the standard `net` package instead.

View File

@@ -1,78 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// Package appengine provides basic functionality for Google App Engine.
//
// For more information on how to write Go apps for Google App Engine, see:
// https://cloud.google.com/appengine/docs/go/
package appengine
import (
"net/http"
"github.com/golang/protobuf/proto"
"google.golang.org/appengine/internal"
)
// IsDevAppServer reports whether the App Engine app is running in the
// development App Server.
func IsDevAppServer() bool {
// TODO(dsymonds): Detect this.
return false
}
// Context represents the context of an in-flight HTTP request.
type Context interface {
// Debugf formats its arguments according to the format, analogous to fmt.Printf,
// and records the text as a log message at Debug level.
Debugf(format string, args ...interface{})
// Infof is like Debugf, but at Info level.
Infof(format string, args ...interface{})
// Warningf is like Debugf, but at Warning level.
Warningf(format string, args ...interface{})
// Errorf is like Debugf, but at Error level.
Errorf(format string, args ...interface{})
// Criticalf is like Debugf, but at Critical level.
Criticalf(format string, args ...interface{})
// The remaining methods are for internal use only.
// Developer-facing APIs wrap these methods to provide a more friendly API.
// Internal use only.
Call(service, method string, in, out proto.Message, opts *internal.CallOptions) error
// Internal use only. Use AppID instead.
FullyQualifiedAppID() string
// Internal use only.
Request() interface{}
}
// NewContext returns a context for an in-flight HTTP request.
// Repeated calls will return the same value.
func NewContext(req *http.Request) Context {
return internal.NewContext(req)
}
// TODO(dsymonds): Add BackgroundContext function?
// BlobKey is a key for a blobstore blob.
//
// Conceptually, this type belongs in the blobstore package, but it lives in
// the appengine package to avoid a circular dependency: blobstore depends on
// datastore, and datastore needs to refer to the BlobKey type.
type BlobKey string
// GeoPoint represents a location as latitude/longitude in degrees.
type GeoPoint struct {
Lat, Lng float64
}
// Valid returns whether a GeoPoint is within [-90, 90] latitude and [-180, 180] longitude.
func (g GeoPoint) Valid() bool {
return -90 <= g.Lat && g.Lat <= 90 && -180 <= g.Lng && g.Lng <= 180
}

View File

@@ -1,45 +0,0 @@
package appengine
import (
"testing"
)
func TestValidGeoPoint(t *testing.T) {
testCases := []struct {
desc string
pt GeoPoint
want bool
}{
{
"valid",
GeoPoint{67.21, 13.37},
true,
},
{
"high lat",
GeoPoint{-90.01, 13.37},
false,
},
{
"low lat",
GeoPoint{90.01, 13.37},
false,
},
{
"high lng",
GeoPoint{67.21, 182},
false,
},
{
"low lng",
GeoPoint{67.21, -181},
false,
},
}
for _, tc := range testCases {
if got := tc.pt.Valid(); got != tc.want {
t.Errorf("%s: got %v, want %v", tc.desc, got, tc.want)
}
}
}

View File

@@ -1,81 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package channel implements the server side of App Engine's Channel API.
Create creates a new channel associated with the given clientID,
which must be unique to the client that will use the returned token.
token, err := channel.Create(c, "player1")
if err != nil {
// handle error
}
// return token to the client in an HTTP response
Send sends a message to the client over the channel identified by clientID.
channel.Send(c, "player1", "Game over!")
*/
package channel
import (
"encoding/json"
"google.golang.org/appengine"
"google.golang.org/appengine/internal"
basepb "google.golang.org/appengine/internal/base"
pb "google.golang.org/appengine/internal/channel"
)
// Create creates a channel and returns a token for use by the client.
// The clientID is an application-provided string used to identify the client.
func Create(c appengine.Context, clientID string) (token string, err error) {
req := &pb.CreateChannelRequest{
ApplicationKey: &clientID,
}
resp := &pb.CreateChannelResponse{}
err = c.Call(service, "CreateChannel", req, resp, nil)
token = resp.GetToken()
return token, remapError(err)
}
// Send sends a message on the channel associated with clientID.
func Send(c appengine.Context, clientID, message string) error {
req := &pb.SendMessageRequest{
ApplicationKey: &clientID,
Message: &message,
}
resp := &basepb.VoidProto{}
return remapError(c.Call(service, "SendChannelMessage", req, resp, nil))
}
// SendJSON is a helper function that sends a JSON-encoded value
// on the channel associated with clientID.
func SendJSON(c appengine.Context, clientID string, value interface{}) error {
m, err := json.Marshal(value)
if err != nil {
return err
}
return Send(c, clientID, string(m))
}
// remapError fixes any APIError referencing "xmpp" into one referencing "channel".
func remapError(err error) error {
if e, ok := err.(*internal.APIError); ok {
if e.Service == "xmpp" {
e.Service = "channel"
}
}
return err
}
var service = "xmpp" // prod
func init() {
if appengine.IsDevAppServer() {
service = "channel" // dev
}
internal.RegisterErrorCodeMap("channel", pb.ChannelServiceError_ErrorCode_name)
}

View File

@@ -1,17 +0,0 @@
package channel
import (
"testing"
"google.golang.org/appengine/internal"
)
func TestRemapError(t *testing.T) {
err := &internal.APIError{
Service: "xmpp",
}
err = remapError(err).(*internal.APIError)
if err.Service != "channel" {
t.Errorf("err.Service = %q, want %q", err.Service, "channel")
}
}

View File

@@ -1,405 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package datastore
import (
"errors"
"fmt"
"reflect"
"github.com/golang/protobuf/proto"
"google.golang.org/appengine"
"google.golang.org/appengine/internal"
pb "google.golang.org/appengine/internal/datastore"
)
var (
// ErrInvalidEntityType is returned when functions like Get or Next are
// passed a dst or src argument of invalid type.
ErrInvalidEntityType = errors.New("datastore: invalid entity type")
// ErrInvalidKey is returned when an invalid key is presented.
ErrInvalidKey = errors.New("datastore: invalid key")
// ErrNoSuchEntity is returned when no entity was found for a given key.
ErrNoSuchEntity = errors.New("datastore: no such entity")
)
// ErrFieldMismatch is returned when a field is to be loaded into a different
// type than the one it was stored from, or when a field is missing or
// unexported in the destination struct.
// StructType is the type of the struct pointed to by the destination argument
// passed to Get or to Iterator.Next.
type ErrFieldMismatch struct {
StructType reflect.Type
FieldName string
Reason string
}
func (e *ErrFieldMismatch) Error() string {
return fmt.Sprintf("datastore: cannot load field %q into a %q: %s",
e.FieldName, e.StructType, e.Reason)
}
// protoToKey converts a Reference proto to a *Key.
func protoToKey(r *pb.Reference) (k *Key, err error) {
appID := r.GetApp()
namespace := r.GetNameSpace()
for _, e := range r.Path.Element {
k = &Key{
kind: e.GetType(),
stringID: e.GetName(),
intID: e.GetId(),
parent: k,
appID: appID,
namespace: namespace,
}
if !k.valid() {
return nil, ErrInvalidKey
}
}
return
}
// keyToProto converts a *Key to a Reference proto.
func keyToProto(defaultAppID string, k *Key) *pb.Reference {
appID := k.appID
if appID == "" {
appID = defaultAppID
}
n := 0
for i := k; i != nil; i = i.parent {
n++
}
e := make([]*pb.Path_Element, n)
for i := k; i != nil; i = i.parent {
n--
e[n] = &pb.Path_Element{
Type: &i.kind,
}
// At most one of {Name,Id} should be set.
// Neither will be set for incomplete keys.
if i.stringID != "" {
e[n].Name = &i.stringID
} else if i.intID != 0 {
e[n].Id = &i.intID
}
}
var namespace *string
if k.namespace != "" {
namespace = proto.String(k.namespace)
}
return &pb.Reference{
App: proto.String(appID),
NameSpace: namespace,
Path: &pb.Path{
Element: e,
},
}
}
// multiKeyToProto is a batch version of keyToProto.
func multiKeyToProto(appID string, key []*Key) []*pb.Reference {
ret := make([]*pb.Reference, len(key))
for i, k := range key {
ret[i] = keyToProto(appID, k)
}
return ret
}
// multiValid is a batch version of Key.valid. It returns an error, not a
// []bool.
func multiValid(key []*Key) error {
invalid := false
for _, k := range key {
if !k.valid() {
invalid = true
break
}
}
if !invalid {
return nil
}
err := make(appengine.MultiError, len(key))
for i, k := range key {
if !k.valid() {
err[i] = ErrInvalidKey
}
}
return err
}
// It's unfortunate that the two semantically equivalent concepts pb.Reference
// and pb.PropertyValue_ReferenceValue aren't the same type. For example, the
// two have different protobuf field numbers.
// referenceValueToKey is the same as protoToKey except the input is a
// PropertyValue_ReferenceValue instead of a Reference.
func referenceValueToKey(r *pb.PropertyValue_ReferenceValue) (k *Key, err error) {
appID := r.GetApp()
namespace := r.GetNameSpace()
for _, e := range r.Pathelement {
k = &Key{
kind: e.GetType(),
stringID: e.GetName(),
intID: e.GetId(),
parent: k,
appID: appID,
namespace: namespace,
}
if !k.valid() {
return nil, ErrInvalidKey
}
}
return
}
// keyToReferenceValue is the same as keyToProto except the output is a
// PropertyValue_ReferenceValue instead of a Reference.
func keyToReferenceValue(defaultAppID string, k *Key) *pb.PropertyValue_ReferenceValue {
ref := keyToProto(defaultAppID, k)
pe := make([]*pb.PropertyValue_ReferenceValue_PathElement, len(ref.Path.Element))
for i, e := range ref.Path.Element {
pe[i] = &pb.PropertyValue_ReferenceValue_PathElement{
Type: e.Type,
Id: e.Id,
Name: e.Name,
}
}
return &pb.PropertyValue_ReferenceValue{
App: ref.App,
NameSpace: ref.NameSpace,
Pathelement: pe,
}
}
type multiArgType int
const (
multiArgTypeInvalid multiArgType = iota
multiArgTypePropertyLoadSaver
multiArgTypeStruct
multiArgTypeStructPtr
multiArgTypeInterface
)
// checkMultiArg checks that v has type []S, []*S, []I, or []P, for some struct
// type S, for some interface type I, or some non-interface non-pointer type P
// such that P or *P implements PropertyLoadSaver.
//
// It returns what category the slice's elements are, and the reflect.Type
// that represents S, I or P.
//
// As a special case, PropertyList is an invalid type for v.
func checkMultiArg(v reflect.Value) (m multiArgType, elemType reflect.Type) {
if v.Kind() != reflect.Slice {
return multiArgTypeInvalid, nil
}
if v.Type() == typeOfPropertyList {
return multiArgTypeInvalid, nil
}
elemType = v.Type().Elem()
if reflect.PtrTo(elemType).Implements(typeOfPropertyLoadSaver) {
return multiArgTypePropertyLoadSaver, elemType
}
switch elemType.Kind() {
case reflect.Struct:
return multiArgTypeStruct, elemType
case reflect.Interface:
return multiArgTypeInterface, elemType
case reflect.Ptr:
elemType = elemType.Elem()
if elemType.Kind() == reflect.Struct {
return multiArgTypeStructPtr, elemType
}
}
return multiArgTypeInvalid, nil
}
// Get loads the entity stored for k into dst, which must be a struct pointer
// or implement PropertyLoadSaver. If there is no such entity for the key, Get
// returns ErrNoSuchEntity.
//
// The values of dst's unmatched struct fields are not modified, and matching
// slice-typed fields are not reset before appending to them. In particular, it
// is recommended to pass a pointer to a zero valued struct on each Get call.
//
// ErrFieldMismatch is returned when a field is to be loaded into a different
// type than the one it was stored from, or when a field is missing or
// unexported in the destination struct. ErrFieldMismatch is only returned if
// dst is a struct pointer.
func Get(c appengine.Context, key *Key, dst interface{}) error {
if dst == nil { // GetMulti catches nil interface; we need to catch nil ptr here
return ErrInvalidEntityType
}
err := GetMulti(c, []*Key{key}, []interface{}{dst})
if me, ok := err.(appengine.MultiError); ok {
return me[0]
}
return err
}
// GetMulti is a batch version of Get.
//
// dst must be a []S, []*S, []I or []P, for some struct type S, some interface
// type I, or some non-interface non-pointer type P such that P or *P
// implements PropertyLoadSaver. If an []I, each element must be a valid dst
// for Get: it must be a struct pointer or implement PropertyLoadSaver.
//
// As a special case, PropertyList is an invalid type for dst, even though a
// PropertyList is a slice of structs. It is treated as invalid to avoid being
// mistakenly passed when []PropertyList was intended.
func GetMulti(c appengine.Context, key []*Key, dst interface{}) error {
v := reflect.ValueOf(dst)
multiArgType, _ := checkMultiArg(v)
if multiArgType == multiArgTypeInvalid {
return errors.New("datastore: dst has invalid type")
}
if len(key) != v.Len() {
return errors.New("datastore: key and dst slices have different length")
}
if len(key) == 0 {
return nil
}
if err := multiValid(key); err != nil {
return err
}
req := &pb.GetRequest{
Key: multiKeyToProto(c.FullyQualifiedAppID(), key),
}
res := &pb.GetResponse{}
if err := c.Call("datastore_v3", "Get", req, res, nil); err != nil {
return err
}
if len(key) != len(res.Entity) {
return errors.New("datastore: internal error: server returned the wrong number of entities")
}
multiErr, any := make(appengine.MultiError, len(key)), false
for i, e := range res.Entity {
if e.Entity == nil {
multiErr[i] = ErrNoSuchEntity
} else {
elem := v.Index(i)
if multiArgType == multiArgTypePropertyLoadSaver || multiArgType == multiArgTypeStruct {
elem = elem.Addr()
}
if multiArgType == multiArgTypeStructPtr && elem.IsNil() {
elem.Set(reflect.New(elem.Type().Elem()))
}
multiErr[i] = loadEntity(elem.Interface(), e.Entity)
}
if multiErr[i] != nil {
any = true
}
}
if any {
return multiErr
}
return nil
}
// Put saves the entity src into the datastore with key k. src must be a struct
// pointer or implement PropertyLoadSaver; if a struct pointer then any
// unexported fields of that struct will be skipped. If k is an incomplete key,
// the returned key will be a unique key generated by the datastore.
func Put(c appengine.Context, key *Key, src interface{}) (*Key, error) {
k, err := PutMulti(c, []*Key{key}, []interface{}{src})
if err != nil {
if me, ok := err.(appengine.MultiError); ok {
return nil, me[0]
}
return nil, err
}
return k[0], nil
}
// PutMulti is a batch version of Put.
//
// src must satisfy the same conditions as the dst argument to GetMulti.
func PutMulti(c appengine.Context, key []*Key, src interface{}) ([]*Key, error) {
v := reflect.ValueOf(src)
multiArgType, _ := checkMultiArg(v)
if multiArgType == multiArgTypeInvalid {
return nil, errors.New("datastore: src has invalid type")
}
if len(key) != v.Len() {
return nil, errors.New("datastore: key and src slices have different length")
}
if len(key) == 0 {
return nil, nil
}
appID := c.FullyQualifiedAppID()
if err := multiValid(key); err != nil {
return nil, err
}
req := &pb.PutRequest{}
for i := range key {
elem := v.Index(i)
if multiArgType == multiArgTypePropertyLoadSaver || multiArgType == multiArgTypeStruct {
elem = elem.Addr()
}
sProto, err := saveEntity(appID, key[i], elem.Interface())
if err != nil {
return nil, err
}
req.Entity = append(req.Entity, sProto)
}
res := &pb.PutResponse{}
if err := c.Call("datastore_v3", "Put", req, res, nil); err != nil {
return nil, err
}
if len(key) != len(res.Key) {
return nil, errors.New("datastore: internal error: server returned the wrong number of keys")
}
ret := make([]*Key, len(key))
for i := range ret {
var err error
ret[i], err = protoToKey(res.Key[i])
if err != nil || ret[i].Incomplete() {
return nil, errors.New("datastore: internal error: server returned an invalid key")
}
}
return ret, nil
}
// Delete deletes the entity for the given key.
func Delete(c appengine.Context, key *Key) error {
err := DeleteMulti(c, []*Key{key})
if me, ok := err.(appengine.MultiError); ok {
return me[0]
}
return err
}
// DeleteMulti is a batch version of Delete.
func DeleteMulti(c appengine.Context, key []*Key) error {
if len(key) == 0 {
return nil
}
if err := multiValid(key); err != nil {
return err
}
req := &pb.DeleteRequest{
Key: multiKeyToProto(c.FullyQualifiedAppID(), key),
}
res := &pb.DeleteResponse{}
return c.Call("datastore_v3", "Delete", req, res, nil)
}
func namespaceMod(m proto.Message, namespace string) {
// pb.Query is the only type that has a name_space field.
// All other namespace support in datastore is in the keys.
switch m := m.(type) {
case *pb.Query:
if m.NameSpace == nil {
m.NameSpace = &namespace
}
}
}
func init() {
internal.NamespaceMods["datastore_v3"] = namespaceMod
internal.RegisterErrorCodeMap("datastore_v3", pb.Error_ErrorCode_name)
internal.RegisterTimeoutErrorCode("datastore_v3", int32(pb.Error_TIMEOUT))
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,316 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package datastore provides a client for App Engine's datastore service.
Basic Operations
Entities are the unit of storage and are associated with a key. A key
consists of an optional parent key, a string application ID, a string kind
(also known as an entity type), and either a StringID or an IntID. A
StringID is also known as an entity name or key name.
It is valid to create a key with a zero StringID and a zero IntID; this is
called an incomplete key, and does not refer to any saved entity. Putting an
entity into the datastore under an incomplete key will cause a unique key
to be generated for that entity, with a non-zero IntID.
An entity's contents are a mapping from case-sensitive field names to values.
Valid value types are:
- signed integers (int, int8, int16, int32 and int64),
- bool,
- string,
- float32 and float64,
- []byte (up to 1 megabyte in length),
- any type whose underlying type is one of the above predeclared types,
- ByteString,
- *Key,
- time.Time (stored with microsecond precision),
- appengine.BlobKey,
- appengine.GeoPoint,
- structs whose fields are all valid value types,
- slices of any of the above.
Slices of structs are valid, as are structs that contain slices. However, if
one struct contains another, then at most one of those can be repeated. This
disqualifies recursively defined struct types: any struct T that (directly or
indirectly) contains a []T.
The Get and Put functions load and save an entity's contents. An entity's
contents are typically represented by a struct pointer.
Example code:
type Entity struct {
Value string
}
func handle(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
k := datastore.NewKey(c, "Entity", "stringID", 0, nil)
e := new(Entity)
if err := datastore.Get(c, k, e); err != nil {
http.Error(w, err.Error(), 500)
return
}
old := e.Value
e.Value = r.URL.Path
if _, err := datastore.Put(c, k, e); err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprintf(w, "old=%q\nnew=%q\n", old, e.Value)
}
GetMulti, PutMulti and DeleteMulti are batch versions of the Get, Put and
Delete functions. They take a []*Key instead of a *Key, and may return an
appengine.MultiError when encountering partial failure.
Properties
An entity's contents can be represented by a variety of types. These are
typically struct pointers, but can also be any type that implements the
PropertyLoadSaver interface. If using a struct pointer, you do not have to
explicitly implement the PropertyLoadSaver interface; the datastore will
automatically convert via reflection. If a struct pointer does implement that
interface then those methods will be used in preference to the default
behavior for struct pointers. Struct pointers are more strongly typed and are
easier to use; PropertyLoadSavers are more flexible.
The actual types passed do not have to match between Get and Put calls or even
across different App Engine requests. It is valid to put a *PropertyList and
get that same entity as a *myStruct, or put a *myStruct0 and get a *myStruct1.
Conceptually, any entity is saved as a sequence of properties, and is loaded
into the destination value on a property-by-property basis. When loading into
a struct pointer, an entity that cannot be completely represented (such as a
missing field) will result in an ErrFieldMismatch error but it is up to the
caller whether this error is fatal, recoverable or ignorable.
By default, for struct pointers, all properties are potentially indexed, and
the property name is the same as the field name (and hence must start with an
upper case letter). Fields may have a `datastore:"name,options"` tag. The tag
name is the property name, which must be one or more valid Go identifiers
joined by ".", but may start with a lower case letter. An empty tag name means
to just use the field name. A "-" tag name means that the datastore will
ignore that field. If options is "noindex" then the field will not be indexed.
If the options is "" then the comma may be omitted. There are no other
recognized options.
Fields (except for []byte) are indexed by default. Strings longer than 500
characters cannot be indexed; fields used to store long strings should be
tagged with "noindex". Similarly, ByteStrings longer than 500 bytes cannot be
indexed.
Example code:
// A and B are renamed to a and b.
// A, C and J are not indexed.
// D's tag is equivalent to having no tag at all (E).
// I is ignored entirely by the datastore.
// J has tag information for both the datastore and json packages.
type TaggedStruct struct {
A int `datastore:"a,noindex"`
B int `datastore:"b"`
C int `datastore:",noindex"`
D int `datastore:""`
E int
I int `datastore:"-"`
J int `datastore:",noindex" json:"j"`
}
Structured Properties
If the struct pointed to contains other structs, then the nested or embedded
structs are flattened. For example, given these definitions:
type Inner1 struct {
W int32
X string
}
type Inner2 struct {
Y float64
}
type Inner3 struct {
Z bool
}
type Outer struct {
A int16
I []Inner1
J Inner2
Inner3
}
then an Outer's properties would be equivalent to those of:
type OuterEquivalent struct {
A int16
IDotW []int32 `datastore:"I.W"`
IDotX []string `datastore:"I.X"`
JDotY float64 `datastore:"J.Y"`
Z bool
}
If Outer's embedded Inner3 field was tagged as `datastore:"Foo"` then the
equivalent field would instead be: FooDotZ bool `datastore:"Foo.Z"`.
If an outer struct is tagged "noindex" then all of its implicit flattened
fields are effectively "noindex".
The PropertyLoadSaver Interface
An entity's contents can also be represented by any type that implements the
PropertyLoadSaver interface. This type may be a struct pointer, but it does
not have to be. The datastore package will call Load when getting the entity's
contents, and Save when putting the entity's contents.
Possible uses include deriving non-stored fields, verifying fields, or indexing
a field only if its value is positive.
Example code:
type CustomPropsExample struct {
I, J int
// Sum is not stored, but should always be equal to I + J.
Sum int `datastore:"-"`
}
func (x *CustomPropsExample) Load(c <-chan Property) error {
// Load I and J as usual.
if err := datastore.LoadStruct(x, c); err != nil {
return err
}
// Derive the Sum field.
x.Sum = x.I + x.J
return nil
}
func (x *CustomPropsExample) Save(c chan<- Property) error {
defer close(c)
// Validate the Sum field.
if x.Sum != x.I + x.J {
return errors.New("CustomPropsExample has inconsistent sum")
}
// Save I and J as usual. The code below is equivalent to calling
// "return datastore.SaveStruct(x, c)", but is done manually for
// demonstration purposes.
c <- datastore.Property{
Name: "I",
Value: int64(x.I),
}
c <- datastore.Property{
Name: "J",
Value: int64(x.J),
}
return nil
}
The *PropertyList type implements PropertyLoadSaver, and can therefore hold an
arbitrary entity's contents.
Queries
Queries retrieve entities based on their properties or key's ancestry. Running
a query yields an iterator of results: either keys or (key, entity) pairs.
Queries are re-usable and it is safe to call Query.Run from concurrent
goroutines. Iterators are not safe for concurrent use.
Queries are immutable, and are either created by calling NewQuery, or derived
from an existing query by calling a method like Filter or Order that returns a
new query value. A query is typically constructed by calling NewQuery followed
by a chain of zero or more such methods. These methods are:
- Ancestor and Filter constrain the entities returned by running a query.
- Order affects the order in which they are returned.
- Project constrains the fields returned.
- Distinct de-duplicates projected entities.
- KeysOnly makes the iterator return only keys, not (key, entity) pairs.
- Start, End, Offset and Limit define which sub-sequence of matching entities
to return. Start and End take cursors, Offset and Limit take integers. Start
and Offset affect the first result, End and Limit affect the last result.
If both Start and Offset are set, then the offset is relative to Start.
If both End and Limit are set, then the earliest constraint wins. Limit is
relative to Start+Offset, not relative to End. As a special case, a
negative limit means unlimited.
Example code:
type Widget struct {
Description string
Price int
}
func handle(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
q := datastore.NewQuery("Widget").
Filter("Price <", 1000).
Order("-Price")
b := new(bytes.Buffer)
for t := q.Run(c); ; {
var x Widget
key, err := t.Next(&x)
if err == datastore.Done {
break
}
if err != nil {
serveError(c, w, err)
return
}
fmt.Fprintf(b, "Key=%v\nWidget=%#v\n\n", key, x)
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
io.Copy(w, b)
}
Transactions
RunInTransaction runs a function in a transaction.
Example code:
type Counter struct {
Count int
}
func inc(c appengine.Context, key *datastore.Key) (int, error) {
var x Counter
if err := datastore.Get(c, key, &x); err != nil && err != datastore.ErrNoSuchEntity {
return 0, err
}
x.Count++
if _, err := datastore.Put(c, key, &x); err != nil {
return 0, err
}
return x.Count, nil
}
func handle(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
var count int
err := datastore.RunInTransaction(c, func(c appengine.Context) error {
var err1 error
count, err1 = inc(c, datastore.NewKey(c, "Counter", "singleton", 0, nil))
return err1
}, nil)
if err != nil {
serveError(c, w, err)
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprintf(w, "Count=%d", count)
}
*/
package datastore

View File

@@ -1,309 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package datastore
import (
"bytes"
"encoding/base64"
"encoding/gob"
"errors"
"fmt"
"strconv"
"strings"
"github.com/golang/protobuf/proto"
"google.golang.org/appengine"
"google.golang.org/appengine/internal"
pb "google.golang.org/appengine/internal/datastore"
)
// Key represents the datastore key for a stored entity, and is immutable.
type Key struct {
kind string
stringID string
intID int64
parent *Key
appID string
namespace string
}
// Kind returns the key's kind (also known as entity type).
func (k *Key) Kind() string {
return k.kind
}
// StringID returns the key's string ID (also known as an entity name or key
// name), which may be "".
func (k *Key) StringID() string {
return k.stringID
}
// IntID returns the key's integer ID, which may be 0.
func (k *Key) IntID() int64 {
return k.intID
}
// Parent returns the key's parent key, which may be nil.
func (k *Key) Parent() *Key {
return k.parent
}
// AppID returns the key's application ID.
func (k *Key) AppID() string {
return k.appID
}
// Namespace returns the key's namespace.
func (k *Key) Namespace() string {
return k.namespace
}
// Incomplete returns whether the key does not refer to a stored entity.
// In particular, whether the key has a zero StringID and a zero IntID.
func (k *Key) Incomplete() bool {
return k.stringID == "" && k.intID == 0
}
// valid returns whether the key is valid.
func (k *Key) valid() bool {
if k == nil {
return false
}
for ; k != nil; k = k.parent {
if k.kind == "" || k.appID == "" {
return false
}
if k.stringID != "" && k.intID != 0 {
return false
}
if k.parent != nil {
if k.parent.Incomplete() {
return false
}
if k.parent.appID != k.appID || k.parent.namespace != k.namespace {
return false
}
}
}
return true
}
// Equal returns whether two keys are equal.
func (k *Key) Equal(o *Key) bool {
for k != nil && o != nil {
if k.kind != o.kind || k.stringID != o.stringID || k.intID != o.intID || k.appID != o.appID || k.namespace != o.namespace {
return false
}
k, o = k.parent, o.parent
}
return k == o
}
// root returns the furthest ancestor of a key, which may be itself.
func (k *Key) root() *Key {
for k.parent != nil {
k = k.parent
}
return k
}
// marshal marshals the key's string representation to the buffer.
func (k *Key) marshal(b *bytes.Buffer) {
if k.parent != nil {
k.parent.marshal(b)
}
b.WriteByte('/')
b.WriteString(k.kind)
b.WriteByte(',')
if k.stringID != "" {
b.WriteString(k.stringID)
} else {
b.WriteString(strconv.FormatInt(k.intID, 10))
}
}
// String returns a string representation of the key.
func (k *Key) String() string {
if k == nil {
return ""
}
b := bytes.NewBuffer(make([]byte, 0, 512))
k.marshal(b)
return b.String()
}
type gobKey struct {
Kind string
StringID string
IntID int64
Parent *gobKey
AppID string
Namespace string
}
func keyToGobKey(k *Key) *gobKey {
if k == nil {
return nil
}
return &gobKey{
Kind: k.kind,
StringID: k.stringID,
IntID: k.intID,
Parent: keyToGobKey(k.parent),
AppID: k.appID,
Namespace: k.namespace,
}
}
func gobKeyToKey(gk *gobKey) *Key {
if gk == nil {
return nil
}
return &Key{
kind: gk.Kind,
stringID: gk.StringID,
intID: gk.IntID,
parent: gobKeyToKey(gk.Parent),
appID: gk.AppID,
namespace: gk.Namespace,
}
}
func (k *Key) GobEncode() ([]byte, error) {
buf := new(bytes.Buffer)
if err := gob.NewEncoder(buf).Encode(keyToGobKey(k)); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func (k *Key) GobDecode(buf []byte) error {
gk := new(gobKey)
if err := gob.NewDecoder(bytes.NewBuffer(buf)).Decode(gk); err != nil {
return err
}
*k = *gobKeyToKey(gk)
return nil
}
func (k *Key) MarshalJSON() ([]byte, error) {
return []byte(`"` + k.Encode() + `"`), nil
}
func (k *Key) UnmarshalJSON(buf []byte) error {
if len(buf) < 2 || buf[0] != '"' || buf[len(buf)-1] != '"' {
return errors.New("datastore: bad JSON key")
}
k2, err := DecodeKey(string(buf[1 : len(buf)-1]))
if err != nil {
return err
}
*k = *k2
return nil
}
// Encode returns an opaque representation of the key
// suitable for use in HTML and URLs.
// This is compatible with the Python and Java runtimes.
func (k *Key) Encode() string {
ref := keyToProto("", k)
b, err := proto.Marshal(ref)
if err != nil {
panic(err)
}
// Trailing padding is stripped.
return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=")
}
// DecodeKey decodes a key from the opaque representation returned by Encode.
func DecodeKey(encoded string) (*Key, error) {
// Re-add padding.
if m := len(encoded) % 4; m != 0 {
encoded += strings.Repeat("=", 4-m)
}
b, err := base64.URLEncoding.DecodeString(encoded)
if err != nil {
return nil, err
}
ref := new(pb.Reference)
if err := proto.Unmarshal(b, ref); err != nil {
return nil, err
}
return protoToKey(ref)
}
// NewIncompleteKey creates a new incomplete key.
// kind cannot be empty.
func NewIncompleteKey(c appengine.Context, kind string, parent *Key) *Key {
return NewKey(c, kind, "", 0, parent)
}
// NewKey creates a new key.
// kind cannot be empty.
// Either one or both of stringID and intID must be zero. If both are zero,
// the key returned is incomplete.
// parent must either be a complete key or nil.
func NewKey(c appengine.Context, kind, stringID string, intID int64, parent *Key) *Key {
// If there's a parent key, use its namespace.
// Otherwise, do a fake RPC to try to get a namespace if c is a namespacedContext (or wraps one).
var namespace string
if parent != nil {
namespace = parent.namespace
} else {
namespace = internal.VirtAPI(c, "GetNamespace")
}
return &Key{
kind: kind,
stringID: stringID,
intID: intID,
parent: parent,
appID: c.FullyQualifiedAppID(),
namespace: namespace,
}
}
// AllocateIDs returns a range of n integer IDs with the given kind and parent
// combination. kind cannot be empty; parent may be nil. The IDs in the range
// returned will not be used by the datastore's automatic ID sequence generator
// and may be used with NewKey without conflict.
//
// The range is inclusive at the low end and exclusive at the high end. In
// other words, valid intIDs x satisfy low <= x && x < high.
//
// If no error is returned, low + n == high.
func AllocateIDs(c appengine.Context, kind string, parent *Key, n int) (low, high int64, err error) {
if kind == "" {
return 0, 0, errors.New("datastore: AllocateIDs given an empty kind")
}
if n < 0 {
return 0, 0, fmt.Errorf("datastore: AllocateIDs given a negative count: %d", n)
}
if n == 0 {
return 0, 0, nil
}
req := &pb.AllocateIdsRequest{
ModelKey: keyToProto("", NewIncompleteKey(c, kind, parent)),
Size: proto.Int64(int64(n)),
}
res := &pb.AllocateIdsResponse{}
if err := c.Call("datastore_v3", "AllocateIds", req, res, nil); err != nil {
return 0, 0, err
}
// The protobuf is inclusive at both ends. Idiomatic Go (e.g. slices, for loops)
// is inclusive at the low end and exclusive at the high end, so we add 1.
low = res.GetStart()
high = res.GetEnd() + 1
if low+int64(n) != high {
return 0, 0, fmt.Errorf("datastore: internal error: could not allocate %d IDs", n)
}
return low, high, nil
}

View File

@@ -1,214 +0,0 @@
// Copyright 2011 Google Inc. All Rights Reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package datastore
import (
"bytes"
"encoding/gob"
"encoding/json"
"testing"
"github.com/golang/protobuf/proto"
"google.golang.org/appengine"
"google.golang.org/appengine/internal"
)
func TestKeyEncoding(t *testing.T) {
testCases := []struct {
desc string
key *Key
exp string
}{
{
desc: "A simple key with an int ID",
key: &Key{
kind: "Person",
intID: 1,
appID: "glibrary",
},
exp: "aghnbGlicmFyeXIMCxIGUGVyc29uGAEM",
},
{
desc: "A simple key with a string ID",
key: &Key{
kind: "Graph",
stringID: "graph:7-day-active",
appID: "glibrary",
},
exp: "aghnbGlicmFyeXIdCxIFR3JhcGgiEmdyYXBoOjctZGF5LWFjdGl2ZQw",
},
{
desc: "A key with a parent",
key: &Key{
kind: "WordIndex",
intID: 1033,
parent: &Key{
kind: "WordIndex",
intID: 1020032,
appID: "glibrary",
},
appID: "glibrary",
},
exp: "aghnbGlicmFyeXIhCxIJV29yZEluZGV4GIChPgwLEglXb3JkSW5kZXgYiQgM",
},
}
for _, tc := range testCases {
enc := tc.key.Encode()
if enc != tc.exp {
t.Errorf("%s: got %q, want %q", tc.desc, enc, tc.exp)
}
key, err := DecodeKey(tc.exp)
if err != nil {
t.Errorf("%s: failed decoding key: %v", tc.desc, err)
continue
}
if !key.Equal(tc.key) {
t.Errorf("%s: decoded key %v, want %v", tc.desc, key, tc.key)
}
}
}
func TestKeyGob(t *testing.T) {
k := &Key{
kind: "Gopher",
intID: 3,
parent: &Key{
kind: "Mom",
stringID: "narwhal",
appID: "gopher-con",
},
appID: "gopher-con",
}
buf := new(bytes.Buffer)
if err := gob.NewEncoder(buf).Encode(k); err != nil {
t.Fatalf("gob encode failed: %v", err)
}
k2 := new(Key)
if err := gob.NewDecoder(buf).Decode(k2); err != nil {
t.Fatalf("gob decode failed: %v", err)
}
if !k2.Equal(k) {
t.Errorf("gob round trip of %v produced %v", k, k2)
}
}
func TestNilKeyGob(t *testing.T) {
type S struct {
Key *Key
}
s1 := new(S)
buf := new(bytes.Buffer)
if err := gob.NewEncoder(buf).Encode(s1); err != nil {
t.Fatalf("gob encode failed: %v", err)
}
s2 := new(S)
if err := gob.NewDecoder(buf).Decode(s2); err != nil {
t.Fatalf("gob decode failed: %v", err)
}
if s2.Key != nil {
t.Errorf("gob round trip of nil key produced %v", s2.Key)
}
}
func TestKeyJSON(t *testing.T) {
k := &Key{
kind: "Gopher",
intID: 2,
parent: &Key{
kind: "Mom",
stringID: "narwhal",
appID: "gopher-con",
},
appID: "gopher-con",
}
exp := `"` + k.Encode() + `"`
buf, err := json.Marshal(k)
if err != nil {
t.Fatalf("json.Marshal failed: %v", err)
}
if s := string(buf); s != exp {
t.Errorf("JSON encoding of key %v: got %q, want %q", k, s, exp)
}
k2 := new(Key)
if err := json.Unmarshal(buf, k2); err != nil {
t.Fatalf("json.Unmarshal failed: %v", err)
}
if !k2.Equal(k) {
t.Errorf("JSON round trip of %v produced %v", k, k2)
}
}
func TestNilKeyJSON(t *testing.T) {
type S struct {
Key *Key
}
s1 := new(S)
buf, err := json.Marshal(s1)
if err != nil {
t.Fatalf("json.Marshal failed: %v", err)
}
s2 := new(S)
if err := json.Unmarshal(buf, s2); err != nil {
t.Fatalf("json.Unmarshal failed: %v", err)
}
if s2.Key != nil {
t.Errorf("JSON round trip of nil key produced %v", s2.Key)
}
}
type fakeKeyer struct {
appengine.Context
}
func (fakeKeyer) FullyQualifiedAppID() string { return "s~some-app" }
func (fakeKeyer) Call(service, method string, in, out proto.Message, opts *internal.CallOptions) error {
return nil
}
func TestIncompleteKeyWithParent(t *testing.T) {
var c appengine.Context = fakeKeyer{}
// fadduh is a complete key.
fadduh := NewKey(c, "Person", "", 1, nil)
if fadduh.Incomplete() {
t.Fatalf("fadduh is incomplete")
}
// robert is an incomplete key with fadduh as a parent.
robert := NewIncompleteKey(c, "Person", fadduh)
if !robert.Incomplete() {
t.Fatalf("robert is complete")
}
// Both should be valid keys.
if !fadduh.valid() {
t.Errorf("fadduh is invalid: %v", fadduh)
}
if !robert.valid() {
t.Errorf("robert is invalid: %v", robert)
}
}
func TestNamespace(t *testing.T) {
key := &Key{
kind: "Person",
intID: 1,
appID: "s~some-app",
namespace: "mynamespace",
}
if g, w := key.Namespace(), "mynamespace"; g != w {
t.Errorf("key.Namespace() = %q, want %q", g, w)
}
}

View File

@@ -1,334 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package datastore
import (
"fmt"
"reflect"
"time"
"google.golang.org/appengine"
pb "google.golang.org/appengine/internal/datastore"
)
var (
typeOfBlobKey = reflect.TypeOf(appengine.BlobKey(""))
typeOfByteSlice = reflect.TypeOf([]byte(nil))
typeOfByteString = reflect.TypeOf(ByteString(nil))
typeOfGeoPoint = reflect.TypeOf(appengine.GeoPoint{})
typeOfTime = reflect.TypeOf(time.Time{})
)
// typeMismatchReason returns a string explaining why the property p could not
// be stored in an entity field of type v.Type().
func typeMismatchReason(p Property, v reflect.Value) string {
entityType := "empty"
switch p.Value.(type) {
case int64:
entityType = "int"
case bool:
entityType = "bool"
case string:
entityType = "string"
case float64:
entityType = "float"
case *Key:
entityType = "*datastore.Key"
case time.Time:
entityType = "time.Time"
case appengine.BlobKey:
entityType = "appengine.BlobKey"
case appengine.GeoPoint:
entityType = "appengine.GeoPoint"
case ByteString:
entityType = "datastore.ByteString"
case []byte:
entityType = "[]byte"
}
return fmt.Sprintf("type mismatch: %s versus %v", entityType, v.Type())
}
type propertyLoader struct {
// m holds the number of times a substruct field like "Foo.Bar.Baz" has
// been seen so far. The map is constructed lazily.
m map[string]int
}
func (l *propertyLoader) load(codec *structCodec, structValue reflect.Value, p Property, requireSlice bool) string {
var v reflect.Value
// Traverse a struct's struct-typed fields.
for name := p.Name; ; {
decoder, ok := codec.byName[name]
if !ok {
return "no such struct field"
}
v = structValue.Field(decoder.index)
if !v.IsValid() {
return "no such struct field"
}
if !v.CanSet() {
return "cannot set struct field"
}
if decoder.substructCodec == nil {
break
}
if v.Kind() == reflect.Slice {
if l.m == nil {
l.m = make(map[string]int)
}
index := l.m[p.Name]
l.m[p.Name] = index + 1
for v.Len() <= index {
v.Set(reflect.Append(v, reflect.New(v.Type().Elem()).Elem()))
}
structValue = v.Index(index)
requireSlice = false
} else {
structValue = v
}
// Strip the "I." from "I.X".
name = name[len(codec.byIndex[decoder.index].name):]
codec = decoder.substructCodec
}
var slice reflect.Value
if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 {
slice = v
v = reflect.New(v.Type().Elem()).Elem()
} else if requireSlice {
return "multiple-valued property requires a slice field type"
}
// Convert indexValues to a Go value with a meaning derived from the
// destination type.
pValue := p.Value
if iv, ok := pValue.(indexValue); ok {
meaning := pb.Property_NO_MEANING
switch v.Type() {
case typeOfBlobKey:
meaning = pb.Property_BLOBKEY
case typeOfByteSlice:
meaning = pb.Property_BLOB
case typeOfByteString:
meaning = pb.Property_BYTESTRING
case typeOfGeoPoint:
meaning = pb.Property_GEORSS_POINT
case typeOfTime:
meaning = pb.Property_GD_WHEN
}
var err error
pValue, err = propValue(iv.value, meaning)
if err != nil {
return err.Error()
}
}
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
x, ok := pValue.(int64)
if !ok && pValue != nil {
return typeMismatchReason(p, v)
}
if v.OverflowInt(x) {
return fmt.Sprintf("value %v overflows struct field of type %v", x, v.Type())
}
v.SetInt(x)
case reflect.Bool:
x, ok := pValue.(bool)
if !ok && pValue != nil {
return typeMismatchReason(p, v)
}
v.SetBool(x)
case reflect.String:
switch x := pValue.(type) {
case appengine.BlobKey:
v.SetString(string(x))
case ByteString:
v.SetString(string(x))
case string:
v.SetString(x)
default:
if pValue != nil {
return typeMismatchReason(p, v)
}
}
case reflect.Float32, reflect.Float64:
x, ok := pValue.(float64)
if !ok && pValue != nil {
return typeMismatchReason(p, v)
}
if v.OverflowFloat(x) {
return fmt.Sprintf("value %v overflows struct field of type %v", x, v.Type())
}
v.SetFloat(x)
case reflect.Ptr:
x, ok := pValue.(*Key)
if !ok && pValue != nil {
return typeMismatchReason(p, v)
}
if _, ok := v.Interface().(*Key); !ok {
return typeMismatchReason(p, v)
}
v.Set(reflect.ValueOf(x))
case reflect.Struct:
switch v.Type() {
case typeOfTime:
x, ok := pValue.(time.Time)
if !ok && pValue != nil {
return typeMismatchReason(p, v)
}
v.Set(reflect.ValueOf(x))
case typeOfGeoPoint:
x, ok := pValue.(appengine.GeoPoint)
if !ok && pValue != nil {
return typeMismatchReason(p, v)
}
v.Set(reflect.ValueOf(x))
default:
return typeMismatchReason(p, v)
}
case reflect.Slice:
x, ok := pValue.([]byte)
if !ok {
if y, yok := pValue.(ByteString); yok {
x, ok = []byte(y), true
}
}
if !ok && pValue != nil {
return typeMismatchReason(p, v)
}
if v.Type().Elem().Kind() != reflect.Uint8 {
return typeMismatchReason(p, v)
}
v.SetBytes(x)
default:
return typeMismatchReason(p, v)
}
if slice.IsValid() {
slice.Set(reflect.Append(slice, v))
}
return ""
}
// loadEntity loads an EntityProto into PropertyLoadSaver or struct pointer.
func loadEntity(dst interface{}, src *pb.EntityProto) (err error) {
props, err := protoToProperties(src)
if err != nil {
return err
}
if e, ok := dst.(PropertyLoadSaver); ok {
return e.Load(props)
}
return LoadStruct(dst, props)
}
func (s structPLS) Load(props []Property) error {
var fieldName, reason string
var l propertyLoader
for _, p := range props {
if errStr := l.load(s.codec, s.v, p, p.Multiple); errStr != "" {
// We don't return early, as we try to load as many properties as possible.
// It is valid to load an entity into a struct that cannot fully represent it.
// That case returns an error, but the caller is free to ignore it.
fieldName, reason = p.Name, errStr
}
}
if reason != "" {
return &ErrFieldMismatch{
StructType: s.v.Type(),
FieldName: fieldName,
Reason: reason,
}
}
return nil
}
func protoToProperties(src *pb.EntityProto) ([]Property, error) {
props, rawProps := src.Property, src.RawProperty
out := make([]Property, 0, len(props)+len(rawProps))
for {
var (
x *pb.Property
noIndex bool
)
if len(props) > 0 {
x, props = props[0], props[1:]
} else if len(rawProps) > 0 {
x, rawProps = rawProps[0], rawProps[1:]
noIndex = true
} else {
break
}
var value interface{}
if x.Meaning != nil && *x.Meaning == pb.Property_INDEX_VALUE {
value = indexValue{x.Value}
} else {
var err error
value, err = propValue(x.Value, x.GetMeaning())
if err != nil {
return nil, err
}
}
out = append(out, Property{
Name: x.GetName(),
Value: value,
NoIndex: noIndex,
Multiple: x.GetMultiple(),
})
}
return out, nil
}
// propValue returns a Go value that combines the raw PropertyValue with a
// meaning. For example, an Int64Value with GD_WHEN becomes a time.Time.
func propValue(v *pb.PropertyValue, m pb.Property_Meaning) (interface{}, error) {
switch {
case v.Int64Value != nil:
if m == pb.Property_GD_WHEN {
return fromUnixMicro(*v.Int64Value), nil
} else {
return *v.Int64Value, nil
}
case v.BooleanValue != nil:
return *v.BooleanValue, nil
case v.StringValue != nil:
if m == pb.Property_BLOB {
return []byte(*v.StringValue), nil
} else if m == pb.Property_BLOBKEY {
return appengine.BlobKey(*v.StringValue), nil
} else if m == pb.Property_BYTESTRING {
return ByteString(*v.StringValue), nil
} else {
return *v.StringValue, nil
}
case v.DoubleValue != nil:
return *v.DoubleValue, nil
case v.Referencevalue != nil:
key, err := referenceValueToKey(v.Referencevalue)
if err != nil {
return nil, err
}
return key, nil
case v.Pointvalue != nil:
// NOTE: Strangely, latitude maps to X, longitude to Y.
return appengine.GeoPoint{Lat: v.Pointvalue.GetX(), Lng: v.Pointvalue.GetY()}, nil
}
return nil, nil
}
// indexValue is a Property value that is created when entities are loaded from
// an index, such as from a projection query.
//
// Such Property values do not contain all of the metadata required to be
// faithfully represented as a Go value, and are instead represented as an
// opaque indexValue. Load the properties into a concrete struct type (e.g. by
// passing a struct pointer to Iterator.Next) to reconstruct actual Go values
// of type int, string, time.Time, etc.
type indexValue struct {
value *pb.PropertyValue
}

View File

@@ -1,294 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package datastore
import (
"fmt"
"reflect"
"strings"
"sync"
"unicode"
)
// Entities with more than this many indexed properties will not be saved.
const maxIndexedProperties = 5000
// []byte fields more than 1 megabyte long will not be loaded or saved.
const maxBlobLen = 1 << 20
// Property is a name/value pair plus some metadata. A datastore entity's
// contents are loaded and saved as a sequence of Properties. An entity can
// have multiple Properties with the same name, provided that p.Multiple is
// true on all of that entity's Properties with that name.
type Property struct {
// Name is the property name.
Name string
// Value is the property value. The valid types are:
// - int64
// - bool
// - string
// - float64
// - ByteString
// - *Key
// - time.Time
// - appengine.BlobKey
// - appengine.GeoPoint
// - []byte (up to 1 megabyte in length)
// This set is smaller than the set of valid struct field types that the
// datastore can load and save. A Property Value cannot be a slice (apart
// from []byte); use multiple Properties instead. Also, a Value's type
// must be explicitly on the list above; it is not sufficient for the
// underlying type to be on that list. For example, a Value of "type
// myInt64 int64" is invalid. Smaller-width integers and floats are also
// invalid. Again, this is more restrictive than the set of valid struct
// field types.
//
// A Value will have an opaque type when loading entities from an index,
// such as via a projection query. Load entities into a struct instead
// of a PropertyLoadSaver when using a projection query.
//
// A Value may also be the nil interface value; this is equivalent to
// Python's None but not directly representable by a Go struct. Loading
// a nil-valued property into a struct will set that field to the zero
// value.
Value interface{}
// NoIndex is whether the datastore cannot index this property.
NoIndex bool
// Multiple is whether the entity can have multiple properties with
// the same name. Even if a particular instance only has one property with
// a certain name, Multiple should be true if a struct would best represent
// it as a field of type []T instead of type T.
Multiple bool
}
// ByteString is a short byte slice (up to 500 bytes) that can be indexed.
type ByteString []byte
// PropertyLoadSaver can be converted from and to a slice of Properties.
type PropertyLoadSaver interface {
Load([]Property) error
Save() ([]Property, error)
}
// PropertyList converts a []Property to implement PropertyLoadSaver.
type PropertyList []Property
var (
typeOfPropertyLoadSaver = reflect.TypeOf((*PropertyLoadSaver)(nil)).Elem()
typeOfPropertyList = reflect.TypeOf(PropertyList(nil))
)
// Load loads all of the provided properties into l.
// It does not first reset *l to an empty slice.
func (l *PropertyList) Load(p []Property) error {
*l = append(*l, p...)
return nil
}
// Save saves all of l's properties as a slice or Properties.
func (l *PropertyList) Save() ([]Property, error) {
return *l, nil
}
// validPropertyName returns whether name consists of one or more valid Go
// identifiers joined by ".".
func validPropertyName(name string) bool {
if name == "" {
return false
}
for _, s := range strings.Split(name, ".") {
if s == "" {
return false
}
first := true
for _, c := range s {
if first {
first = false
if c != '_' && !unicode.IsLetter(c) {
return false
}
} else {
if c != '_' && !unicode.IsLetter(c) && !unicode.IsDigit(c) {
return false
}
}
}
}
return true
}
// structTag is the parsed `datastore:"name,options"` tag of a struct field.
// If a field has no tag, or the tag has an empty name, then the structTag's
// name is just the field name. A "-" name means that the datastore ignores
// that field.
type structTag struct {
name string
noIndex bool
}
// structCodec describes how to convert a struct to and from a sequence of
// properties.
type structCodec struct {
// byIndex gives the structTag for the i'th field.
byIndex []structTag
// byName gives the field codec for the structTag with the given name.
byName map[string]fieldCodec
// hasSlice is whether a struct or any of its nested or embedded structs
// has a slice-typed field (other than []byte).
hasSlice bool
// complete is whether the structCodec is complete. An incomplete
// structCodec may be encountered when walking a recursive struct.
complete bool
}
// fieldCodec is a struct field's index and, if that struct field's type is
// itself a struct, that substruct's structCodec.
type fieldCodec struct {
index int
substructCodec *structCodec
}
// structCodecs collects the structCodecs that have already been calculated.
var (
structCodecsMutex sync.Mutex
structCodecs = make(map[reflect.Type]*structCodec)
)
// getStructCodec returns the structCodec for the given struct type.
func getStructCodec(t reflect.Type) (*structCodec, error) {
structCodecsMutex.Lock()
defer structCodecsMutex.Unlock()
return getStructCodecLocked(t)
}
// getStructCodecLocked implements getStructCodec. The structCodecsMutex must
// be held when calling this function.
func getStructCodecLocked(t reflect.Type) (ret *structCodec, retErr error) {
c, ok := structCodecs[t]
if ok {
return c, nil
}
c = &structCodec{
byIndex: make([]structTag, t.NumField()),
byName: make(map[string]fieldCodec),
}
// Add c to the structCodecs map before we are sure it is good. If t is
// a recursive type, it needs to find the incomplete entry for itself in
// the map.
structCodecs[t] = c
defer func() {
if retErr != nil {
delete(structCodecs, t)
}
}()
for i := range c.byIndex {
f := t.Field(i)
name, opts := f.Tag.Get("datastore"), ""
if i := strings.Index(name, ","); i != -1 {
name, opts = name[:i], name[i+1:]
}
if name == "" {
if !f.Anonymous {
name = f.Name
}
} else if name == "-" {
c.byIndex[i] = structTag{name: name}
continue
} else if !validPropertyName(name) {
return nil, fmt.Errorf("datastore: struct tag has invalid property name: %q", name)
}
substructType, fIsSlice := reflect.Type(nil), false
switch f.Type.Kind() {
case reflect.Struct:
substructType = f.Type
case reflect.Slice:
if f.Type.Elem().Kind() == reflect.Struct {
substructType = f.Type.Elem()
}
fIsSlice = f.Type != typeOfByteSlice
c.hasSlice = c.hasSlice || fIsSlice
}
if substructType != nil && substructType != typeOfTime && substructType != typeOfGeoPoint {
if name != "" {
name = name + "."
}
sub, err := getStructCodecLocked(substructType)
if err != nil {
return nil, err
}
if !sub.complete {
return nil, fmt.Errorf("datastore: recursive struct: field %q", f.Name)
}
if fIsSlice && sub.hasSlice {
return nil, fmt.Errorf(
"datastore: flattening nested structs leads to a slice of slices: field %q", f.Name)
}
c.hasSlice = c.hasSlice || sub.hasSlice
for relName := range sub.byName {
absName := name + relName
if _, ok := c.byName[absName]; ok {
return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", absName)
}
c.byName[absName] = fieldCodec{index: i, substructCodec: sub}
}
} else {
if _, ok := c.byName[name]; ok {
return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", name)
}
c.byName[name] = fieldCodec{index: i}
}
c.byIndex[i] = structTag{
name: name,
noIndex: opts == "noindex",
}
}
c.complete = true
return c, nil
}
// structPLS adapts a struct to be a PropertyLoadSaver.
type structPLS struct {
v reflect.Value
codec *structCodec
}
// newStructPLS returns a PropertyLoadSaver for the struct pointer p.
func newStructPLS(p interface{}) (PropertyLoadSaver, error) {
v := reflect.ValueOf(p)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
return nil, ErrInvalidEntityType
}
v = v.Elem()
codec, err := getStructCodec(v.Type())
if err != nil {
return nil, err
}
return structPLS{v, codec}, nil
}
// LoadStruct loads the properties from p to dst.
// dst must be a struct pointer.
func LoadStruct(dst interface{}, p []Property) error {
x, err := newStructPLS(dst)
if err != nil {
return err
}
return x.Load(p)
}
// SaveStruct returns the properties from src as a slice of Properties.
// src must be a struct pointer.
func SaveStruct(src interface{}) ([]Property, error) {
x, err := newStructPLS(src)
if err != nil {
return nil, err
}
return x.Save()
}

View File

@@ -1,559 +0,0 @@
// Copyright 2011 Google Inc. All Rights Reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package datastore
import (
"reflect"
"testing"
"time"
"google.golang.org/appengine"
)
func TestValidPropertyName(t *testing.T) {
testCases := []struct {
name string
want bool
}{
// Invalid names.
{"", false},
{"'", false},
{".", false},
{"..", false},
{".foo", false},
{"0", false},
{"00", false},
{"X.X.4.X.X", false},
{"\n", false},
{"\x00", false},
{"abc\xffz", false},
{"foo.", false},
{"foo..", false},
{"foo..bar", false},
{"☃", false},
{`"`, false},
// Valid names.
{"AB", true},
{"Abc", true},
{"X.X.X.X.X", true},
{"_", true},
{"_0", true},
{"a", true},
{"a_B", true},
{"f00", true},
{"f0o", true},
{"fo0", true},
{"foo", true},
{"foo.bar", true},
{"foo.bar.baz", true},
{"世界", true},
}
for _, tc := range testCases {
got := validPropertyName(tc.name)
if got != tc.want {
t.Errorf("%q: got %v, want %v", tc.name, got, tc.want)
}
}
}
func TestStructCodec(t *testing.T) {
type oStruct struct {
O int
}
type pStruct struct {
P int
Q int
}
type rStruct struct {
R int
S pStruct
T oStruct
oStruct
}
type uStruct struct {
U int
v int
}
oStructCodec := &structCodec{
byIndex: []structTag{
{name: "O"},
},
byName: map[string]fieldCodec{
"O": {index: 0},
},
complete: true,
}
pStructCodec := &structCodec{
byIndex: []structTag{
{name: "P"},
{name: "Q"},
},
byName: map[string]fieldCodec{
"P": {index: 0},
"Q": {index: 1},
},
complete: true,
}
rStructCodec := &structCodec{
byIndex: []structTag{
{name: "R"},
{name: "S."},
{name: "T."},
{name: ""},
},
byName: map[string]fieldCodec{
"R": {index: 0},
"S.P": {index: 1, substructCodec: pStructCodec},
"S.Q": {index: 1, substructCodec: pStructCodec},
"T.O": {index: 2, substructCodec: oStructCodec},
"O": {index: 3, substructCodec: oStructCodec},
},
complete: true,
}
uStructCodec := &structCodec{
byIndex: []structTag{
{name: "U"},
{name: "v"},
},
byName: map[string]fieldCodec{
"U": {index: 0},
"v": {index: 1},
},
complete: true,
}
testCases := []struct {
desc string
structValue interface{}
want *structCodec
}{
{
"oStruct",
oStruct{},
oStructCodec,
},
{
"pStruct",
pStruct{},
pStructCodec,
},
{
"rStruct",
rStruct{},
rStructCodec,
},
{
"uStruct",
uStruct{},
uStructCodec,
},
{
"non-basic fields",
struct {
B appengine.BlobKey
K *Key
T time.Time
}{},
&structCodec{
byIndex: []structTag{
{name: "B"},
{name: "K"},
{name: "T"},
},
byName: map[string]fieldCodec{
"B": {index: 0},
"K": {index: 1},
"T": {index: 2},
},
complete: true,
},
},
{
"struct tags with ignored embed",
struct {
A int `datastore:"a,noindex"`
B int `datastore:"b"`
C int `datastore:",noindex"`
D int `datastore:""`
E int
I int `datastore:"-"`
J int `datastore:",noindex" json:"j"`
oStruct `datastore:"-"`
}{},
&structCodec{
byIndex: []structTag{
{name: "a", noIndex: true},
{name: "b", noIndex: false},
{name: "C", noIndex: true},
{name: "D", noIndex: false},
{name: "E", noIndex: false},
{name: "-", noIndex: false},
{name: "J", noIndex: true},
{name: "-", noIndex: false},
},
byName: map[string]fieldCodec{
"a": {index: 0},
"b": {index: 1},
"C": {index: 2},
"D": {index: 3},
"E": {index: 4},
"J": {index: 6},
},
complete: true,
},
},
{
"unexported fields",
struct {
A int
b int
C int `datastore:"x"`
d int `datastore:"Y"`
}{},
&structCodec{
byIndex: []structTag{
{name: "A"},
{name: "b"},
{name: "x"},
{name: "Y"},
},
byName: map[string]fieldCodec{
"A": {index: 0},
"b": {index: 1},
"x": {index: 2},
"Y": {index: 3},
},
complete: true,
},
},
{
"nested and embedded structs",
struct {
A int
B int
CC oStruct
DDD rStruct
oStruct
}{},
&structCodec{
byIndex: []structTag{
{name: "A"},
{name: "B"},
{name: "CC."},
{name: "DDD."},
{name: ""},
},
byName: map[string]fieldCodec{
"A": {index: 0},
"B": {index: 1},
"CC.O": {index: 2, substructCodec: oStructCodec},
"DDD.R": {index: 3, substructCodec: rStructCodec},
"DDD.S.P": {index: 3, substructCodec: rStructCodec},
"DDD.S.Q": {index: 3, substructCodec: rStructCodec},
"DDD.T.O": {index: 3, substructCodec: rStructCodec},
"DDD.O": {index: 3, substructCodec: rStructCodec},
"O": {index: 4, substructCodec: oStructCodec},
},
complete: true,
},
},
{
"struct tags with nested and embedded structs",
struct {
A int `datastore:"-"`
B int `datastore:"w"`
C oStruct `datastore:"xx"`
D rStruct `datastore:"y"`
oStruct `datastore:"z"`
}{},
&structCodec{
byIndex: []structTag{
{name: "-"},
{name: "w"},
{name: "xx."},
{name: "y."},
{name: "z."},
},
byName: map[string]fieldCodec{
"w": {index: 1},
"xx.O": {index: 2, substructCodec: oStructCodec},
"y.R": {index: 3, substructCodec: rStructCodec},
"y.S.P": {index: 3, substructCodec: rStructCodec},
"y.S.Q": {index: 3, substructCodec: rStructCodec},
"y.T.O": {index: 3, substructCodec: rStructCodec},
"y.O": {index: 3, substructCodec: rStructCodec},
"z.O": {index: 4, substructCodec: oStructCodec},
},
complete: true,
},
},
{
"unexported nested and embedded structs",
struct {
a int
B int
c uStruct
D uStruct
uStruct
}{},
&structCodec{
byIndex: []structTag{
{name: "a"},
{name: "B"},
{name: "c."},
{name: "D."},
{name: ""},
},
byName: map[string]fieldCodec{
"a": {index: 0},
"B": {index: 1},
"c.U": {index: 2, substructCodec: uStructCodec},
"c.v": {index: 2, substructCodec: uStructCodec},
"D.U": {index: 3, substructCodec: uStructCodec},
"D.v": {index: 3, substructCodec: uStructCodec},
"U": {index: 4, substructCodec: uStructCodec},
"v": {index: 4, substructCodec: uStructCodec},
},
complete: true,
},
},
{
"noindex nested struct",
struct {
A oStruct `datastore:",noindex"`
}{},
&structCodec{
byIndex: []structTag{
{name: "A.", noIndex: true},
},
byName: map[string]fieldCodec{
"A.O": {index: 0, substructCodec: oStructCodec},
},
complete: true,
},
},
}
for _, tc := range testCases {
got, err := getStructCodec(reflect.TypeOf(tc.structValue))
if err != nil {
t.Errorf("%s: getStructCodec: %v", tc.desc, err)
continue
}
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("%s\ngot %v\nwant %v\n", tc.desc, got, tc.want)
continue
}
}
}
func TestRepeatedPropertyName(t *testing.T) {
good := []interface{}{
struct {
A int `datastore:"-"`
}{},
struct {
A int `datastore:"b"`
B int
}{},
struct {
A int
B int `datastore:"B"`
}{},
struct {
A int `datastore:"B"`
B int `datastore:"-"`
}{},
struct {
A int `datastore:"-"`
B int `datastore:"A"`
}{},
struct {
A int `datastore:"B"`
B int `datastore:"A"`
}{},
struct {
A int `datastore:"B"`
B int `datastore:"C"`
C int `datastore:"A"`
}{},
struct {
A int `datastore:"B"`
B int `datastore:"C"`
C int `datastore:"D"`
}{},
}
bad := []interface{}{
struct {
A int `datastore:"B"`
B int
}{},
struct {
A int
B int `datastore:"A"`
}{},
struct {
A int `datastore:"C"`
B int `datastore:"C"`
}{},
struct {
A int `datastore:"B"`
B int `datastore:"C"`
C int `datastore:"B"`
}{},
}
testGetStructCodec(t, good, bad)
}
func TestFlatteningNestedStructs(t *testing.T) {
type deepGood struct {
A struct {
B []struct {
C struct {
D int
}
}
}
}
type deepBad struct {
A struct {
B []struct {
C struct {
D []int
}
}
}
}
type iSay struct {
Tomato int
}
type youSay struct {
Tomato int
}
type tweedledee struct {
Dee int `datastore:"D"`
}
type tweedledum struct {
Dum int `datastore:"D"`
}
good := []interface{}{
struct {
X []struct {
Y string
}
}{},
struct {
X []struct {
Y []byte
}
}{},
struct {
P []int
X struct {
Y []int
}
}{},
struct {
X struct {
Y []int
}
Q []int
}{},
struct {
P []int
X struct {
Y []int
}
Q []int
}{},
struct {
deepGood
}{},
struct {
DG deepGood
}{},
struct {
Foo struct {
Z int `datastore:"X"`
} `datastore:"A"`
Bar struct {
Z int `datastore:"Y"`
} `datastore:"A"`
}{},
}
bad := []interface{}{
struct {
X []struct {
Y []string
}
}{},
struct {
X []struct {
Y []int
}
}{},
struct {
deepBad
}{},
struct {
DB deepBad
}{},
struct {
iSay
youSay
}{},
struct {
tweedledee
tweedledum
}{},
struct {
Foo struct {
Z int
} `datastore:"A"`
Bar struct {
Z int
} `datastore:"A"`
}{},
}
testGetStructCodec(t, good, bad)
}
func testGetStructCodec(t *testing.T, good []interface{}, bad []interface{}) {
for _, x := range good {
if _, err := getStructCodec(reflect.TypeOf(x)); err != nil {
t.Errorf("type %T: got non-nil error (%s), want nil", x, err)
}
}
for _, x := range bad {
if _, err := getStructCodec(reflect.TypeOf(x)); err == nil {
t.Errorf("type %T: got nil error, want non-nil", x)
}
}
}
func TestNilKeyIsStored(t *testing.T) {
x := struct {
K *Key
I int
}{}
p := PropertyList{}
// Save x as properties.
p1, _ := SaveStruct(&x)
p.Load(p1)
// Set x's fields to non-zero.
x.K = &Key{}
x.I = 2
// Load x from properties.
p2, _ := p.Save()
LoadStruct(&x, p2)
// Check that x's fields were set to zero.
if x.K != nil {
t.Errorf("K field was not zero")
}
if x.I != 0 {
t.Errorf("I field was not zero")
}
}

View File

@@ -1,712 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package datastore
import (
"encoding/base64"
"errors"
"fmt"
"math"
"reflect"
"strings"
"github.com/golang/protobuf/proto"
"google.golang.org/appengine"
pb "google.golang.org/appengine/internal/datastore"
)
type operator int
const (
lessThan operator = iota
lessEq
equal
greaterEq
greaterThan
)
var operatorToProto = map[operator]*pb.Query_Filter_Operator{
lessThan: pb.Query_Filter_LESS_THAN.Enum(),
lessEq: pb.Query_Filter_LESS_THAN_OR_EQUAL.Enum(),
equal: pb.Query_Filter_EQUAL.Enum(),
greaterEq: pb.Query_Filter_GREATER_THAN_OR_EQUAL.Enum(),
greaterThan: pb.Query_Filter_GREATER_THAN.Enum(),
}
// filter is a conditional filter on query results.
type filter struct {
FieldName string
Op operator
Value interface{}
}
type sortDirection int
const (
ascending sortDirection = iota
descending
)
var sortDirectionToProto = map[sortDirection]*pb.Query_Order_Direction{
ascending: pb.Query_Order_ASCENDING.Enum(),
descending: pb.Query_Order_DESCENDING.Enum(),
}
// order is a sort order on query results.
type order struct {
FieldName string
Direction sortDirection
}
// NewQuery creates a new Query for a specific entity kind.
//
// An empty kind means to return all entities, including entities created and
// managed by other App Engine features, and is called a kindless query.
// Kindless queries cannot include filters or sort orders on property values.
func NewQuery(kind string) *Query {
return &Query{
kind: kind,
limit: -1,
}
}
// Query represents a datastore query.
type Query struct {
kind string
ancestor *Key
filter []filter
order []order
projection []string
distinct bool
keysOnly bool
eventual bool
limit int32
offset int32
start *pb.CompiledCursor
end *pb.CompiledCursor
err error
}
func (q *Query) clone() *Query {
x := *q
// Copy the contents of the slice-typed fields to a new backing store.
if len(q.filter) > 0 {
x.filter = make([]filter, len(q.filter))
copy(x.filter, q.filter)
}
if len(q.order) > 0 {
x.order = make([]order, len(q.order))
copy(x.order, q.order)
}
return &x
}
// Ancestor returns a derivative query with an ancestor filter.
// The ancestor should not be nil.
func (q *Query) Ancestor(ancestor *Key) *Query {
q = q.clone()
if ancestor == nil {
q.err = errors.New("datastore: nil query ancestor")
return q
}
q.ancestor = ancestor
return q
}
// EventualConsistency returns a derivative query that returns eventually
// consistent results.
// It only has an effect on ancestor queries.
func (q *Query) EventualConsistency() *Query {
q = q.clone()
q.eventual = true
return q
}
// Filter returns a derivative query with a field-based filter.
// The filterStr argument must be a field name followed by optional space,
// followed by an operator, one of ">", "<", ">=", "<=", or "=".
// Fields are compared against the provided value using the operator.
// Multiple filters are AND'ed together.
func (q *Query) Filter(filterStr string, value interface{}) *Query {
q = q.clone()
filterStr = strings.TrimSpace(filterStr)
if len(filterStr) < 1 {
q.err = errors.New("datastore: invalid filter: " + filterStr)
return q
}
f := filter{
FieldName: strings.TrimRight(filterStr, " ><=!"),
Value: value,
}
switch op := strings.TrimSpace(filterStr[len(f.FieldName):]); op {
case "<=":
f.Op = lessEq
case ">=":
f.Op = greaterEq
case "<":
f.Op = lessThan
case ">":
f.Op = greaterThan
case "=":
f.Op = equal
default:
q.err = fmt.Errorf("datastore: invalid operator %q in filter %q", op, filterStr)
return q
}
q.filter = append(q.filter, f)
return q
}
// Order returns a derivative query with a field-based sort order. Orders are
// applied in the order they are added. The default order is ascending; to sort
// in descending order prefix the fieldName with a minus sign (-).
func (q *Query) Order(fieldName string) *Query {
q = q.clone()
fieldName = strings.TrimSpace(fieldName)
o := order{
Direction: ascending,
FieldName: fieldName,
}
if strings.HasPrefix(fieldName, "-") {
o.Direction = descending
o.FieldName = strings.TrimSpace(fieldName[1:])
} else if strings.HasPrefix(fieldName, "+") {
q.err = fmt.Errorf("datastore: invalid order: %q", fieldName)
return q
}
if len(o.FieldName) == 0 {
q.err = errors.New("datastore: empty order")
return q
}
q.order = append(q.order, o)
return q
}
// Project returns a derivative query that yields only the given fields. It
// cannot be used with KeysOnly.
func (q *Query) Project(fieldNames ...string) *Query {
q = q.clone()
q.projection = append([]string(nil), fieldNames...)
return q
}
// Distinct returns a derivative query that yields de-duplicated entities with
// respect to the set of projected fields. It is only used for projection
// queries.
func (q *Query) Distinct() *Query {
q = q.clone()
q.distinct = true
return q
}
// KeysOnly returns a derivative query that yields only keys, not keys and
// entities. It cannot be used with projection queries.
func (q *Query) KeysOnly() *Query {
q = q.clone()
q.keysOnly = true
return q
}
// Limit returns a derivative query that has a limit on the number of results
// returned. A negative value means unlimited.
func (q *Query) Limit(limit int) *Query {
q = q.clone()
if limit < math.MinInt32 || limit > math.MaxInt32 {
q.err = errors.New("datastore: query limit overflow")
return q
}
q.limit = int32(limit)
return q
}
// Offset returns a derivative query that has an offset of how many keys to
// skip over before returning results. A negative value is invalid.
func (q *Query) Offset(offset int) *Query {
q = q.clone()
if offset < 0 {
q.err = errors.New("datastore: negative query offset")
return q
}
if offset > math.MaxInt32 {
q.err = errors.New("datastore: query offset overflow")
return q
}
q.offset = int32(offset)
return q
}
// Start returns a derivative query with the given start point.
func (q *Query) Start(c Cursor) *Query {
q = q.clone()
if c.cc == nil {
q.err = errors.New("datastore: invalid cursor")
return q
}
q.start = c.cc
return q
}
// End returns a derivative query with the given end point.
func (q *Query) End(c Cursor) *Query {
q = q.clone()
if c.cc == nil {
q.err = errors.New("datastore: invalid cursor")
return q
}
q.end = c.cc
return q
}
// toProto converts the query to a protocol buffer.
func (q *Query) toProto(dst *pb.Query, appID string) error {
if len(q.projection) != 0 && q.keysOnly {
return errors.New("datastore: query cannot both project and be keys-only")
}
dst.Reset()
dst.App = proto.String(appID)
if q.kind != "" {
dst.Kind = proto.String(q.kind)
}
if q.ancestor != nil {
dst.Ancestor = keyToProto(appID, q.ancestor)
if q.eventual {
dst.Strong = proto.Bool(false)
}
}
if q.projection != nil {
dst.PropertyName = q.projection
if q.distinct {
dst.GroupByPropertyName = q.projection
}
}
if q.keysOnly {
dst.KeysOnly = proto.Bool(true)
dst.RequirePerfectPlan = proto.Bool(true)
}
for _, qf := range q.filter {
if qf.FieldName == "" {
return errors.New("datastore: empty query filter field name")
}
p, errStr := valueToProto(appID, qf.FieldName, reflect.ValueOf(qf.Value), false)
if errStr != "" {
return errors.New("datastore: bad query filter value type: " + errStr)
}
xf := &pb.Query_Filter{
Op: operatorToProto[qf.Op],
Property: []*pb.Property{p},
}
if xf.Op == nil {
return errors.New("datastore: unknown query filter operator")
}
dst.Filter = append(dst.Filter, xf)
}
for _, qo := range q.order {
if qo.FieldName == "" {
return errors.New("datastore: empty query order field name")
}
xo := &pb.Query_Order{
Property: proto.String(qo.FieldName),
Direction: sortDirectionToProto[qo.Direction],
}
if xo.Direction == nil {
return errors.New("datastore: unknown query order direction")
}
dst.Order = append(dst.Order, xo)
}
if q.limit >= 0 {
dst.Limit = proto.Int32(q.limit)
}
if q.offset != 0 {
dst.Offset = proto.Int32(q.offset)
}
dst.CompiledCursor = q.start
dst.EndCompiledCursor = q.end
dst.Compile = proto.Bool(true)
return nil
}
// Count returns the number of results for the query.
func (q *Query) Count(c appengine.Context) (int, error) {
// Check that the query is well-formed.
if q.err != nil {
return 0, q.err
}
// Run a copy of the query, with keysOnly true (if we're not a projection,
// since the two are incompatible), and an adjusted offset. We also set the
// limit to zero, as we don't want any actual entity data, just the number
// of skipped results.
newQ := q.clone()
newQ.keysOnly = len(newQ.projection) == 0
newQ.limit = 0
if q.limit < 0 {
// If the original query was unlimited, set the new query's offset to maximum.
newQ.offset = math.MaxInt32
} else {
newQ.offset = q.offset + q.limit
if newQ.offset < 0 {
// Do the best we can, in the presence of overflow.
newQ.offset = math.MaxInt32
}
}
req := &pb.Query{}
if err := newQ.toProto(req, c.FullyQualifiedAppID()); err != nil {
return 0, err
}
res := &pb.QueryResult{}
if err := c.Call("datastore_v3", "RunQuery", req, res, nil); err != nil {
return 0, err
}
// n is the count we will return. For example, suppose that our original
// query had an offset of 4 and a limit of 2008: the count will be 2008,
// provided that there are at least 2012 matching entities. However, the
// RPCs will only skip 1000 results at a time. The RPC sequence is:
// call RunQuery with (offset, limit) = (2012, 0) // 2012 == newQ.offset
// response has (skippedResults, moreResults) = (1000, true)
// n += 1000 // n == 1000
// call Next with (offset, limit) = (1012, 0) // 1012 == newQ.offset - n
// response has (skippedResults, moreResults) = (1000, true)
// n += 1000 // n == 2000
// call Next with (offset, limit) = (12, 0) // 12 == newQ.offset - n
// response has (skippedResults, moreResults) = (12, false)
// n += 12 // n == 2012
// // exit the loop
// n -= 4 // n == 2008
var n int32
for {
// The QueryResult should have no actual entity data, just skipped results.
if len(res.Result) != 0 {
return 0, errors.New("datastore: internal error: Count request returned too much data")
}
n += res.GetSkippedResults()
if !res.GetMoreResults() {
break
}
if err := callNext(c, res, newQ.offset-n, 0); err != nil {
return 0, err
}
}
n -= q.offset
if n < 0 {
// If the offset was greater than the number of matching entities,
// return 0 instead of negative.
n = 0
}
return int(n), nil
}
// callNext issues a datastore_v3/Next RPC to advance a cursor, such as that
// returned by a query with more results.
func callNext(c appengine.Context, res *pb.QueryResult, offset, limit int32) error {
if res.Cursor == nil {
return errors.New("datastore: internal error: server did not return a cursor")
}
req := &pb.NextRequest{
Cursor: res.Cursor,
}
if limit >= 0 {
req.Count = proto.Int32(limit)
}
if offset != 0 {
req.Offset = proto.Int32(offset)
}
if res.CompiledCursor != nil {
req.Compile = proto.Bool(true)
}
res.Reset()
return c.Call("datastore_v3", "Next", req, res, nil)
}
// GetAll runs the query in the given context and returns all keys that match
// that query, as well as appending the values to dst.
//
// dst must have type *[]S or *[]*S or *[]P, for some struct type S or some non-
// interface, non-pointer type P such that P or *P implements PropertyLoadSaver.
//
// As a special case, *PropertyList is an invalid type for dst, even though a
// PropertyList is a slice of structs. It is treated as invalid to avoid being
// mistakenly passed when *[]PropertyList was intended.
//
// The keys returned by GetAll will be in a 1-1 correspondence with the entities
// added to dst.
//
// If q is a ``keys-only'' query, GetAll ignores dst and only returns the keys.
func (q *Query) GetAll(c appengine.Context, dst interface{}) ([]*Key, error) {
var (
dv reflect.Value
mat multiArgType
elemType reflect.Type
errFieldMismatch error
)
if !q.keysOnly {
dv = reflect.ValueOf(dst)
if dv.Kind() != reflect.Ptr || dv.IsNil() {
return nil, ErrInvalidEntityType
}
dv = dv.Elem()
mat, elemType = checkMultiArg(dv)
if mat == multiArgTypeInvalid || mat == multiArgTypeInterface {
return nil, ErrInvalidEntityType
}
}
var keys []*Key
for t := q.Run(c); ; {
k, e, err := t.next()
if err == Done {
break
}
if err != nil {
return keys, err
}
if !q.keysOnly {
ev := reflect.New(elemType)
if elemType.Kind() == reflect.Map {
// This is a special case. The zero values of a map type are
// not immediately useful; they have to be make'd.
//
// Funcs and channels are similar, in that a zero value is not useful,
// but even a freshly make'd channel isn't useful: there's no fixed
// channel buffer size that is always going to be large enough, and
// there's no goroutine to drain the other end. Theoretically, these
// types could be supported, for example by sniffing for a constructor
// method or requiring prior registration, but for now it's not a
// frequent enough concern to be worth it. Programmers can work around
// it by explicitly using Iterator.Next instead of the Query.GetAll
// convenience method.
x := reflect.MakeMap(elemType)
ev.Elem().Set(x)
}
if err = loadEntity(ev.Interface(), e); err != nil {
if _, ok := err.(*ErrFieldMismatch); ok {
// We continue loading entities even in the face of field mismatch errors.
// If we encounter any other error, that other error is returned. Otherwise,
// an ErrFieldMismatch is returned.
errFieldMismatch = err
} else {
return keys, err
}
}
if mat != multiArgTypeStructPtr {
ev = ev.Elem()
}
dv.Set(reflect.Append(dv, ev))
}
keys = append(keys, k)
}
return keys, errFieldMismatch
}
// Run runs the query in the given context.
func (q *Query) Run(c appengine.Context) *Iterator {
if q.err != nil {
return &Iterator{err: q.err}
}
t := &Iterator{
c: c,
limit: q.limit,
q: q,
prevCC: q.start,
}
var req pb.Query
if err := q.toProto(&req, c.FullyQualifiedAppID()); err != nil {
t.err = err
return t
}
if err := c.Call("datastore_v3", "RunQuery", &req, &t.res, nil); err != nil {
t.err = err
return t
}
offset := q.offset - t.res.GetSkippedResults()
for offset > 0 && t.res.GetMoreResults() {
t.prevCC = t.res.CompiledCursor
if err := callNext(t.c, &t.res, offset, t.limit); err != nil {
t.err = err
break
}
skip := t.res.GetSkippedResults()
if skip < 0 {
t.err = errors.New("datastore: internal error: negative number of skipped_results")
break
}
offset -= skip
}
if offset < 0 {
t.err = errors.New("datastore: internal error: query offset was overshot")
}
return t
}
// Iterator is the result of running a query.
type Iterator struct {
c appengine.Context
err error
// res is the result of the most recent RunQuery or Next API call.
res pb.QueryResult
// i is how many elements of res.Result we have iterated over.
i int
// limit is the limit on the number of results this iterator should return.
// A negative value means unlimited.
limit int32
// q is the original query which yielded this iterator.
q *Query
// prevCC is the compiled cursor that marks the end of the previous batch
// of results.
prevCC *pb.CompiledCursor
}
// Done is returned when a query iteration has completed.
var Done = errors.New("datastore: query has no more results")
// Next returns the key of the next result. When there are no more results,
// Done is returned as the error.
//
// If the query is not keys only and dst is non-nil, it also loads the entity
// stored for that key into the struct pointer or PropertyLoadSaver dst, with
// the same semantics and possible errors as for the Get function.
func (t *Iterator) Next(dst interface{}) (*Key, error) {
k, e, err := t.next()
if err != nil {
return nil, err
}
if dst != nil && !t.q.keysOnly {
err = loadEntity(dst, e)
}
return k, err
}
func (t *Iterator) next() (*Key, *pb.EntityProto, error) {
if t.err != nil {
return nil, nil, t.err
}
// Issue datastore_v3/Next RPCs as necessary.
for t.i == len(t.res.Result) {
if !t.res.GetMoreResults() {
t.err = Done
return nil, nil, t.err
}
t.prevCC = t.res.CompiledCursor
if err := callNext(t.c, &t.res, 0, t.limit); err != nil {
t.err = err
return nil, nil, t.err
}
if t.res.GetSkippedResults() != 0 {
t.err = errors.New("datastore: internal error: iterator has skipped results")
return nil, nil, t.err
}
t.i = 0
if t.limit >= 0 {
t.limit -= int32(len(t.res.Result))
if t.limit < 0 {
t.err = errors.New("datastore: internal error: query returned more results than the limit")
return nil, nil, t.err
}
}
}
// Extract the key from the t.i'th element of t.res.Result.
e := t.res.Result[t.i]
t.i++
if e.Key == nil {
return nil, nil, errors.New("datastore: internal error: server did not return a key")
}
k, err := protoToKey(e.Key)
if err != nil || k.Incomplete() {
return nil, nil, errors.New("datastore: internal error: server returned an invalid key")
}
return k, e, nil
}
// Cursor returns a cursor for the iterator's current location.
func (t *Iterator) Cursor() (Cursor, error) {
if t.err != nil && t.err != Done {
return Cursor{}, t.err
}
// If we are at either end of the current batch of results,
// return the compiled cursor at that end.
skipped := t.res.GetSkippedResults()
if t.i == 0 && skipped == 0 {
if t.prevCC == nil {
// A nil pointer (of type *pb.CompiledCursor) means no constraint:
// passing it as the end cursor of a new query means unlimited results
// (glossing over the integer limit parameter for now).
// A non-nil pointer to an empty pb.CompiledCursor means the start:
// passing it as the end cursor of a new query means 0 results.
// If prevCC was nil, then the original query had no start cursor, but
// Iterator.Cursor should return "the start" instead of unlimited.
return Cursor{&zeroCC}, nil
}
return Cursor{t.prevCC}, nil
}
if t.i == len(t.res.Result) {
return Cursor{t.res.CompiledCursor}, nil
}
// Otherwise, re-run the query offset to this iterator's position, starting from
// the most recent compiled cursor. This is done on a best-effort basis, as it
// is racy; if a concurrent process has added or removed entities, then the
// cursor returned may be inconsistent.
q := t.q.clone()
q.start = t.prevCC
q.offset = skipped + int32(t.i)
q.limit = 0
q.keysOnly = len(q.projection) == 0
t1 := q.Run(t.c)
_, _, err := t1.next()
if err != Done {
if err == nil {
err = fmt.Errorf("datastore: internal error: zero-limit query did not have zero results")
}
return Cursor{}, err
}
return Cursor{t1.res.CompiledCursor}, nil
}
var zeroCC pb.CompiledCursor
// Cursor is an iterator's position. It can be converted to and from an opaque
// string. A cursor can be used from different HTTP requests, but only with a
// query with the same kind, ancestor, filter and order constraints.
type Cursor struct {
cc *pb.CompiledCursor
}
// String returns a base-64 string representation of a cursor.
func (c Cursor) String() string {
if c.cc == nil {
return ""
}
b, err := proto.Marshal(c.cc)
if err != nil {
// The only way to construct a Cursor with a non-nil cc field is to
// unmarshal from the byte representation. We panic if the unmarshal
// succeeds but the marshaling of the unchanged protobuf value fails.
panic(fmt.Sprintf("datastore: internal error: malformed cursor: %v", err))
}
return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=")
}
// Decode decodes a cursor from its base-64 string representation.
func DecodeCursor(s string) (Cursor, error) {
if s == "" {
return Cursor{&zeroCC}, nil
}
if n := len(s) % 4; n != 0 {
s += strings.Repeat("=", 4-n)
}
b, err := base64.URLEncoding.DecodeString(s)
if err != nil {
return Cursor{}, err
}
cc := &pb.CompiledCursor{}
if err := proto.Unmarshal(b, cc); err != nil {
return Cursor{}, err
}
return Cursor{cc}, nil
}

View File

@@ -1,580 +0,0 @@
// Copyright 2011 Google Inc. All Rights Reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package datastore
import (
"errors"
"fmt"
"reflect"
"strings"
"testing"
"github.com/golang/protobuf/proto"
"google.golang.org/appengine/internal/aetesting"
pb "google.golang.org/appengine/internal/datastore"
)
var (
path1 = &pb.Path{
Element: []*pb.Path_Element{
{
Type: proto.String("Gopher"),
Id: proto.Int64(6),
},
},
}
path2 = &pb.Path{
Element: []*pb.Path_Element{
{
Type: proto.String("Gopher"),
Id: proto.Int64(6),
},
{
Type: proto.String("Gopher"),
Id: proto.Int64(8),
},
},
}
)
func fakeRunQuery(in *pb.Query, out *pb.QueryResult) error {
expectedIn := &pb.Query{
App: proto.String("dev~fake-app"),
Kind: proto.String("Gopher"),
Compile: proto.Bool(true),
}
if !proto.Equal(in, expectedIn) {
return fmt.Errorf("unsupported argument: got %v want %v", in, expectedIn)
}
*out = pb.QueryResult{
Result: []*pb.EntityProto{
{
Key: &pb.Reference{
App: proto.String("s~test-app"),
Path: path1,
},
EntityGroup: path1,
Property: []*pb.Property{
{
Meaning: pb.Property_TEXT.Enum(),
Name: proto.String("Name"),
Value: &pb.PropertyValue{
StringValue: proto.String("George"),
},
},
{
Name: proto.String("Height"),
Value: &pb.PropertyValue{
Int64Value: proto.Int64(32),
},
},
},
},
{
Key: &pb.Reference{
App: proto.String("s~test-app"),
Path: path2,
},
EntityGroup: path1, // ancestor is George
Property: []*pb.Property{
{
Meaning: pb.Property_TEXT.Enum(),
Name: proto.String("Name"),
Value: &pb.PropertyValue{
StringValue: proto.String("Rufus"),
},
},
// No height for Rufus.
},
},
},
MoreResults: proto.Bool(false),
}
return nil
}
type StructThatImplementsPLS struct{}
func (StructThatImplementsPLS) Load(p []Property) error { return nil }
func (StructThatImplementsPLS) Save() ([]Property, error) { return nil, nil }
var _ PropertyLoadSaver = StructThatImplementsPLS{}
type StructPtrThatImplementsPLS struct{}
func (*StructPtrThatImplementsPLS) Load(p []Property) error { return nil }
func (*StructPtrThatImplementsPLS) Save() ([]Property, error) { return nil, nil }
var _ PropertyLoadSaver = &StructPtrThatImplementsPLS{}
type PropertyMap map[string]Property
func (m PropertyMap) Load(props []Property) error {
for _, p := range props {
if p.Multiple {
return errors.New("PropertyMap does not support multiple properties")
}
m[p.Name] = p
}
return nil
}
func (m PropertyMap) Save() ([]Property, error) {
props := make([]Property, 0, len(m))
for _, p := range m {
if p.Multiple {
return nil, errors.New("PropertyMap does not support multiple properties")
}
props = append(props, p)
}
return props, nil
}
var _ PropertyLoadSaver = PropertyMap{}
type Gopher struct {
Name string
Height int
}
// typeOfEmptyInterface is the type of interface{}, but we can't use
// reflect.TypeOf((interface{})(nil)) directly because TypeOf takes an
// interface{}.
var typeOfEmptyInterface = reflect.TypeOf((*interface{})(nil)).Elem()
func TestCheckMultiArg(t *testing.T) {
testCases := []struct {
v interface{}
mat multiArgType
elemType reflect.Type
}{
// Invalid cases.
{nil, multiArgTypeInvalid, nil},
{Gopher{}, multiArgTypeInvalid, nil},
{&Gopher{}, multiArgTypeInvalid, nil},
{PropertyList{}, multiArgTypeInvalid, nil}, // This is a special case.
{PropertyMap{}, multiArgTypeInvalid, nil},
{[]*PropertyList(nil), multiArgTypeInvalid, nil},
{[]*PropertyMap(nil), multiArgTypeInvalid, nil},
{[]**Gopher(nil), multiArgTypeInvalid, nil},
{[]*interface{}(nil), multiArgTypeInvalid, nil},
// Valid cases.
{
[]PropertyList(nil),
multiArgTypePropertyLoadSaver,
reflect.TypeOf(PropertyList{}),
},
{
[]PropertyMap(nil),
multiArgTypePropertyLoadSaver,
reflect.TypeOf(PropertyMap{}),
},
{
[]StructThatImplementsPLS(nil),
multiArgTypePropertyLoadSaver,
reflect.TypeOf(StructThatImplementsPLS{}),
},
{
[]StructPtrThatImplementsPLS(nil),
multiArgTypePropertyLoadSaver,
reflect.TypeOf(StructPtrThatImplementsPLS{}),
},
{
[]Gopher(nil),
multiArgTypeStruct,
reflect.TypeOf(Gopher{}),
},
{
[]*Gopher(nil),
multiArgTypeStructPtr,
reflect.TypeOf(Gopher{}),
},
{
[]interface{}(nil),
multiArgTypeInterface,
typeOfEmptyInterface,
},
}
for _, tc := range testCases {
mat, elemType := checkMultiArg(reflect.ValueOf(tc.v))
if mat != tc.mat || elemType != tc.elemType {
t.Errorf("checkMultiArg(%T): got %v, %v want %v, %v",
tc.v, mat, elemType, tc.mat, tc.elemType)
}
}
}
func TestSimpleQuery(t *testing.T) {
struct1 := Gopher{Name: "George", Height: 32}
struct2 := Gopher{Name: "Rufus"}
pList1 := PropertyList{
{
Name: "Name",
Value: "George",
},
{
Name: "Height",
Value: int64(32),
},
}
pList2 := PropertyList{
{
Name: "Name",
Value: "Rufus",
},
}
pMap1 := PropertyMap{
"Name": Property{
Name: "Name",
Value: "George",
},
"Height": Property{
Name: "Height",
Value: int64(32),
},
}
pMap2 := PropertyMap{
"Name": Property{
Name: "Name",
Value: "Rufus",
},
}
testCases := []struct {
dst interface{}
want interface{}
}{
// The destination must have type *[]P, *[]S or *[]*S, for some non-interface
// type P such that *P implements PropertyLoadSaver, or for some struct type S.
{new([]Gopher), &[]Gopher{struct1, struct2}},
{new([]*Gopher), &[]*Gopher{&struct1, &struct2}},
{new([]PropertyList), &[]PropertyList{pList1, pList2}},
{new([]PropertyMap), &[]PropertyMap{pMap1, pMap2}},
// Any other destination type is invalid.
{0, nil},
{Gopher{}, nil},
{PropertyList{}, nil},
{PropertyMap{}, nil},
{[]int{}, nil},
{[]Gopher{}, nil},
{[]PropertyList{}, nil},
{new(int), nil},
{new(Gopher), nil},
{new(PropertyList), nil}, // This is a special case.
{new(PropertyMap), nil},
{new([]int), nil},
{new([]map[int]int), nil},
{new([]map[string]Property), nil},
{new([]map[string]interface{}), nil},
{new([]*int), nil},
{new([]*map[int]int), nil},
{new([]*map[string]Property), nil},
{new([]*map[string]interface{}), nil},
{new([]**Gopher), nil},
{new([]*PropertyList), nil},
{new([]*PropertyMap), nil},
}
for _, tc := range testCases {
nCall := 0
c := aetesting.FakeSingleContext(t, "datastore_v3", "RunQuery", func(in *pb.Query, out *pb.QueryResult) error {
nCall++
return fakeRunQuery(in, out)
})
var (
expectedErr error
expectedNCall int
)
if tc.want == nil {
expectedErr = ErrInvalidEntityType
} else {
expectedNCall = 1
}
keys, err := NewQuery("Gopher").GetAll(c, tc.dst)
if err != expectedErr {
t.Errorf("dst type %T: got error %v, want %v", tc.dst, err, expectedErr)
continue
}
if nCall != expectedNCall {
t.Errorf("dst type %T: Context.Call was called an incorrect number of times: got %d want %d", tc.dst, nCall, expectedNCall)
continue
}
if err != nil {
continue
}
key1 := NewKey(c, "Gopher", "", 6, nil)
expectedKeys := []*Key{
key1,
NewKey(c, "Gopher", "", 8, key1),
}
if l1, l2 := len(keys), len(expectedKeys); l1 != l2 {
t.Errorf("dst type %T: got %d keys, want %d keys", tc.dst, l1, l2)
continue
}
for i, key := range keys {
if key.AppID() != "s~test-app" {
t.Errorf(`dst type %T: Key #%d's AppID = %q, want "s~test-app"`, tc.dst, i, key.AppID())
continue
}
if !keysEqual(key, expectedKeys[i]) {
t.Errorf("dst type %T: got key #%d %v, want %v", tc.dst, i, key, expectedKeys[i])
continue
}
}
if !reflect.DeepEqual(tc.dst, tc.want) {
t.Errorf("dst type %T: Entities got %+v, want %+v", tc.dst, tc.dst, tc.want)
continue
}
}
}
// keysEqual is like (*Key).Equal, but ignores the App ID.
func keysEqual(a, b *Key) bool {
for a != nil && b != nil {
if a.Kind() != b.Kind() || a.StringID() != b.StringID() || a.IntID() != b.IntID() {
return false
}
a, b = a.Parent(), b.Parent()
}
return a == b
}
func TestQueriesAreImmutable(t *testing.T) {
// Test that deriving q2 from q1 does not modify q1.
q0 := NewQuery("foo")
q1 := NewQuery("foo")
q2 := q1.Offset(2)
if !reflect.DeepEqual(q0, q1) {
t.Errorf("q0 and q1 were not equal")
}
if reflect.DeepEqual(q1, q2) {
t.Errorf("q1 and q2 were equal")
}
// Test that deriving from q4 twice does not conflict, even though
// q4 has a long list of order clauses. This tests that the arrays
// backed by a query's slice of orders are not shared.
f := func() *Query {
q := NewQuery("bar")
// 47 is an ugly number that is unlikely to be near a re-allocation
// point in repeated append calls. For example, it's not near a power
// of 2 or a multiple of 10.
for i := 0; i < 47; i++ {
q = q.Order(fmt.Sprintf("x%d", i))
}
return q
}
q3 := f().Order("y")
q4 := f()
q5 := q4.Order("y")
q6 := q4.Order("z")
if !reflect.DeepEqual(q3, q5) {
t.Errorf("q3 and q5 were not equal")
}
if reflect.DeepEqual(q5, q6) {
t.Errorf("q5 and q6 were equal")
}
}
func TestFilterParser(t *testing.T) {
testCases := []struct {
filterStr string
wantOK bool
wantFieldName string
wantOp operator
}{
// Supported ops.
{"x<", true, "x", lessThan},
{"x <", true, "x", lessThan},
{"x <", true, "x", lessThan},
{" x < ", true, "x", lessThan},
{"x <=", true, "x", lessEq},
{"x =", true, "x", equal},
{"x >=", true, "x", greaterEq},
{"x >", true, "x", greaterThan},
{"in >", true, "in", greaterThan},
{"in>", true, "in", greaterThan},
// Valid but (currently) unsupported ops.
{"x!=", false, "", 0},
{"x !=", false, "", 0},
{" x != ", false, "", 0},
{"x IN", false, "", 0},
{"x in", false, "", 0},
// Invalid ops.
{"x EQ", false, "", 0},
{"x lt", false, "", 0},
{"x <>", false, "", 0},
{"x >>", false, "", 0},
{"x ==", false, "", 0},
{"x =<", false, "", 0},
{"x =>", false, "", 0},
{"x !", false, "", 0},
{"x ", false, "", 0},
{"x", false, "", 0},
}
for _, tc := range testCases {
q := NewQuery("foo").Filter(tc.filterStr, 42)
if ok := q.err == nil; ok != tc.wantOK {
t.Errorf("%q: ok=%t, want %t", tc.filterStr, ok, tc.wantOK)
continue
}
if !tc.wantOK {
continue
}
if len(q.filter) != 1 {
t.Errorf("%q: len=%d, want %d", tc.filterStr, len(q.filter), 1)
continue
}
got, want := q.filter[0], filter{tc.wantFieldName, tc.wantOp, 42}
if got != want {
t.Errorf("%q: got %v, want %v", tc.filterStr, got, want)
continue
}
}
}
func TestQueryToProto(t *testing.T) {
// The context is required to make Keys for the test cases.
var got *pb.Query
NoErr := errors.New("No error")
c := aetesting.FakeSingleContext(t, "datastore_v3", "RunQuery", func(in *pb.Query, out *pb.QueryResult) error {
got = in
return NoErr // return a non-nil error so Run doesn't keep going.
})
testCases := []struct {
desc string
query *Query
want *pb.Query
err string
}{
{
desc: "empty",
query: NewQuery(""),
want: &pb.Query{},
},
{
desc: "standard query",
query: NewQuery("kind").Order("-I").Filter("I >", 17).Filter("U =", "Dave").Limit(7).Offset(42),
want: &pb.Query{
Kind: proto.String("kind"),
Filter: []*pb.Query_Filter{
{
Op: pb.Query_Filter_GREATER_THAN.Enum(),
Property: []*pb.Property{
{
Name: proto.String("I"),
Value: &pb.PropertyValue{Int64Value: proto.Int64(17)},
Multiple: proto.Bool(false),
},
},
},
{
Op: pb.Query_Filter_EQUAL.Enum(),
Property: []*pb.Property{
{
Name: proto.String("U"),
Value: &pb.PropertyValue{StringValue: proto.String("Dave")},
Multiple: proto.Bool(false),
},
},
},
},
Order: []*pb.Query_Order{
{
Property: proto.String("I"),
Direction: pb.Query_Order_DESCENDING.Enum(),
},
},
Limit: proto.Int32(7),
Offset: proto.Int32(42),
},
},
{
desc: "ancestor",
query: NewQuery("").Ancestor(NewKey(c, "kind", "Mummy", 0, nil)),
want: &pb.Query{
Ancestor: &pb.Reference{
App: proto.String("dev~fake-app"),
Path: &pb.Path{
Element: []*pb.Path_Element{{Type: proto.String("kind"), Name: proto.String("Mummy")}},
},
},
},
},
{
desc: "projection",
query: NewQuery("").Project("A", "B"),
want: &pb.Query{
PropertyName: []string{"A", "B"},
},
},
{
desc: "projection with distinct",
query: NewQuery("").Project("A", "B").Distinct(),
want: &pb.Query{
PropertyName: []string{"A", "B"},
GroupByPropertyName: []string{"A", "B"},
},
},
{
desc: "keys only",
query: NewQuery("").KeysOnly(),
want: &pb.Query{
KeysOnly: proto.Bool(true),
RequirePerfectPlan: proto.Bool(true),
},
},
{
desc: "empty filter",
query: NewQuery("kind").Filter("=", 17),
err: "empty query filter field nam",
},
{
desc: "bad filter type",
query: NewQuery("kind").Filter("M =", map[string]bool{}),
err: "bad query filter value type",
},
{
desc: "bad filter operator",
query: NewQuery("kind").Filter("I <<=", 17),
err: `invalid operator "<<=" in filter "I <<="`,
},
{
desc: "empty order",
query: NewQuery("kind").Order(""),
err: "empty order",
},
{
desc: "bad order direction",
query: NewQuery("kind").Order("+I"),
err: `invalid order: "+I`,
},
}
for _, tt := range testCases {
got = nil
if _, err := tt.query.Run(c).Next(nil); err != NoErr {
if tt.err == "" || !strings.Contains(err.Error(), tt.err) {
t.Errorf("%s: error %v, want %q", tt.desc, err, tt.err)
}
continue
}
if tt.err != "" {
t.Errorf("%s: no error, want %q", tt.desc, tt.err)
continue
}
// Fields that are common to all protos.
tt.want.App = proto.String("dev~fake-app")
tt.want.Compile = proto.Bool(true)
if !proto.Equal(got, tt.want) {
t.Errorf("%s:\ngot %v\nwant %v", tt.desc, got, tt.want)
}
}
}

View File

@@ -1,300 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package datastore
import (
"errors"
"fmt"
"math"
"reflect"
"time"
"github.com/golang/protobuf/proto"
"google.golang.org/appengine"
pb "google.golang.org/appengine/internal/datastore"
)
func toUnixMicro(t time.Time) int64 {
// We cannot use t.UnixNano() / 1e3 because we want to handle times more than
// 2^63 nanoseconds (which is about 292 years) away from 1970, and those cannot
// be represented in the numerator of a single int64 divide.
return t.Unix()*1e6 + int64(t.Nanosecond()/1e3)
}
func fromUnixMicro(t int64) time.Time {
return time.Unix(t/1e6, (t%1e6)*1e3)
}
var (
minTime = time.Unix(int64(math.MinInt64)/1e6, (int64(math.MinInt64)%1e6)*1e3)
maxTime = time.Unix(int64(math.MaxInt64)/1e6, (int64(math.MaxInt64)%1e6)*1e3)
)
// valueToProto converts a named value to a newly allocated Property.
// The returned error string is empty on success.
func valueToProto(defaultAppID, name string, v reflect.Value, multiple bool) (p *pb.Property, errStr string) {
var (
pv pb.PropertyValue
unsupported bool
)
switch v.Kind() {
case reflect.Invalid:
// No-op.
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
pv.Int64Value = proto.Int64(v.Int())
case reflect.Bool:
pv.BooleanValue = proto.Bool(v.Bool())
case reflect.String:
pv.StringValue = proto.String(v.String())
case reflect.Float32, reflect.Float64:
pv.DoubleValue = proto.Float64(v.Float())
case reflect.Ptr:
if k, ok := v.Interface().(*Key); ok {
if k != nil {
pv.Referencevalue = keyToReferenceValue(defaultAppID, k)
}
} else {
unsupported = true
}
case reflect.Struct:
switch t := v.Interface().(type) {
case time.Time:
if t.Before(minTime) || t.After(maxTime) {
return nil, "time value out of range"
}
pv.Int64Value = proto.Int64(toUnixMicro(t))
case appengine.GeoPoint:
if !t.Valid() {
return nil, "invalid GeoPoint value"
}
// NOTE: Strangely, latitude maps to X, longitude to Y.
pv.Pointvalue = &pb.PropertyValue_PointValue{X: &t.Lat, Y: &t.Lng}
default:
unsupported = true
}
case reflect.Slice:
if b, ok := v.Interface().([]byte); ok {
pv.StringValue = proto.String(string(b))
} else {
// nvToProto should already catch slice values.
// If we get here, we have a slice of slice values.
unsupported = true
}
default:
unsupported = true
}
if unsupported {
return nil, "unsupported datastore value type: " + v.Type().String()
}
p = &pb.Property{
Name: proto.String(name),
Value: &pv,
Multiple: proto.Bool(multiple),
}
if v.IsValid() {
switch v.Interface().(type) {
case []byte:
p.Meaning = pb.Property_BLOB.Enum()
case ByteString:
p.Meaning = pb.Property_BYTESTRING.Enum()
case appengine.BlobKey:
p.Meaning = pb.Property_BLOBKEY.Enum()
case time.Time:
p.Meaning = pb.Property_GD_WHEN.Enum()
case appengine.GeoPoint:
p.Meaning = pb.Property_GEORSS_POINT.Enum()
}
}
return p, ""
}
// saveEntity saves an EntityProto into a PropertyLoadSaver or struct pointer.
func saveEntity(defaultAppID string, key *Key, src interface{}) (*pb.EntityProto, error) {
var err error
var props []Property
if e, ok := src.(PropertyLoadSaver); ok {
props, err = e.Save()
} else {
props, err = SaveStruct(src)
}
if err != nil {
return nil, err
}
return propertiesToProto(defaultAppID, key, props)
}
func saveStructProperty(props *[]Property, name string, noIndex, multiple bool, v reflect.Value) error {
p := Property{
Name: name,
NoIndex: noIndex,
Multiple: multiple,
}
switch x := v.Interface().(type) {
case *Key:
p.Value = x
case time.Time:
p.Value = x
case appengine.BlobKey:
p.Value = x
case appengine.GeoPoint:
p.Value = x
case ByteString:
p.Value = x
default:
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
p.Value = v.Int()
case reflect.Bool:
p.Value = v.Bool()
case reflect.String:
p.Value = v.String()
case reflect.Float32, reflect.Float64:
p.Value = v.Float()
case reflect.Slice:
if v.Type().Elem().Kind() == reflect.Uint8 {
p.NoIndex = true
p.Value = v.Bytes()
}
case reflect.Struct:
if !v.CanAddr() {
return fmt.Errorf("datastore: unsupported struct field: value is unaddressable")
}
sub, err := newStructPLS(v.Addr().Interface())
if err != nil {
return fmt.Errorf("datastore: unsupported struct field: %v", err)
}
return sub.(structPLS).save(props, name, noIndex, multiple)
}
}
if p.Value == nil {
return fmt.Errorf("datastore: unsupported struct field type: %v", v.Type())
}
*props = append(*props, p)
return nil
}
func (s structPLS) Save() ([]Property, error) {
var props []Property
if err := s.save(&props, "", false, false); err != nil {
return nil, err
}
return props, nil
}
func (s structPLS) save(props *[]Property, prefix string, noIndex, multiple bool) error {
for i, t := range s.codec.byIndex {
if t.name == "-" {
continue
}
name := t.name
if prefix != "" {
name = prefix + name
}
v := s.v.Field(i)
if !v.IsValid() || !v.CanSet() {
continue
}
noIndex1 := noIndex || t.noIndex
// For slice fields that aren't []byte, save each element.
if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 {
for j := 0; j < v.Len(); j++ {
if err := saveStructProperty(props, name, noIndex1, true, v.Index(j)); err != nil {
return err
}
}
continue
}
// Otherwise, save the field itself.
if err := saveStructProperty(props, name, noIndex1, multiple, v); err != nil {
return err
}
}
return nil
}
func propertiesToProto(defaultAppID string, key *Key, props []Property) (*pb.EntityProto, error) {
e := &pb.EntityProto{
Key: keyToProto(defaultAppID, key),
}
if key.parent == nil {
e.EntityGroup = &pb.Path{}
} else {
e.EntityGroup = keyToProto(defaultAppID, key.root()).Path
}
prevMultiple := make(map[string]bool)
for _, p := range props {
if pm, ok := prevMultiple[p.Name]; ok {
if !pm || !p.Multiple {
return nil, fmt.Errorf("datastore: multiple Properties with Name %q, but Multiple is false", p.Name)
}
} else {
prevMultiple[p.Name] = p.Multiple
}
x := &pb.Property{
Name: proto.String(p.Name),
Value: new(pb.PropertyValue),
Multiple: proto.Bool(p.Multiple),
}
switch v := p.Value.(type) {
case int64:
x.Value.Int64Value = proto.Int64(v)
case bool:
x.Value.BooleanValue = proto.Bool(v)
case string:
x.Value.StringValue = proto.String(v)
if p.NoIndex {
x.Meaning = pb.Property_TEXT.Enum()
}
case float64:
x.Value.DoubleValue = proto.Float64(v)
case *Key:
if v != nil {
x.Value.Referencevalue = keyToReferenceValue(defaultAppID, v)
}
case time.Time:
if v.Before(minTime) || v.After(maxTime) {
return nil, fmt.Errorf("datastore: time value out of range")
}
x.Value.Int64Value = proto.Int64(toUnixMicro(v))
x.Meaning = pb.Property_GD_WHEN.Enum()
case appengine.BlobKey:
x.Value.StringValue = proto.String(string(v))
x.Meaning = pb.Property_BLOBKEY.Enum()
case appengine.GeoPoint:
if !v.Valid() {
return nil, fmt.Errorf("datastore: invalid GeoPoint value")
}
// NOTE: Strangely, latitude maps to X, longitude to Y.
x.Value.Pointvalue = &pb.PropertyValue_PointValue{X: &v.Lat, Y: &v.Lng}
x.Meaning = pb.Property_GEORSS_POINT.Enum()
case []byte:
x.Value.StringValue = proto.String(string(v))
x.Meaning = pb.Property_BLOB.Enum()
if !p.NoIndex {
return nil, fmt.Errorf("datastore: cannot index a []byte valued Property with Name %q", p.Name)
}
case ByteString:
x.Value.StringValue = proto.String(string(v))
x.Meaning = pb.Property_BYTESTRING.Enum()
default:
if p.Value != nil {
return nil, fmt.Errorf("datastore: invalid Value type for a Property with Name %q", p.Name)
}
}
if p.NoIndex {
e.RawProperty = append(e.RawProperty, x)
} else {
e.Property = append(e.Property, x)
if len(e.Property) > maxIndexedProperties {
return nil, errors.New("datastore: too many indexed properties")
}
}
}
return e, nil
}

View File

@@ -1,65 +0,0 @@
// Copyright 2012 Google Inc. All Rights Reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package datastore
import (
"testing"
"time"
)
func TestUnixMicro(t *testing.T) {
// Test that all these time.Time values survive a round trip to unix micros.
testCases := []time.Time{
{},
time.Date(2, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(23, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(234, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(1000, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(1600, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(1700, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(1800, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC),
time.Unix(-1e6, -1000),
time.Unix(-1e6, 0),
time.Unix(-1e6, +1000),
time.Unix(-60, -1000),
time.Unix(-60, 0),
time.Unix(-60, +1000),
time.Unix(-1, -1000),
time.Unix(-1, 0),
time.Unix(-1, +1000),
time.Unix(0, -3000),
time.Unix(0, -2000),
time.Unix(0, -1000),
time.Unix(0, 0),
time.Unix(0, +1000),
time.Unix(0, +2000),
time.Unix(+60, -1000),
time.Unix(+60, 0),
time.Unix(+60, +1000),
time.Unix(+1e6, -1000),
time.Unix(+1e6, 0),
time.Unix(+1e6, +1000),
time.Date(1999, 12, 31, 23, 59, 59, 999000, time.UTC),
time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2006, 1, 2, 15, 4, 5, 678000, time.UTC),
time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC),
time.Date(3456, 1, 1, 0, 0, 0, 0, time.UTC),
}
for _, tc := range testCases {
got := fromUnixMicro(toUnixMicro(tc))
if !got.Equal(tc) {
t.Errorf("got %q, want %q", got, tc)
}
}
// Test that a time.Time that isn't an integral number of microseconds
// is not perfectly reconstructed after a round trip.
t0 := time.Unix(0, 123)
t1 := fromUnixMicro(toUnixMicro(t0))
if t1.Nanosecond()%1000 != 0 || t0.Nanosecond()%1000 == 0 {
t.Errorf("quantization to µs: got %q with %d ns, started with %d ns", t1, t1.Nanosecond(), t0.Nanosecond())
}
}

View File

@@ -1,138 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package datastore
import (
"errors"
"github.com/golang/protobuf/proto"
"google.golang.org/appengine"
"google.golang.org/appengine/internal"
basepb "google.golang.org/appengine/internal/base"
pb "google.golang.org/appengine/internal/datastore"
)
func init() {
internal.RegisterTransactionSetter(func(x *pb.Query, t *pb.Transaction) {
x.Transaction = t
})
internal.RegisterTransactionSetter(func(x *pb.GetRequest, t *pb.Transaction) {
x.Transaction = t
})
internal.RegisterTransactionSetter(func(x *pb.PutRequest, t *pb.Transaction) {
x.Transaction = t
})
internal.RegisterTransactionSetter(func(x *pb.DeleteRequest, t *pb.Transaction) {
x.Transaction = t
})
}
// ErrConcurrentTransaction is returned when a transaction is rolled back due
// to a conflict with a concurrent transaction.
var ErrConcurrentTransaction = errors.New("datastore: concurrent transaction")
type transaction struct {
appengine.Context
transaction pb.Transaction
finished bool
}
func (t *transaction) Call(service, method string, in, out proto.Message, opts *internal.CallOptions) error {
if t.finished {
return errors.New("datastore: transaction context has expired")
}
internal.ApplyTransaction(in, &t.transaction)
return t.Context.Call(service, method, in, out, opts)
}
func runOnce(c appengine.Context, f func(appengine.Context) error, opts *TransactionOptions) error {
// Begin the transaction.
t := &transaction{Context: c}
req := &pb.BeginTransactionRequest{
App: proto.String(c.FullyQualifiedAppID()),
}
if opts != nil && opts.XG {
req.AllowMultipleEg = proto.Bool(true)
}
if err := t.Context.Call("datastore_v3", "BeginTransaction", req, &t.transaction, nil); err != nil {
return err
}
// Call f, rolling back the transaction if f returns a non-nil error, or panics.
// The panic is not recovered.
defer func() {
if t.finished {
return
}
t.finished = true
// Ignore the error return value, since we are already returning a non-nil
// error (or we're panicking).
c.Call("datastore_v3", "Rollback", &t.transaction, &basepb.VoidProto{}, nil)
}()
if err := f(t); err != nil {
return err
}
t.finished = true
// Commit the transaction.
res := &pb.CommitResponse{}
err := c.Call("datastore_v3", "Commit", &t.transaction, res, nil)
if ae, ok := err.(*internal.APIError); ok {
if appengine.IsDevAppServer() {
// The Python Dev AppServer raises an ApplicationError with error code 2 (which is
// Error.CONCURRENT_TRANSACTION) and message "Concurrency exception.".
if ae.Code == int32(pb.Error_BAD_REQUEST) && ae.Detail == "ApplicationError: 2 Concurrency exception." {
return ErrConcurrentTransaction
}
}
if ae.Code == int32(pb.Error_CONCURRENT_TRANSACTION) {
return ErrConcurrentTransaction
}
}
return err
}
// RunInTransaction runs f in a transaction. It calls f with a transaction
// context tc that f should use for all App Engine operations.
//
// If f returns nil, RunInTransaction attempts to commit the transaction,
// returning nil if it succeeds. If the commit fails due to a conflicting
// transaction, RunInTransaction retries f, each time with a new transaction
// context. It gives up and returns ErrConcurrentTransaction after three
// failed attempts.
//
// If f returns non-nil, then any datastore changes will not be applied and
// RunInTransaction returns that same error. The function f is not retried.
//
// Note that when f returns, the transaction is not yet committed. Calling code
// must be careful not to assume that any of f's changes have been committed
// until RunInTransaction returns nil.
//
// Nested transactions are not supported; c may not be a transaction context.
func RunInTransaction(c appengine.Context, f func(tc appengine.Context) error, opts *TransactionOptions) error {
if _, ok := c.(*transaction); ok {
return errors.New("datastore: nested transactions are not supported")
}
for i := 0; i < 3; i++ {
if err := runOnce(c, f, opts); err != ErrConcurrentTransaction {
return err
}
}
return ErrConcurrentTransaction
}
// TransactionOptions are the options for running a transaction.
type TransactionOptions struct {
// XG is whether the transaction can cross multiple entity groups. In
// comparison, a single group transaction is one where all datastore keys
// used have the same root key. Note that cross group transactions do not
// have the same behavior as single group transactions. In particular, it
// is much more likely to see partially applied transactions in different
// entity groups, in global queries.
// It is valid to set XG to true even if the transaction is within a
// single entity group.
XG bool
}

View File

@@ -1,275 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package delay provides a way to execute code outside the scope of a
user request by using the taskqueue API.
To declare a function that may be executed later, call Func
in a top-level assignment context, passing it an arbitrary string key
and a function whose first argument is of type appengine.Context.
var laterFunc = delay.Func("key", myFunc)
It is also possible to use a function literal.
var laterFunc = delay.Func("key", func(c appengine.Context, x string) {
// ...
})
To call a function, invoke its Call method.
laterFunc.Call(c, "something")
A function may be called any number of times. If the function has any
return arguments, and the last one is of type error, the function may
return a non-nil error to signal that the function should be retried.
The arguments to functions may be of any type that is encodable by the gob
package. If an argument is of interface type, it is the client's responsibility
to register with the gob package whatever concrete type may be passed for that
argument; see http://golang.org/pkg/gob/#Register for details.
Any errors during initialization or execution of a function will be
logged to the application logs. Error logs that occur during initialization will
be associated with the request that invoked the Call method.
The state of a function invocation that has not yet successfully
executed is preserved by combining the file name in which it is declared
with the string key that was passed to the Func function. Updating an app
with pending function invocations is safe as long as the relevant
functions have the (filename, key) combination preserved.
The delay package uses the Task Queue API to create tasks that call the
reserved application path "/_ah/queue/go/delay".
This path must not be marked as "login: required" in app.yaml;
it must be marked as "login: admin" or have no access restriction.
*/
package delay
import (
"bytes"
"encoding/gob"
"errors"
"fmt"
"net/http"
"reflect"
"runtime"
"google.golang.org/appengine"
"google.golang.org/appengine/taskqueue"
)
// Function represents a function that may have a delayed invocation.
type Function struct {
fv reflect.Value // Kind() == reflect.Func
key string
err error // any error during initialization
}
const (
// The HTTP path for invocations.
path = "/_ah/queue/go/delay"
// Use the default queue.
queue = ""
)
var (
// registry of all delayed functions
funcs = make(map[string]*Function)
// precomputed types
contextType = reflect.TypeOf((*appengine.Context)(nil)).Elem()
errorType = reflect.TypeOf((*error)(nil)).Elem()
// errors
errFirstArg = errors.New("first argument must be appengine.Context")
)
// Func declares a new Function. The second argument must be a function with a
// first argument of type appengine.Context.
// This function must be called at program initialization time. That means it
// must be called in a global variable declaration or from an init function.
// This restriction is necessary because the instance that delays a function
// call may not be the one that executes it. Only the code executed at program
// initialization time is guaranteed to have been run by an instance before it
// receives a request.
func Func(key string, i interface{}) *Function {
f := &Function{fv: reflect.ValueOf(i)}
// Derive unique, somewhat stable key for this func.
_, file, _, _ := runtime.Caller(1)
f.key = file + ":" + key
t := f.fv.Type()
if t.Kind() != reflect.Func {
f.err = errors.New("not a function")
return f
}
if t.NumIn() == 0 || t.In(0) != contextType {
f.err = errFirstArg
return f
}
// Register the function's arguments with the gob package.
// This is required because they are marshaled inside a []interface{}.
// gob.Register only expects to be called during initialization;
// that's fine because this function expects the same.
for i := 0; i < t.NumIn(); i++ {
// Only concrete types may be registered. If the argument has
// interface type, the client is resposible for registering the
// concrete types it will hold.
if t.In(i).Kind() == reflect.Interface {
continue
}
gob.Register(reflect.Zero(t.In(i)).Interface())
}
funcs[f.key] = f
return f
}
type invocation struct {
Key string
Args []interface{}
}
// Call invokes a delayed function.
// f.Call(c, ...)
// is equivalent to
// t, _ := f.Task(...)
// taskqueue.Add(c, t, "")
func (f *Function) Call(c appengine.Context, args ...interface{}) {
t, err := f.Task(args...)
if err != nil {
c.Errorf("%v", err)
return
}
if _, err := taskqueueAdder(c, t, queue); err != nil {
c.Errorf("delay: taskqueue.Add failed: %v", err)
return
}
}
// Task creates a Task that will invoke the function.
// Its parameters may be tweaked before adding it to a queue.
// Users should not modify the Path or Payload fields of the returned Task.
func (f *Function) Task(args ...interface{}) (*taskqueue.Task, error) {
if f.err != nil {
return nil, fmt.Errorf("delay: func is invalid: %v", f.err)
}
nArgs := len(args) + 1 // +1 for the appengine.Context
ft := f.fv.Type()
minArgs := ft.NumIn()
if ft.IsVariadic() {
minArgs--
}
if nArgs < minArgs {
return nil, fmt.Errorf("delay: too few arguments to func: %d < %d", nArgs, minArgs)
}
if !ft.IsVariadic() && nArgs > minArgs {
return nil, fmt.Errorf("delay: too many arguments to func: %d > %d", nArgs, minArgs)
}
// Check arg types.
for i := 1; i < nArgs; i++ {
at := reflect.TypeOf(args[i-1])
var dt reflect.Type
if i < minArgs {
// not a variadic arg
dt = ft.In(i)
} else {
// a variadic arg
dt = ft.In(minArgs).Elem()
}
// nil arguments won't have a type, so they need special handling.
if at == nil {
// nil interface
switch dt.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
continue // may be nil
}
return nil, fmt.Errorf("delay: argument %d has wrong type: %v is not nilable", i, dt)
}
switch at.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
av := reflect.ValueOf(args[i-1])
if av.IsNil() {
// nil value in interface; not supported by gob, so we replace it
// with a nil interface value
args[i-1] = nil
}
}
if !at.AssignableTo(dt) {
return nil, fmt.Errorf("delay: argument %d has wrong type: %v is not assignable to %v", i, at, dt)
}
}
inv := invocation{
Key: f.key,
Args: args,
}
buf := new(bytes.Buffer)
if err := gob.NewEncoder(buf).Encode(inv); err != nil {
return nil, fmt.Errorf("delay: gob encoding failed: %v", err)
}
return &taskqueue.Task{
Path: path,
Payload: buf.Bytes(),
}, nil
}
var taskqueueAdder = taskqueue.Add // for testing
func init() {
http.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
runFunc(appengine.NewContext(req), w, req)
})
}
func runFunc(c appengine.Context, w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
var inv invocation
if err := gob.NewDecoder(req.Body).Decode(&inv); err != nil {
c.Errorf("delay: failed decoding task payload: %v", err)
c.Warningf("delay: dropping task")
return
}
f := funcs[inv.Key]
if f == nil {
c.Errorf("delay: no func with key %q found", inv.Key)
c.Warningf("delay: dropping task")
return
}
ft := f.fv.Type()
in := []reflect.Value{reflect.ValueOf(c)}
for _, arg := range inv.Args {
var v reflect.Value
if arg != nil {
v = reflect.ValueOf(arg)
} else {
// Task was passed a nil argument, so we must construct
// the zero value for the argument here.
n := len(in) // we're constructing the nth argument
var at reflect.Type
if !ft.IsVariadic() || n < ft.NumIn()-1 {
at = ft.In(n)
} else {
at = ft.In(ft.NumIn() - 1).Elem()
}
v = reflect.Zero(at)
}
in = append(in, v)
}
out := f.fv.Call(in)
if n := ft.NumOut(); n > 0 && ft.Out(n-1) == errorType {
if errv := out[n-1]; !errv.IsNil() {
c.Errorf("delay: func failed (will retry): %v", errv.Interface())
w.WriteHeader(http.StatusInternalServerError)
return
}
}
}

View File

@@ -1,307 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package delay
import (
"bytes"
"encoding/gob"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"google.golang.org/appengine"
"google.golang.org/appengine/taskqueue"
)
type CustomType struct {
N int
}
type CustomInterface interface {
N() int
}
type CustomImpl int
func (c CustomImpl) N() int { return int(c) }
// CustomImpl needs to be registered with gob.
func init() {
gob.Register(CustomImpl(0))
}
var (
invalidFunc = Func("invalid", func() {})
regFuncRuns = 0
regFuncMsg = ""
regFunc = Func("reg", func(c appengine.Context, arg string) {
regFuncRuns++
regFuncMsg = arg
})
custFuncTally = 0
custFunc = Func("cust", func(c appengine.Context, ct *CustomType, ci CustomInterface) {
a, b := 2, 3
if ct != nil {
a = ct.N
}
if ci != nil {
b = ci.N()
}
custFuncTally += a + b
})
anotherCustFunc = Func("cust2", func(c appengine.Context, n int, ct *CustomType, ci CustomInterface) {
})
varFuncMsg = ""
varFunc = Func("variadic", func(c appengine.Context, format string, args ...int) {
// convert []int to []interface{} for fmt.Sprintf.
as := make([]interface{}, len(args))
for i, a := range args {
as[i] = a
}
varFuncMsg = fmt.Sprintf(format, as...)
})
errFuncRuns = 0
errFuncErr = errors.New("error!")
errFunc = Func("err", func(c appengine.Context) error {
errFuncRuns++
if errFuncRuns == 1 {
return nil
}
return errFuncErr
})
)
type fakeContext struct {
appengine.Context
logging [][]interface{}
}
func (f *fakeContext) log(level, format string, args ...interface{}) {
f.logging = append(f.logging, append([]interface{}{level, format}, args...))
}
func (f *fakeContext) Infof(format string, args ...interface{}) { f.log("INFO", format, args...) }
func (f *fakeContext) Errorf(format string, args ...interface{}) { f.log("ERROR", format, args...) }
func TestInvalidFunction(t *testing.T) {
c := &fakeContext{}
invalidFunc.Call(c)
wantLogging := [][]interface{}{
{"ERROR", "%v", fmt.Errorf("delay: func is invalid: %s", errFirstArg)},
}
if !reflect.DeepEqual(c.logging, wantLogging) {
t.Errorf("Incorrect logging: got %+v, want %+v", c.logging, wantLogging)
}
}
func TestVariadicFunctionArguments(t *testing.T) {
// Check the argument type validation for variadic functions.
c := &fakeContext{}
calls := 0
taskqueueAdder = func(c appengine.Context, t *taskqueue.Task, _ string) (*taskqueue.Task, error) {
calls++
return t, nil
}
varFunc.Call(c, "hi")
varFunc.Call(c, "%d", 12)
varFunc.Call(c, "%d %d %d", 3, 1, 4)
if calls != 3 {
t.Errorf("Got %d calls to taskqueueAdder, want 3", calls)
}
varFunc.Call(c, "%d %s", 12, "a string is bad")
wantLogging := [][]interface{}{
{"ERROR", "%v", errors.New("delay: argument 3 has wrong type: string is not assignable to int")},
}
if !reflect.DeepEqual(c.logging, wantLogging) {
t.Errorf("Incorrect logging: got %+v, want %+v", c.logging, wantLogging)
}
}
func TestBadArguments(t *testing.T) {
// Try running regFunc with different sets of inappropriate arguments.
c := &fakeContext{}
regFunc.Call(c)
regFunc.Call(c, "lala", 53)
regFunc.Call(c, 53)
wantLogging := [][]interface{}{
{"ERROR", "%v", errors.New("delay: too few arguments to func: 1 < 2")},
{"ERROR", "%v", errors.New("delay: too many arguments to func: 3 > 2")},
{"ERROR", "%v", errors.New("delay: argument 1 has wrong type: int is not assignable to string")},
}
if !reflect.DeepEqual(c.logging, wantLogging) {
t.Errorf("Incorrect logging: got %+v, want %+v", c.logging, wantLogging)
}
}
func TestRunningFunction(t *testing.T) {
c := &fakeContext{}
// Fake out the adding of a task.
var task *taskqueue.Task
taskqueueAdder = func(_ appengine.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
if queue != "" {
t.Errorf(`Got queue %q, expected ""`, queue)
}
task = tk
return tk, nil
}
regFuncRuns, regFuncMsg = 0, "" // reset state
const msg = "Why, hello!"
regFunc.Call(c, msg)
// Simulate the Task Queue service.
req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
if err != nil {
t.Fatalf("Failed making http.Request: %v", err)
}
rw := httptest.NewRecorder()
runFunc(c, rw, req)
if regFuncRuns != 1 {
t.Errorf("regFuncRuns: got %d, want 1", regFuncRuns)
}
if regFuncMsg != msg {
t.Errorf("regFuncMsg: got %q, want %q", regFuncMsg, msg)
}
}
func TestCustomType(t *testing.T) {
c := &fakeContext{}
// Fake out the adding of a task.
var task *taskqueue.Task
taskqueueAdder = func(_ appengine.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
if queue != "" {
t.Errorf(`Got queue %q, expected ""`, queue)
}
task = tk
return tk, nil
}
custFuncTally = 0 // reset state
custFunc.Call(c, &CustomType{N: 11}, CustomImpl(13))
// Simulate the Task Queue service.
req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
if err != nil {
t.Fatalf("Failed making http.Request: %v", err)
}
rw := httptest.NewRecorder()
runFunc(c, rw, req)
if custFuncTally != 24 {
t.Errorf("custFuncTally = %d, want 24", custFuncTally)
}
// Try the same, but with nil values; one is a nil pointer (and thus a non-nil interface value),
// and the other is a nil interface value.
custFuncTally = 0 // reset state
custFunc.Call(c, (*CustomType)(nil), nil)
// Simulate the Task Queue service.
req, err = http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
if err != nil {
t.Fatalf("Failed making http.Request: %v", err)
}
rw = httptest.NewRecorder()
runFunc(c, rw, req)
if custFuncTally != 5 {
t.Errorf("custFuncTally = %d, want 5", custFuncTally)
}
}
func TestRunningVariadic(t *testing.T) {
c := &fakeContext{}
// Fake out the adding of a task.
var task *taskqueue.Task
taskqueueAdder = func(_ appengine.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
if queue != "" {
t.Errorf(`Got queue %q, expected ""`, queue)
}
task = tk
return tk, nil
}
varFuncMsg = "" // reset state
varFunc.Call(c, "Amiga %d has %d KB RAM", 500, 512)
// Simulate the Task Queue service.
req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
if err != nil {
t.Fatalf("Failed making http.Request: %v", err)
}
rw := httptest.NewRecorder()
runFunc(c, rw, req)
const expected = "Amiga 500 has 512 KB RAM"
if varFuncMsg != expected {
t.Errorf("varFuncMsg = %q, want %q", varFuncMsg, expected)
}
}
func TestErrorFunction(t *testing.T) {
c := &fakeContext{}
// Fake out the adding of a task.
var task *taskqueue.Task
taskqueueAdder = func(_ appengine.Context, tk *taskqueue.Task, queue string) (*taskqueue.Task, error) {
if queue != "" {
t.Errorf(`Got queue %q, expected ""`, queue)
}
task = tk
return tk, nil
}
errFunc.Call(c)
// Simulate the Task Queue service.
// The first call should succeed; the second call should fail.
{
req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
if err != nil {
t.Fatalf("Failed making http.Request: %v", err)
}
rw := httptest.NewRecorder()
runFunc(c, rw, req)
}
{
req, err := http.NewRequest("POST", path, bytes.NewBuffer(task.Payload))
if err != nil {
t.Fatalf("Failed making http.Request: %v", err)
}
rw := httptest.NewRecorder()
runFunc(c, rw, req)
if rw.Code != http.StatusInternalServerError {
t.Errorf("Got status code %d, want %d", rw.Code, http.StatusInternalServerError)
}
wantLogging := [][]interface{}{
{"ERROR", "delay: func failed (will retry): %v", errFuncErr},
}
if !reflect.DeepEqual(c.logging, wantLogging) {
t.Errorf("Incorrect logging: got %+v, want %+v", c.logging, wantLogging)
}
}
}

View File

@@ -1,19 +0,0 @@
# Demo application for Managed VMs.
application: vm-guestbook
version: 1
runtime: go
vm: true
api_version: go1
manual_scaling:
instances: 1
handlers:
# Favicon. Without this, the browser hits this once per page view.
- url: /favicon.ico
static_files: favicon.ico
upload: favicon.ico
# Main app. All the real work is here.
- url: /.*
script: _go_app

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,102 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package guestbook
import (
"html/template"
"net/http"
"time"
"google.golang.org/appengine"
"google.golang.org/appengine/datastore"
"google.golang.org/appengine/user"
)
var initTime time.Time
type Greeting struct {
Author string
Content string
Date time.Time
}
func init() {
http.HandleFunc("/", handleMainPage)
http.HandleFunc("/sign", handleSign)
}
// guestbookKey returns the key used for all guestbook entries.
func guestbookKey(c appengine.Context) *datastore.Key {
// The string "default_guestbook" here could be varied to have multiple guestbooks.
return datastore.NewKey(c, "Guestbook", "default_guestbook", 0, nil)
}
var tpl = template.Must(template.ParseGlob("templates/*.html"))
func handleMainPage(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "GET requests only", http.StatusMethodNotAllowed)
return
}
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
c := appengine.NewContext(r)
tic := time.Now()
q := datastore.NewQuery("Greeting").Ancestor(guestbookKey(c)).Order("-Date").Limit(10)
var gg []*Greeting
if _, err := q.GetAll(c, &gg); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
c.Errorf("GetAll: %v", err)
return
}
c.Infof("Datastore lookup took %s", time.Since(tic).String())
c.Infof("Rendering %d greetings", len(gg))
var email, logout, login string
if u := user.Current(c); u != nil {
logout, _ = user.LogoutURL(c, "/")
email = u.Email
} else {
login, _ = user.LoginURL(c, "/")
}
data := struct {
Greetings []*Greeting
Login, Logout, Email string
}{
Greetings: gg,
Login: login,
Logout: logout,
Email: email,
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := tpl.ExecuteTemplate(w, "guestbook.html", data); err != nil {
c.Errorf("%v", err)
}
}
func handleSign(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "POST requests only", http.StatusMethodNotAllowed)
return
}
c := appengine.NewContext(r)
g := &Greeting{
Content: r.FormValue("content"),
Date: time.Now(),
}
if u := user.Current(c); u != nil {
g.Author = u.String()
}
key := datastore.NewIncompleteKey(c, "Greeting", guestbookKey(c))
if _, err := datastore.Put(c, key, g); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Redirect with 303 which causes the subsequent request to use GET.
http.Redirect(w, r, "/", http.StatusSeeOther)
}

View File

@@ -1,7 +0,0 @@
indexes:
- kind: Greeting
ancestor: yes
properties:
- name: Date
direction: desc

View File

@@ -1,26 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Guestbook Demo</title>
</head>
<body>
<p>
{{with .Email}}You are currently logged in as {{.}}.{{end}}
{{with .Login}}<a href="{{.}}">Sign in</a>{{end}}
{{with .Logout}}<a href="{{.}}">Sign out</a>{{end}}
</p>
{{range .Greetings }}
<p>
{{with .Author}}<b>{{.}}</b>{{else}}An anonymous person{{end}}
on <em>{{.Date.Format "3:04pm, Mon 2 Jan"}}</em>
wrote <blockquote>{{.Content}}</blockquote>
</p>
{{end}}
<form action="/sign" method="post">
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Sign Guestbook"></div>
</form>
</body>
</html>

View File

@@ -1,15 +0,0 @@
application: helloworld
version: 1
runtime: go
api_version: go1
vm: true
manual_scaling:
instances: 1
handlers:
- url: /favicon.ico
static_files: favicon.ico
upload: favicon.ico
- url: /.*
script: _go_app

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,45 +0,0 @@
// Copyright 2014 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package helloworld
import (
"html/template"
"net/http"
"time"
"google.golang.org/appengine"
)
var initTime = time.Now()
func init() {
http.HandleFunc("/", handle)
}
func handle(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
c := appengine.NewContext(r)
c.Infof("Serving the front page.")
tmpl.Execute(w, time.Since(initTime))
}
var tmpl = template.Must(template.New("front").Parse(`
<html><body>
<p>
Hello, World! 세상아 안녕!
</p>
<p>
This instance has been running for <em>{{.}}</em>.
</p>
</body></html>
`))

View File

@@ -1,46 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// This file provides error functions for common API failure modes.
package appengine
import (
"fmt"
"google.golang.org/appengine/internal"
)
// IsOverQuota reports whether err represents an API call failure
// due to insufficient available quota.
func IsOverQuota(err error) bool {
callErr, ok := err.(*internal.CallError)
return ok && callErr.Code == 4
}
// MultiError is returned by batch operations when there are errors with
// particular elements. Errors will be in a one-to-one correspondence with
// the input elements; successful elements will have a nil entry.
type MultiError []error
func (m MultiError) Error() string {
s, n := "", 0
for _, e := range m {
if e != nil {
if n == 0 {
s = e.Error()
}
n++
}
}
switch n {
case 0:
return "(0 errors)"
case 1:
return s
case 2:
return s + " (and 1 other error)"
}
return fmt.Sprintf("%s (and %d other errors)", s, n-1)
}

View File

@@ -1,26 +0,0 @@
// Copyright 2014 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// Package file provides helper functions for using Google Cloud Storage.
package file
import (
"fmt"
"google.golang.org/appengine"
aipb "google.golang.org/appengine/internal/app_identity"
)
// DefaultBucketName returns the name of this application's
// default Google Cloud Storage bucket.
func DefaultBucketName(c appengine.Context) (string, error) {
req := &aipb.GetDefaultGcsBucketNameRequest{}
res := &aipb.GetDefaultGcsBucketNameResponse{}
err := c.Call("app_identity_service", "GetDefaultGcsBucketName", req, res, nil)
if err != nil {
return "", fmt.Errorf("file: no default bucket name returned in RPC response: %v", res)
}
return res.GetDefaultGcsBucketName(), nil
}

View File

@@ -1,141 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package appengine
import (
"time"
"google.golang.org/appengine/internal"
pb "google.golang.org/appengine/internal/app_identity"
modpb "google.golang.org/appengine/internal/modules"
)
// AppID returns the application ID for the current application.
// The string will be a plain application ID (e.g. "appid"), with a
// domain prefix for custom domain deployments (e.g. "example.com:appid").
func AppID(c Context) string { return internal.AppID(c.FullyQualifiedAppID()) }
// DefaultVersionHostname returns the standard hostname of the default version
// of the current application (e.g. "my-app.appspot.com"). This is suitable for
// use in constructing URLs.
func DefaultVersionHostname(c Context) string {
return internal.DefaultVersionHostname(c.Request())
}
// ModuleName returns the module name of the current instance.
func ModuleName(c Context) string {
return internal.ModuleName()
}
// ModuleHostname returns a hostname of a module instance.
// If module is the empty string, it refers to the module of the current instance.
// If version is empty, it refers to the version of the current instance if valid,
// or the default version of the module of the current instance.
// If instance is empty, ModuleHostname returns the load-balancing hostname.
func ModuleHostname(c Context, module, version, instance string) (string, error) {
req := &modpb.GetHostnameRequest{}
if module != "" {
req.Module = &module
}
if version != "" {
req.Version = &version
}
if instance != "" {
req.Instance = &instance
}
res := &modpb.GetHostnameResponse{}
if err := c.Call("modules", "GetHostname", req, res, nil); err != nil {
return "", err
}
return *res.Hostname, nil
}
// VersionID returns the version ID for the current application.
// It will be of the form "X.Y", where X is specified in app.yaml,
// and Y is a number generated when each version of the app is uploaded.
// It does not include a module name.
func VersionID(c Context) string { return internal.VersionID() }
// InstanceID returns a mostly-unique identifier for this instance.
func InstanceID() string { return internal.InstanceID() }
// Datacenter returns an identifier for the datacenter that the instance is running in.
func Datacenter(c Context) string { return internal.Datacenter(c.Request()) }
// ServerSoftware returns the App Engine release version.
// In production, it looks like "Google App Engine/X.Y.Z".
// In the development appserver, it looks like "Development/X.Y".
func ServerSoftware() string { return internal.ServerSoftware() }
// RequestID returns a string that uniquely identifies the request.
func RequestID(c Context) string { return internal.RequestID(c.Request()) }
// AccessToken generates an OAuth2 access token for the specified scopes on
// behalf of service account of this application. This token will expire after
// the returned time.
func AccessToken(c Context, scopes ...string) (token string, expiry time.Time, err error) {
req := &pb.GetAccessTokenRequest{Scope: scopes}
res := &pb.GetAccessTokenResponse{}
err = c.Call("app_identity_service", "GetAccessToken", req, res, nil)
if err != nil {
return "", time.Time{}, err
}
return res.GetAccessToken(), time.Unix(res.GetExpirationTime(), 0), nil
}
// Certificate represents a public certificate for the app.
type Certificate struct {
KeyName string
Data []byte // PEM-encoded X.509 certificate
}
// PublicCertificates retrieves the public certificates for the app.
// They can be used to verify a signature returned by SignBytes.
func PublicCertificates(c Context) ([]Certificate, error) {
req := &pb.GetPublicCertificateForAppRequest{}
res := &pb.GetPublicCertificateForAppResponse{}
if err := c.Call("app_identity_service", "GetPublicCertificatesForApp", req, res, nil); err != nil {
return nil, err
}
var cs []Certificate
for _, pc := range res.PublicCertificateList {
cs = append(cs, Certificate{
KeyName: pc.GetKeyName(),
Data: []byte(pc.GetX509CertificatePem()),
})
}
return cs, nil
}
// ServiceAccount returns a string representing the service account name, in
// the form of an email address (typically app_id@appspot.gserviceaccount.com).
func ServiceAccount(c Context) (string, error) {
req := &pb.GetServiceAccountNameRequest{}
res := &pb.GetServiceAccountNameResponse{}
err := c.Call("app_identity_service", "GetServiceAccountName", req, res, nil)
if err != nil {
return "", err
}
return res.GetServiceAccountName(), err
}
// SignBytes signs bytes using a private key unique to your application.
func SignBytes(c Context, bytes []byte) (string, []byte, error) {
req := &pb.SignForAppRequest{BytesToSign: bytes}
res := &pb.SignForAppResponse{}
err := c.Call("app_identity_service", "SignForApp", req, res, nil)
if err != nil {
return "", nil, err
}
return res.GetKeyName(), res.GetSignatureBytes(), err
}
func init() {
internal.RegisterErrorCodeMap("app_identity_service", pb.AppIdentityServiceError_ErrorCode_name)
internal.RegisterErrorCodeMap("modules", modpb.ModulesServiceError_ErrorCode_name)
}

View File

@@ -1,65 +0,0 @@
// Copyright 2012 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// Package image provides image services.
package image
import (
"fmt"
"net/url"
"google.golang.org/appengine"
"google.golang.org/appengine/internal"
pb "google.golang.org/appengine/internal/image"
)
type ServingURLOptions struct {
Secure bool // whether the URL should use HTTPS
// Size must be between zero and 1600.
// If Size is non-zero, a resized version of the image is served,
// and Size is the served image's longest dimension. The aspect ratio is preserved.
// If Crop is true the image is cropped from the center instead of being resized.
Size int
Crop bool
}
// ServingURL returns a URL that will serve an image from Blobstore.
func ServingURL(c appengine.Context, key appengine.BlobKey, opts *ServingURLOptions) (*url.URL, error) {
req := &pb.ImagesGetUrlBaseRequest{
BlobKey: (*string)(&key),
}
if opts != nil && opts.Secure {
req.CreateSecureUrl = &opts.Secure
}
res := &pb.ImagesGetUrlBaseResponse{}
if err := c.Call("images", "GetUrlBase", req, res, nil); err != nil {
return nil, err
}
// The URL may have suffixes added to dynamically resize or crop:
// - adding "=s32" will serve the image resized to 32 pixels, preserving the aspect ratio.
// - adding "=s32-c" is the same as "=s32" except it will be cropped.
u := *res.Url
if opts != nil && opts.Size > 0 {
u += fmt.Sprintf("=s%d", opts.Size)
if opts.Crop {
u += "-c"
}
}
return url.Parse(u)
}
// DeleteServingURL deletes the serving URL for an image.
func DeleteServingURL(c appengine.Context, key appengine.BlobKey) error {
req := &pb.ImagesDeleteUrlBaseRequest{
BlobKey: (*string)(&key),
}
res := &pb.ImagesDeleteUrlBaseResponse{}
return c.Call("images", "DeleteUrlBase", req, res, nil)
}
func init() {
internal.RegisterErrorCodeMap("images", pb.ImagesServiceError_ErrorCode_name)
}

View File

@@ -1,88 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// Package aetesting provides utilities for testing App Engine packages.
// This is not for testing user applications.
package aetesting
import (
"fmt"
"reflect"
"testing"
"github.com/golang/protobuf/proto"
"google.golang.org/appengine"
"google.golang.org/appengine/internal"
)
// FakeSingleContext returns a context whose Call invocations will be serviced
// by f, which should be a function that has two arguments of the input and output
// protocol buffer type, and one error return.
func FakeSingleContext(t *testing.T, service, method string, f interface{}) appengine.Context {
fv := reflect.ValueOf(f)
if fv.Kind() != reflect.Func {
t.Fatal("not a function")
}
ft := fv.Type()
if ft.NumIn() != 2 || ft.NumOut() != 1 {
t.Fatalf("f has %d in and %d out, want 2 in and 1 out", ft.NumIn(), ft.NumOut())
}
for i := 0; i < 2; i++ {
at := ft.In(i)
if !at.Implements(protoMessageType) {
t.Fatalf("arg %d does not implement proto.Message", i)
}
}
if ft.Out(0) != errorType {
t.Fatalf("f's return is %v, want error", ft.Out(0))
}
return &single{
t: t,
service: service,
method: method,
f: fv,
}
}
var (
protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem()
errorType = reflect.TypeOf((*error)(nil)).Elem()
)
type single struct {
t *testing.T
service, method string
f reflect.Value
}
func (s *single) logf(level, format string, args ...interface{}) {
s.t.Logf(level+": "+format, args...)
}
func (s *single) Debugf(format string, args ...interface{}) { s.logf("DEBUG", format, args...) }
func (s *single) Infof(format string, args ...interface{}) { s.logf("INFO", format, args...) }
func (s *single) Warningf(format string, args ...interface{}) { s.logf("WARNING", format, args...) }
func (s *single) Errorf(format string, args ...interface{}) { s.logf("ERROR", format, args...) }
func (s *single) Criticalf(format string, args ...interface{}) { s.logf("CRITICAL", format, args...) }
func (*single) FullyQualifiedAppID() string { return "dev~fake-app" }
func (*single) Request() interface{} { return nil }
func (s *single) Call(service, method string, in, out proto.Message, opts *internal.CallOptions) error {
if service == "__go__" {
return fmt.Errorf("Unknown API call /%s.%s", service, method)
}
if service != s.service || method != s.method {
s.t.Fatalf("Unexpected call to /%s.%s", service, method)
}
ins := []reflect.Value{
reflect.ValueOf(in),
reflect.ValueOf(out),
}
outs := s.f.Call(ins)
if outs[0].IsNil() {
return nil
}
return outs[0].Interface().(error)
}

View File

@@ -1,589 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package internal
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"net/url"
"os"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/golang/protobuf/proto"
basepb "google.golang.org/appengine/internal/base"
logpb "google.golang.org/appengine/internal/log"
remotepb "google.golang.org/appengine/internal/remote_api"
)
const (
apiPath = "/rpc_http"
)
var (
// Incoming headers.
ticketHeader = http.CanonicalHeaderKey("X-AppEngine-API-Ticket")
dapperHeader = http.CanonicalHeaderKey("X-Google-DapperTraceInfo")
defNamespaceHeader = http.CanonicalHeaderKey("X-AppEngine-Default-Namespace")
curNamespaceHeader = http.CanonicalHeaderKey("X-AppEngine-Current-Namespace")
userIPHeader = http.CanonicalHeaderKey("X-AppEngine-User-IP")
remoteAddrHeader = http.CanonicalHeaderKey("X-AppEngine-Remote-Addr")
// Outgoing headers.
apiEndpointHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Endpoint")
apiEndpointHeaderValue = []string{"app-engine-apis"}
apiMethodHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Method")
apiMethodHeaderValue = []string{"/VMRemoteAPI.CallRemoteAPI"}
apiDeadlineHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Deadline")
apiContentType = http.CanonicalHeaderKey("Content-Type")
apiContentTypeValue = []string{"application/octet-stream"}
logFlushHeader = http.CanonicalHeaderKey("X-AppEngine-Log-Flush-Count")
apiHTTPClient = &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: limitDial,
},
}
)
func apiHost() string {
host, port := "appengine.googleapis.com", "10001"
if h := os.Getenv("API_HOST"); h != "" {
host = h
}
if p := os.Getenv("API_PORT"); p != "" {
port = p
}
return host + ":" + port
}
func handleHTTP(w http.ResponseWriter, r *http.Request) {
c := &context{
req: r,
outHeader: w.Header(),
}
stopFlushing := make(chan int)
ctxs.Lock()
ctxs.m[r] = c
ctxs.Unlock()
defer func() {
ctxs.Lock()
delete(ctxs.m, r)
ctxs.Unlock()
}()
// Patch up RemoteAddr so it looks reasonable.
if addr := r.Header.Get(userIPHeader); addr != "" {
r.RemoteAddr = addr
} else if addr = r.Header.Get(remoteAddrHeader); addr != "" {
r.RemoteAddr = addr
} else {
// Should not normally reach here, but pick a sensible default anyway.
r.RemoteAddr = "127.0.0.1"
}
// The address in the headers will most likely be of these forms:
// 123.123.123.123
// 2001:db8::1
// net/http.Request.RemoteAddr is specified to be in "IP:port" form.
if _, _, err := net.SplitHostPort(r.RemoteAddr); err != nil {
// Assume the remote address is only a host; add a default port.
r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80")
}
// Start goroutine responsible for flushing app logs.
// This is done after adding c to ctx.m (and stopped before removing it)
// because flushing logs requires making an API call.
go c.logFlusher(stopFlushing)
executeRequestSafely(c, r)
c.outHeader = nil // make sure header changes aren't respected any more
stopFlushing <- 1 // any logging beyond this point will be dropped
// Flush any pending logs asynchronously.
c.pendingLogs.Lock()
flushes := c.pendingLogs.flushes
if len(c.pendingLogs.lines) > 0 {
flushes++
}
c.pendingLogs.Unlock()
go c.flushLog(false)
w.Header().Set(logFlushHeader, strconv.Itoa(flushes))
// Avoid nil Write call if c.Write is never called.
if c.outCode != 0 {
w.WriteHeader(c.outCode)
}
if c.outBody != nil {
w.Write(c.outBody)
}
}
func executeRequestSafely(c *context, r *http.Request) {
defer func() {
if x := recover(); x != nil {
c.logf(4, "%s", renderPanic(x)) // 4 == critical
}
}()
http.DefaultServeMux.ServeHTTP(c, r)
}
func renderPanic(x interface{}) string {
buf := make([]byte, 16<<10) // 16 KB should be plenty
buf = buf[:runtime.Stack(buf, false)]
// Remove the first few stack frames:
// this func
// the recover closure in the caller
// That will root the stack trace at the site of the panic.
const (
skipStart = "internal.renderPanic"
skipFrames = 2
)
start := bytes.Index(buf, []byte(skipStart))
p := start
for i := 0; i < skipFrames*2 && p+1 < len(buf); i++ {
p = bytes.IndexByte(buf[p+1:], '\n') + p + 1
if p < 0 {
break
}
}
if p >= 0 {
// buf[start:p+1] is the block to remove.
// Copy buf[p+1:] over buf[start:] and shrink buf.
copy(buf[start:], buf[p+1:])
buf = buf[:len(buf)-(p+1-start)]
}
// Add panic heading.
head := fmt.Sprintf("panic: %v\n\n", x)
if len(head) > len(buf) {
// Extremely unlikely to happen.
return head
}
copy(buf[len(head):], buf)
copy(buf, head)
return string(buf)
}
var ctxs = struct {
sync.Mutex
m map[*http.Request]*context
bg *context // background context, lazily initialized
}{
m: make(map[*http.Request]*context),
}
// context represents the context of an in-flight HTTP request.
// It implements the appengine.Context and http.ResponseWriter interfaces.
type context struct {
req *http.Request
outCode int
outHeader http.Header
outBody []byte
pendingLogs struct {
sync.Mutex
lines []*logpb.UserAppLogLine
flushes int
}
}
func NewContext(req *http.Request) *context {
ctxs.Lock()
c := ctxs.m[req]
ctxs.Unlock()
if c == nil {
// Someone passed in an http.Request that is not in-flight.
// We panic here rather than panicking at a later point
// so that stack traces will be more sensible.
log.Panic("appengine: NewContext passed an unknown http.Request")
}
return c
}
func BackgroundContext() *context {
ctxs.Lock()
defer ctxs.Unlock()
if ctxs.bg != nil {
return ctxs.bg
}
// Compute background security ticket.
appID := partitionlessAppID()
escAppID := strings.Replace(strings.Replace(appID, ":", "_", -1), ".", "_", -1)
majVersion := VersionID()
if i := strings.Index(majVersion, "_"); i >= 0 {
majVersion = majVersion[:i]
}
ticket := fmt.Sprintf("%s/%s.%s.%s", escAppID, ModuleName(), majVersion, InstanceID())
ctxs.bg = &context{
req: &http.Request{
Header: http.Header{
ticketHeader: []string{ticket},
},
},
}
// TODO(dsymonds): Wire up the shutdown handler to do a final flush.
go ctxs.bg.logFlusher(make(chan int))
return ctxs.bg
}
var errTimeout = &CallError{
Detail: "Deadline exceeded",
Code: int32(remotepb.RpcError_CANCELLED),
Timeout: true,
}
func (c *context) Header() http.Header { return c.outHeader }
// Copied from $GOROOT/src/pkg/net/http/transfer.go. Some response status
// codes do not permit a response body (nor response entity headers such as
// Content-Length, Content-Type, etc).
func bodyAllowedForStatus(status int) bool {
switch {
case status >= 100 && status <= 199:
return false
case status == 204:
return false
case status == 304:
return false
}
return true
}
func (c *context) Write(b []byte) (int, error) {
if c.outCode == 0 {
c.WriteHeader(http.StatusOK)
}
if len(b) > 0 && !bodyAllowedForStatus(c.outCode) {
return 0, http.ErrBodyNotAllowed
}
c.outBody = append(c.outBody, b...)
return len(b), nil
}
func (c *context) WriteHeader(code int) {
if c.outCode != 0 {
c.Errorf("WriteHeader called multiple times on request.")
return
}
c.outCode = code
}
func (c *context) post(body []byte, timeout time.Duration) (b []byte, err error) {
dst := apiHost()
hreq := &http.Request{
Method: "POST",
URL: &url.URL{
Scheme: "http",
Host: dst,
Path: apiPath,
},
Header: http.Header{
apiEndpointHeader: apiEndpointHeaderValue,
apiMethodHeader: apiMethodHeaderValue,
apiContentType: apiContentTypeValue,
apiDeadlineHeader: []string{strconv.FormatFloat(timeout.Seconds(), 'f', -1, 64)},
},
Body: ioutil.NopCloser(bytes.NewReader(body)),
ContentLength: int64(len(body)),
Host: dst,
}
if info := c.req.Header.Get(dapperHeader); info != "" {
hreq.Header.Set(dapperHeader, info)
}
tr := apiHTTPClient.Transport.(*http.Transport)
var timedOut int32 // atomic; set to 1 if timed out
t := time.AfterFunc(timeout, func() {
atomic.StoreInt32(&timedOut, 1)
tr.CancelRequest(hreq)
})
defer t.Stop()
defer func() {
// Check if timeout was exceeded.
if atomic.LoadInt32(&timedOut) != 0 {
err = errTimeout
}
}()
hresp, err := apiHTTPClient.Do(hreq)
if err != nil {
return nil, &CallError{
Detail: fmt.Sprintf("service bridge HTTP failed: %v", err),
Code: int32(remotepb.RpcError_UNKNOWN),
}
}
defer hresp.Body.Close()
hrespBody, err := ioutil.ReadAll(hresp.Body)
if hresp.StatusCode != 200 {
return nil, &CallError{
Detail: fmt.Sprintf("service bridge returned HTTP %d (%q)", hresp.StatusCode, hrespBody),
Code: int32(remotepb.RpcError_UNKNOWN),
}
}
if err != nil {
return nil, &CallError{
Detail: fmt.Sprintf("service bridge response bad: %v", err),
Code: int32(remotepb.RpcError_UNKNOWN),
}
}
return hrespBody, nil
}
var virtualMethodHeaders = map[string]string{
"GetNamespace": curNamespaceHeader,
"GetDefaultNamespace": defNamespaceHeader,
"user:Email": http.CanonicalHeaderKey("X-AppEngine-User-Email"),
"user:AuthDomain": http.CanonicalHeaderKey("X-AppEngine-Auth-Domain"),
"user:ID": http.CanonicalHeaderKey("X-AppEngine-User-Id"),
"user:IsAdmin": http.CanonicalHeaderKey("X-AppEngine-User-Is-Admin"),
"user:FederatedIdentity": http.CanonicalHeaderKey("X-AppEngine-Federated-Identity"),
"user:FederatedProvider": http.CanonicalHeaderKey("X-AppEngine-Federated-Provider"),
}
func (c *context) Call(service, method string, in, out proto.Message, opts *CallOptions) error {
if service == "__go__" {
if hdr, ok := virtualMethodHeaders[method]; ok {
out.(*basepb.StringProto).Value = proto.String(c.req.Header.Get(hdr))
return nil
}
}
// Default RPC timeout is 5s.
timeout := 5 * time.Second
if opts != nil && opts.Timeout > 0 {
timeout = opts.Timeout
}
data, err := proto.Marshal(in)
if err != nil {
return err
}
ticket := c.req.Header.Get(ticketHeader)
req := &remotepb.Request{
ServiceName: &service,
Method: &method,
Request: data,
RequestId: &ticket,
}
hreqBody, err := proto.Marshal(req)
if err != nil {
return err
}
hrespBody, err := c.post(hreqBody, timeout)
if err != nil {
return err
}
res := &remotepb.Response{}
if err := proto.Unmarshal(hrespBody, res); err != nil {
return err
}
if res.RpcError != nil {
ce := &CallError{
Detail: res.RpcError.GetDetail(),
Code: *res.RpcError.Code,
}
switch remotepb.RpcError_ErrorCode(ce.Code) {
case remotepb.RpcError_CANCELLED, remotepb.RpcError_DEADLINE_EXCEEDED:
ce.Timeout = true
}
return ce
}
if res.ApplicationError != nil {
return &APIError{
Service: *req.ServiceName,
Detail: res.ApplicationError.GetDetail(),
Code: *res.ApplicationError.Code,
}
}
if res.Exception != nil || res.JavaException != nil {
// This shouldn't happen, but let's be defensive.
return &CallError{
Detail: "service bridge returned exception",
Code: int32(remotepb.RpcError_UNKNOWN),
}
}
return proto.Unmarshal(res.Response, out)
}
func (c *context) Request() interface{} {
return c.req
}
func (c *context) addLogLine(ll *logpb.UserAppLogLine) {
// Truncate long log lines.
// TODO(dsymonds): Check if this is still necessary.
const lim = 8 << 10
if len(*ll.Message) > lim {
suffix := fmt.Sprintf("...(length %d)", len(*ll.Message))
ll.Message = proto.String((*ll.Message)[:lim-len(suffix)] + suffix)
}
c.pendingLogs.Lock()
c.pendingLogs.lines = append(c.pendingLogs.lines, ll)
c.pendingLogs.Unlock()
}
var logLevelName = map[int64]string{
0: "DEBUG",
1: "INFO",
2: "WARNING",
3: "ERROR",
4: "CRITICAL",
}
func (c *context) logf(level int64, format string, args ...interface{}) {
s := fmt.Sprintf(format, args...)
s = strings.TrimRight(s, "\n") // Remove any trailing newline characters.
c.addLogLine(&logpb.UserAppLogLine{
TimestampUsec: proto.Int64(time.Now().UnixNano() / 1e3),
Level: &level,
Message: &s,
})
log.Print(logLevelName[level] + ": " + s)
}
func (c *context) Debugf(format string, args ...interface{}) { c.logf(0, format, args...) }
func (c *context) Infof(format string, args ...interface{}) { c.logf(1, format, args...) }
func (c *context) Warningf(format string, args ...interface{}) { c.logf(2, format, args...) }
func (c *context) Errorf(format string, args ...interface{}) { c.logf(3, format, args...) }
func (c *context) Criticalf(format string, args ...interface{}) { c.logf(4, format, args...) }
// FullyQualifiedAppID returns the fully-qualified application ID.
// This may contain a partition prefix (e.g. "s~" for High Replication apps),
// or a domain prefix (e.g. "example.com:").
func (c *context) FullyQualifiedAppID() string { return fullyQualifiedAppID() }
// flushLog attempts to flush any pending logs to the appserver.
// It should not be called concurrently.
func (c *context) flushLog(force bool) (flushed bool) {
c.pendingLogs.Lock()
// Grab up to 30 MB. We can get away with up to 32 MB, but let's be cautious.
n, rem := 0, 30<<20
for ; n < len(c.pendingLogs.lines); n++ {
ll := c.pendingLogs.lines[n]
// Each log line will require about 3 bytes of overhead.
nb := proto.Size(ll) + 3
if nb > rem {
break
}
rem -= nb
}
lines := c.pendingLogs.lines[:n]
c.pendingLogs.lines = c.pendingLogs.lines[n:]
c.pendingLogs.Unlock()
if len(lines) == 0 && !force {
// Nothing to flush.
return false
}
rescueLogs := false
defer func() {
if rescueLogs {
c.pendingLogs.Lock()
c.pendingLogs.lines = append(lines, c.pendingLogs.lines...)
c.pendingLogs.Unlock()
}
}()
buf, err := proto.Marshal(&logpb.UserAppLogGroup{
LogLine: lines,
})
if err != nil {
log.Printf("internal.flushLog: marshaling UserAppLogGroup: %v", err)
rescueLogs = true
return false
}
req := &logpb.FlushRequest{
Logs: buf,
}
res := &basepb.VoidProto{}
c.pendingLogs.Lock()
c.pendingLogs.flushes++
c.pendingLogs.Unlock()
if err := c.Call("logservice", "Flush", req, res, nil); err != nil {
log.Printf("internal.flushLog: Flush RPC: %v", err)
rescueLogs = true
return false
}
return true
}
const (
// Log flushing parameters.
flushInterval = 1 * time.Second
forceFlushInterval = 60 * time.Second
)
func (c *context) logFlusher(stop <-chan int) {
lastFlush := time.Now()
tick := time.NewTicker(flushInterval)
for {
select {
case <-stop:
// Request finished.
tick.Stop()
return
case <-tick.C:
force := time.Now().Sub(lastFlush) > forceFlushInterval
if c.flushLog(force) {
lastFlush = time.Now()
}
}
}
}
func ContextForTesting(req *http.Request) *context {
return &context{req: req}
}
// caller is a subset of appengine.Context.
type caller interface {
Call(service, method string, in, out proto.Message, opts *CallOptions) error
}
var virtualOpts = &CallOptions{
// Virtual API calls should happen nearly instantaneously.
Timeout: 1 * time.Millisecond,
}
// VirtAPI invokes a virtual API call for the __go__ service.
// It is for methods that accept a VoidProto and return a StringProto.
// It returns an empty string if the call fails.
func VirtAPI(c caller, method string) string {
s := &basepb.StringProto{}
if err := c.Call("__go__", method, &basepb.VoidProto{}, s, virtualOpts); err != nil {
log.Printf("/__go__.%s failed: %v", method, err)
}
return s.GetValue()
}

View File

@@ -1,5 +0,0 @@
// +build race
package internal
func init() { raceDetector = true }

View File

@@ -1,412 +0,0 @@
// Copyright 2014 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package internal
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"os"
"os/exec"
"strings"
"sync/atomic"
"testing"
"time"
"github.com/golang/protobuf/proto"
basepb "google.golang.org/appengine/internal/base"
remotepb "google.golang.org/appengine/internal/remote_api"
)
const testTicketHeader = "X-Magic-Ticket-Header"
func init() {
ticketHeader = testTicketHeader
}
type fakeAPIHandler struct {
hang chan int // used for RunSlowly RPC
LogFlushes int32 // atomic
}
func (f *fakeAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
writeResponse := func(res *remotepb.Response) {
hresBody, err := proto.Marshal(res)
if err != nil {
http.Error(w, fmt.Sprintf("Failed encoding API response: %v", err), 500)
return
}
w.Write(hresBody)
}
if r.URL.Path != "/rpc_http" {
http.NotFound(w, r)
return
}
hreqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, fmt.Sprintf("Bad body: %v", err), 500)
return
}
apiReq := &remotepb.Request{}
if err := proto.Unmarshal(hreqBody, apiReq); err != nil {
http.Error(w, fmt.Sprintf("Bad encoded API request: %v", err), 500)
return
}
if *apiReq.RequestId != "s3cr3t" {
writeResponse(&remotepb.Response{
RpcError: &remotepb.RpcError{
Code: proto.Int32(int32(remotepb.RpcError_SECURITY_VIOLATION)),
Detail: proto.String("bad security ticket"),
},
})
return
}
if got, want := r.Header.Get(dapperHeader), "trace-001"; got != want {
writeResponse(&remotepb.Response{
RpcError: &remotepb.RpcError{
Code: proto.Int32(int32(remotepb.RpcError_BAD_REQUEST)),
Detail: proto.String(fmt.Sprintf("trace info = %q, want %q", got, want)),
},
})
return
}
service, method := *apiReq.ServiceName, *apiReq.Method
var resOut proto.Message
if service == "actordb" && method == "LookupActor" {
req := &basepb.StringProto{}
res := &basepb.StringProto{}
if err := proto.Unmarshal(apiReq.Request, req); err != nil {
http.Error(w, fmt.Sprintf("Bad encoded request: %v", err), 500)
return
}
if *req.Value == "Doctor Who" {
res.Value = proto.String("David Tennant")
}
resOut = res
}
if service == "errors" {
switch method {
case "Non200":
http.Error(w, "I'm a little teapot.", 418)
return
case "ShortResponse":
w.Header().Set("Content-Length", "100")
w.Write([]byte("way too short"))
return
case "OverQuota":
writeResponse(&remotepb.Response{
RpcError: &remotepb.RpcError{
Code: proto.Int32(int32(remotepb.RpcError_OVER_QUOTA)),
Detail: proto.String("you are hogging the resources!"),
},
})
return
case "RunSlowly":
// TestAPICallRPCFailure creates f.hang, but does not strobe it
// until c.Call returns with remotepb.RpcError_CANCELLED.
// This is here to force a happens-before relationship between
// the httptest server handler and shutdown.
<-f.hang
resOut = &basepb.VoidProto{}
}
}
if service == "logservice" && method == "Flush" {
// Pretend log flushing is slow.
time.Sleep(50 * time.Millisecond)
atomic.AddInt32(&f.LogFlushes, 1)
resOut = &basepb.VoidProto{}
}
encOut, err := proto.Marshal(resOut)
if err != nil {
http.Error(w, fmt.Sprintf("Failed encoding response: %v", err), 500)
return
}
writeResponse(&remotepb.Response{
Response: encOut,
})
}
func setup() (f *fakeAPIHandler, c *context, cleanup func()) {
f = &fakeAPIHandler{}
srv := httptest.NewServer(f)
parts := strings.SplitN(strings.TrimPrefix(srv.URL, "http://"), ":", 2)
os.Setenv("API_HOST", parts[0])
os.Setenv("API_PORT", parts[1])
return f, &context{
req: &http.Request{
Header: http.Header{
ticketHeader: []string{"s3cr3t"},
dapperHeader: []string{"trace-001"},
},
},
}, func() {
srv.Close()
os.Setenv("API_HOST", "")
os.Setenv("API_PORT", "")
}
}
func TestAPICall(t *testing.T) {
_, c, cleanup := setup()
defer cleanup()
req := &basepb.StringProto{
Value: proto.String("Doctor Who"),
}
res := &basepb.StringProto{}
err := c.Call("actordb", "LookupActor", req, res, nil)
if err != nil {
t.Fatalf("API call failed: %v", err)
}
if got, want := *res.Value, "David Tennant"; got != want {
t.Errorf("Response is %q, want %q", got, want)
}
}
func TestAPICallRPCFailure(t *testing.T) {
f, c, cleanup := setup()
defer cleanup()
testCases := []struct {
method string
code remotepb.RpcError_ErrorCode
}{
{"Non200", remotepb.RpcError_UNKNOWN},
{"ShortResponse", remotepb.RpcError_UNKNOWN},
{"OverQuota", remotepb.RpcError_OVER_QUOTA},
{"RunSlowly", remotepb.RpcError_CANCELLED},
}
f.hang = make(chan int) // only for RunSlowly
for _, tc := range testCases {
opts := &CallOptions{
Timeout: 100 * time.Millisecond,
}
err := c.Call("errors", tc.method, &basepb.VoidProto{}, &basepb.VoidProto{}, opts)
ce, ok := err.(*CallError)
if !ok {
t.Errorf("%s: API call error is %T (%v), want *CallError", tc.method, err, err)
continue
}
if ce.Code != int32(tc.code) {
t.Errorf("%s: ce.Code = %d, want %d", tc.method, ce.Code, tc.code)
}
if tc.method == "RunSlowly" {
f.hang <- 1 // release the HTTP handler
}
}
}
func TestAPICallDialFailure(t *testing.T) {
// See what happens if the API host is unresponsive.
// This should time out quickly, not hang forever.
_, c, cleanup := setup()
defer cleanup()
os.Setenv("API_HOST", "")
os.Setenv("API_PORT", "")
start := time.Now()
err := c.Call("foo", "bar", &basepb.VoidProto{}, &basepb.VoidProto{}, nil)
const max = 1 * time.Second
if taken := time.Since(start); taken > max {
t.Errorf("Dial hang took too long: %v > %v", taken, max)
}
if err == nil {
t.Error("Call did not fail")
}
}
func TestDelayedLogFlushing(t *testing.T) {
f, c, cleanup := setup()
defer cleanup()
http.HandleFunc("/quick_log", func(w http.ResponseWriter, r *http.Request) {
c := NewContext(r)
c.Infof("It's a lovely day.")
w.WriteHeader(200)
w.Write(make([]byte, 100<<10)) // write 100 KB to force HTTP flush
})
r := &http.Request{
Method: "GET",
URL: &url.URL{
Scheme: "http",
Path: "/quick_log",
},
Header: c.req.Header,
Body: ioutil.NopCloser(bytes.NewReader(nil)),
}
w := httptest.NewRecorder()
// Check that log flushing does not hold up the HTTP response.
start := time.Now()
handleHTTP(w, r)
if d := time.Since(start); d > 10*time.Millisecond {
t.Errorf("handleHTTP took %v, want under 10ms", d)
}
const hdr = "X-AppEngine-Log-Flush-Count"
if h := w.HeaderMap.Get(hdr); h != "1" {
t.Errorf("%s header = %q, want %q", hdr, h, "1")
}
if f := atomic.LoadInt32(&f.LogFlushes); f != 0 {
t.Errorf("After HTTP response: f.LogFlushes = %d, want 0", f)
}
// Check that the log flush eventually comes in.
time.Sleep(100 * time.Millisecond)
if f := atomic.LoadInt32(&f.LogFlushes); f != 1 {
t.Errorf("After 100ms: f.LogFlushes = %d, want 1", f)
}
}
func TestRemoteAddr(t *testing.T) {
var addr string
http.HandleFunc("/remote_addr", func(w http.ResponseWriter, r *http.Request) {
addr = r.RemoteAddr
})
testCases := []struct {
headers http.Header
addr string
}{
{http.Header{"X-Appengine-User-Ip": []string{"10.5.2.1"}}, "10.5.2.1:80"},
{http.Header{"X-Appengine-Remote-Addr": []string{"1.2.3.4"}}, "1.2.3.4:80"},
{http.Header{"X-Appengine-Remote-Addr": []string{"1.2.3.4:8080"}}, "1.2.3.4:8080"},
{
http.Header{"X-Appengine-Remote-Addr": []string{"2401:fa00:9:1:7646:a0ff:fe90:ca66"}},
"[2401:fa00:9:1:7646:a0ff:fe90:ca66]:80",
},
{
http.Header{"X-Appengine-Remote-Addr": []string{"[::1]:http"}},
"[::1]:http",
},
{http.Header{}, "127.0.0.1:80"},
}
for _, tc := range testCases {
r := &http.Request{
Method: "GET",
URL: &url.URL{Scheme: "http", Path: "/remote_addr"},
Header: tc.headers,
Body: ioutil.NopCloser(bytes.NewReader(nil)),
}
handleHTTP(httptest.NewRecorder(), r)
if addr != tc.addr {
t.Errorf("Header %v, got %q, want %q", tc.headers, addr, tc.addr)
}
}
}
var raceDetector = false
func TestAPICallAllocations(t *testing.T) {
if raceDetector {
t.Skip("not running under race detector")
}
// Run the test API server in a subprocess so we aren't counting its allocations.
cleanup := launchHelperProcess(t)
defer cleanup()
c := &context{
req: &http.Request{
Header: http.Header{
ticketHeader: []string{"s3cr3t"},
dapperHeader: []string{"trace-001"},
},
},
}
req := &basepb.StringProto{
Value: proto.String("Doctor Who"),
}
res := &basepb.StringProto{}
opts := &CallOptions{
Timeout: 100 * time.Millisecond,
}
var apiErr error
avg := testing.AllocsPerRun(100, func() {
if err := c.Call("actordb", "LookupActor", req, res, opts); err != nil && apiErr == nil {
apiErr = err // get the first error only
}
})
if apiErr != nil {
t.Errorf("API call failed: %v", apiErr)
}
// Lots of room for improvement...
const min, max float64 = 75, 85
if avg < min || max < avg {
t.Errorf("Allocations per API call = %g, want in [%g,%g]", avg, min, max)
}
}
func launchHelperProcess(t *testing.T) (cleanup func()) {
cmd := exec.Command(os.Args[0], "-test.run=TestHelperProcess")
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
stdin, err := cmd.StdinPipe()
if err != nil {
t.Fatalf("StdinPipe: %v", err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
t.Fatalf("StdoutPipe: %v", err)
}
if err := cmd.Start(); err != nil {
t.Fatalf("Starting helper process: %v", err)
}
scan := bufio.NewScanner(stdout)
ok := false
for scan.Scan() {
line := scan.Text()
if hp := strings.TrimPrefix(line, helperProcessMagic); hp != line {
parts := strings.SplitN(hp, ":", 2)
os.Setenv("API_HOST", parts[0])
os.Setenv("API_PORT", parts[1])
ok = true
break
}
}
if err := scan.Err(); err != nil {
t.Fatalf("Scanning helper process stdout: %v", err)
}
if !ok {
t.Fatal("Helper process never reported")
}
return func() {
stdin.Close()
if err := cmd.Wait(); err != nil {
t.Errorf("Helper process did not exit cleanly: %v", err)
}
}
}
const helperProcessMagic = "A lovely helper process is listening at "
// This isn't a real test. It's used as a helper process.
func TestHelperProcess(*testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
defer os.Exit(0)
f := &fakeAPIHandler{}
srv := httptest.NewServer(f)
defer srv.Close()
fmt.Println(helperProcessMagic + strings.TrimPrefix(srv.URL, "http://"))
// Wait for stdin to be closed.
io.Copy(ioutil.Discard, os.Stdin)
}

View File

@@ -1,28 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package internal
import (
"strings"
)
func parseFullAppID(appid string) (partition, domain, displayID string) {
if i := strings.Index(appid, "~"); i != -1 {
partition, appid = appid[:i], appid[i+1:]
}
if i := strings.Index(appid, ":"); i != -1 {
domain, appid = appid[:i], appid[i+1:]
}
return partition, domain, appid
}
// appID returns "appid" or "domain.com:appid".
func appID(fullAppID string) string {
_, dom, dis := parseFullAppID(fullAppID)
if dom != "" {
return dom + ":" + dis
}
return dis
}

View File

@@ -1,34 +0,0 @@
// Copyright 2011 Google Inc. All Rights Reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package internal
import (
"testing"
)
func TestAppIDParsing(t *testing.T) {
testCases := []struct {
in string
partition, domain, displayID string
}{
{"simple-app-id", "", "", "simple-app-id"},
{"domain.com:domain-app-id", "", "domain.com", "domain-app-id"},
{"part~partition-app-id", "part", "", "partition-app-id"},
{"part~domain.com:display", "part", "domain.com", "display"},
}
for _, tc := range testCases {
part, dom, dis := parseFullAppID(tc.in)
if part != tc.partition {
t.Errorf("partition of %q: got %q, want %q", tc.in, part, tc.partition)
}
if dom != tc.domain {
t.Errorf("domain of %q: got %q, want %q", tc.in, dom, tc.domain)
}
if dis != tc.displayID {
t.Errorf("displayID of %q: got %q, want %q", tc.in, dis, tc.displayID)
}
}
}

View File

@@ -1,295 +0,0 @@
// Code generated by protoc-gen-go.
// source: google.golang.org/appengine/internal/app_identity/app_identity_service.proto
// DO NOT EDIT!
/*
Package app_identity is a generated protocol buffer package.
It is generated from these files:
google.golang.org/appengine/internal/app_identity/app_identity_service.proto
It has these top-level messages:
AppIdentityServiceError
SignForAppRequest
SignForAppResponse
GetPublicCertificateForAppRequest
PublicCertificate
GetPublicCertificateForAppResponse
GetServiceAccountNameRequest
GetServiceAccountNameResponse
GetAccessTokenRequest
GetAccessTokenResponse
GetDefaultGcsBucketNameRequest
GetDefaultGcsBucketNameResponse
*/
package app_identity
import proto "github.com/golang/protobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
type AppIdentityServiceError_ErrorCode int32
const (
AppIdentityServiceError_SUCCESS AppIdentityServiceError_ErrorCode = 0
AppIdentityServiceError_UNKNOWN_SCOPE AppIdentityServiceError_ErrorCode = 9
AppIdentityServiceError_BLOB_TOO_LARGE AppIdentityServiceError_ErrorCode = 1000
AppIdentityServiceError_DEADLINE_EXCEEDED AppIdentityServiceError_ErrorCode = 1001
AppIdentityServiceError_NOT_A_VALID_APP AppIdentityServiceError_ErrorCode = 1002
AppIdentityServiceError_UNKNOWN_ERROR AppIdentityServiceError_ErrorCode = 1003
AppIdentityServiceError_NOT_ALLOWED AppIdentityServiceError_ErrorCode = 1005
AppIdentityServiceError_NOT_IMPLEMENTED AppIdentityServiceError_ErrorCode = 1006
)
var AppIdentityServiceError_ErrorCode_name = map[int32]string{
0: "SUCCESS",
9: "UNKNOWN_SCOPE",
1000: "BLOB_TOO_LARGE",
1001: "DEADLINE_EXCEEDED",
1002: "NOT_A_VALID_APP",
1003: "UNKNOWN_ERROR",
1005: "NOT_ALLOWED",
1006: "NOT_IMPLEMENTED",
}
var AppIdentityServiceError_ErrorCode_value = map[string]int32{
"SUCCESS": 0,
"UNKNOWN_SCOPE": 9,
"BLOB_TOO_LARGE": 1000,
"DEADLINE_EXCEEDED": 1001,
"NOT_A_VALID_APP": 1002,
"UNKNOWN_ERROR": 1003,
"NOT_ALLOWED": 1005,
"NOT_IMPLEMENTED": 1006,
}
func (x AppIdentityServiceError_ErrorCode) Enum() *AppIdentityServiceError_ErrorCode {
p := new(AppIdentityServiceError_ErrorCode)
*p = x
return p
}
func (x AppIdentityServiceError_ErrorCode) String() string {
return proto.EnumName(AppIdentityServiceError_ErrorCode_name, int32(x))
}
func (x *AppIdentityServiceError_ErrorCode) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(AppIdentityServiceError_ErrorCode_value, data, "AppIdentityServiceError_ErrorCode")
if err != nil {
return err
}
*x = AppIdentityServiceError_ErrorCode(value)
return nil
}
type AppIdentityServiceError struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *AppIdentityServiceError) Reset() { *m = AppIdentityServiceError{} }
func (m *AppIdentityServiceError) String() string { return proto.CompactTextString(m) }
func (*AppIdentityServiceError) ProtoMessage() {}
type SignForAppRequest struct {
BytesToSign []byte `protobuf:"bytes,1,opt,name=bytes_to_sign" json:"bytes_to_sign,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *SignForAppRequest) Reset() { *m = SignForAppRequest{} }
func (m *SignForAppRequest) String() string { return proto.CompactTextString(m) }
func (*SignForAppRequest) ProtoMessage() {}
func (m *SignForAppRequest) GetBytesToSign() []byte {
if m != nil {
return m.BytesToSign
}
return nil
}
type SignForAppResponse struct {
KeyName *string `protobuf:"bytes,1,opt,name=key_name" json:"key_name,omitempty"`
SignatureBytes []byte `protobuf:"bytes,2,opt,name=signature_bytes" json:"signature_bytes,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *SignForAppResponse) Reset() { *m = SignForAppResponse{} }
func (m *SignForAppResponse) String() string { return proto.CompactTextString(m) }
func (*SignForAppResponse) ProtoMessage() {}
func (m *SignForAppResponse) GetKeyName() string {
if m != nil && m.KeyName != nil {
return *m.KeyName
}
return ""
}
func (m *SignForAppResponse) GetSignatureBytes() []byte {
if m != nil {
return m.SignatureBytes
}
return nil
}
type GetPublicCertificateForAppRequest struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *GetPublicCertificateForAppRequest) Reset() { *m = GetPublicCertificateForAppRequest{} }
func (m *GetPublicCertificateForAppRequest) String() string { return proto.CompactTextString(m) }
func (*GetPublicCertificateForAppRequest) ProtoMessage() {}
type PublicCertificate struct {
KeyName *string `protobuf:"bytes,1,opt,name=key_name" json:"key_name,omitempty"`
X509CertificatePem *string `protobuf:"bytes,2,opt,name=x509_certificate_pem" json:"x509_certificate_pem,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *PublicCertificate) Reset() { *m = PublicCertificate{} }
func (m *PublicCertificate) String() string { return proto.CompactTextString(m) }
func (*PublicCertificate) ProtoMessage() {}
func (m *PublicCertificate) GetKeyName() string {
if m != nil && m.KeyName != nil {
return *m.KeyName
}
return ""
}
func (m *PublicCertificate) GetX509CertificatePem() string {
if m != nil && m.X509CertificatePem != nil {
return *m.X509CertificatePem
}
return ""
}
type GetPublicCertificateForAppResponse struct {
PublicCertificateList []*PublicCertificate `protobuf:"bytes,1,rep,name=public_certificate_list" json:"public_certificate_list,omitempty"`
MaxClientCacheTimeInSecond *int64 `protobuf:"varint,2,opt,name=max_client_cache_time_in_second" json:"max_client_cache_time_in_second,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetPublicCertificateForAppResponse) Reset() { *m = GetPublicCertificateForAppResponse{} }
func (m *GetPublicCertificateForAppResponse) String() string { return proto.CompactTextString(m) }
func (*GetPublicCertificateForAppResponse) ProtoMessage() {}
func (m *GetPublicCertificateForAppResponse) GetPublicCertificateList() []*PublicCertificate {
if m != nil {
return m.PublicCertificateList
}
return nil
}
func (m *GetPublicCertificateForAppResponse) GetMaxClientCacheTimeInSecond() int64 {
if m != nil && m.MaxClientCacheTimeInSecond != nil {
return *m.MaxClientCacheTimeInSecond
}
return 0
}
type GetServiceAccountNameRequest struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *GetServiceAccountNameRequest) Reset() { *m = GetServiceAccountNameRequest{} }
func (m *GetServiceAccountNameRequest) String() string { return proto.CompactTextString(m) }
func (*GetServiceAccountNameRequest) ProtoMessage() {}
type GetServiceAccountNameResponse struct {
ServiceAccountName *string `protobuf:"bytes,1,opt,name=service_account_name" json:"service_account_name,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetServiceAccountNameResponse) Reset() { *m = GetServiceAccountNameResponse{} }
func (m *GetServiceAccountNameResponse) String() string { return proto.CompactTextString(m) }
func (*GetServiceAccountNameResponse) ProtoMessage() {}
func (m *GetServiceAccountNameResponse) GetServiceAccountName() string {
if m != nil && m.ServiceAccountName != nil {
return *m.ServiceAccountName
}
return ""
}
type GetAccessTokenRequest struct {
Scope []string `protobuf:"bytes,1,rep,name=scope" json:"scope,omitempty"`
ServiceAccountId *int64 `protobuf:"varint,2,opt,name=service_account_id" json:"service_account_id,omitempty"`
ServiceAccountName *string `protobuf:"bytes,3,opt,name=service_account_name" json:"service_account_name,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetAccessTokenRequest) Reset() { *m = GetAccessTokenRequest{} }
func (m *GetAccessTokenRequest) String() string { return proto.CompactTextString(m) }
func (*GetAccessTokenRequest) ProtoMessage() {}
func (m *GetAccessTokenRequest) GetScope() []string {
if m != nil {
return m.Scope
}
return nil
}
func (m *GetAccessTokenRequest) GetServiceAccountId() int64 {
if m != nil && m.ServiceAccountId != nil {
return *m.ServiceAccountId
}
return 0
}
func (m *GetAccessTokenRequest) GetServiceAccountName() string {
if m != nil && m.ServiceAccountName != nil {
return *m.ServiceAccountName
}
return ""
}
type GetAccessTokenResponse struct {
AccessToken *string `protobuf:"bytes,1,opt,name=access_token" json:"access_token,omitempty"`
ExpirationTime *int64 `protobuf:"varint,2,opt,name=expiration_time" json:"expiration_time,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetAccessTokenResponse) Reset() { *m = GetAccessTokenResponse{} }
func (m *GetAccessTokenResponse) String() string { return proto.CompactTextString(m) }
func (*GetAccessTokenResponse) ProtoMessage() {}
func (m *GetAccessTokenResponse) GetAccessToken() string {
if m != nil && m.AccessToken != nil {
return *m.AccessToken
}
return ""
}
func (m *GetAccessTokenResponse) GetExpirationTime() int64 {
if m != nil && m.ExpirationTime != nil {
return *m.ExpirationTime
}
return 0
}
type GetDefaultGcsBucketNameRequest struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *GetDefaultGcsBucketNameRequest) Reset() { *m = GetDefaultGcsBucketNameRequest{} }
func (m *GetDefaultGcsBucketNameRequest) String() string { return proto.CompactTextString(m) }
func (*GetDefaultGcsBucketNameRequest) ProtoMessage() {}
type GetDefaultGcsBucketNameResponse struct {
DefaultGcsBucketName *string `protobuf:"bytes,1,opt,name=default_gcs_bucket_name" json:"default_gcs_bucket_name,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetDefaultGcsBucketNameResponse) Reset() { *m = GetDefaultGcsBucketNameResponse{} }
func (m *GetDefaultGcsBucketNameResponse) String() string { return proto.CompactTextString(m) }
func (*GetDefaultGcsBucketNameResponse) ProtoMessage() {}
func (m *GetDefaultGcsBucketNameResponse) GetDefaultGcsBucketName() string {
if m != nil && m.DefaultGcsBucketName != nil {
return *m.DefaultGcsBucketName
}
return ""
}
func init() {
proto.RegisterEnum("appengine.AppIdentityServiceError_ErrorCode", AppIdentityServiceError_ErrorCode_name, AppIdentityServiceError_ErrorCode_value)
}

View File

@@ -1,64 +0,0 @@
syntax = "proto2";
option go_package = "app_identity";
package appengine;
message AppIdentityServiceError {
enum ErrorCode {
SUCCESS = 0;
UNKNOWN_SCOPE = 9;
BLOB_TOO_LARGE = 1000;
DEADLINE_EXCEEDED = 1001;
NOT_A_VALID_APP = 1002;
UNKNOWN_ERROR = 1003;
NOT_ALLOWED = 1005;
NOT_IMPLEMENTED = 1006;
}
}
message SignForAppRequest {
optional bytes bytes_to_sign = 1;
}
message SignForAppResponse {
optional string key_name = 1;
optional bytes signature_bytes = 2;
}
message GetPublicCertificateForAppRequest {
}
message PublicCertificate {
optional string key_name = 1;
optional string x509_certificate_pem = 2;
}
message GetPublicCertificateForAppResponse {
repeated PublicCertificate public_certificate_list = 1;
optional int64 max_client_cache_time_in_second = 2;
}
message GetServiceAccountNameRequest {
}
message GetServiceAccountNameResponse {
optional string service_account_name = 1;
}
message GetAccessTokenRequest {
repeated string scope = 1;
optional int64 service_account_id = 2;
optional string service_account_name = 3;
}
message GetAccessTokenResponse {
optional string access_token = 1;
optional int64 expiration_time = 2;
}
message GetDefaultGcsBucketNameRequest {
}
message GetDefaultGcsBucketNameResponse {
optional string default_gcs_bucket_name = 1;
}

View File

@@ -1,134 +0,0 @@
// Code generated by protoc-gen-go.
// source: google.golang.org/appengine/internal/base/api_base.proto
// DO NOT EDIT!
/*
Package base is a generated protocol buffer package.
It is generated from these files:
google.golang.org/appengine/internal/base/api_base.proto
It has these top-level messages:
StringProto
Integer32Proto
Integer64Proto
BoolProto
DoubleProto
BytesProto
VoidProto
*/
package base
import proto "github.com/golang/protobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
type StringProto struct {
Value *string `protobuf:"bytes,1,req,name=value" json:"value,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StringProto) Reset() { *m = StringProto{} }
func (m *StringProto) String() string { return proto.CompactTextString(m) }
func (*StringProto) ProtoMessage() {}
func (m *StringProto) GetValue() string {
if m != nil && m.Value != nil {
return *m.Value
}
return ""
}
type Integer32Proto struct {
Value *int32 `protobuf:"varint,1,req,name=value" json:"value,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Integer32Proto) Reset() { *m = Integer32Proto{} }
func (m *Integer32Proto) String() string { return proto.CompactTextString(m) }
func (*Integer32Proto) ProtoMessage() {}
func (m *Integer32Proto) GetValue() int32 {
if m != nil && m.Value != nil {
return *m.Value
}
return 0
}
type Integer64Proto struct {
Value *int64 `protobuf:"varint,1,req,name=value" json:"value,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Integer64Proto) Reset() { *m = Integer64Proto{} }
func (m *Integer64Proto) String() string { return proto.CompactTextString(m) }
func (*Integer64Proto) ProtoMessage() {}
func (m *Integer64Proto) GetValue() int64 {
if m != nil && m.Value != nil {
return *m.Value
}
return 0
}
type BoolProto struct {
Value *bool `protobuf:"varint,1,req,name=value" json:"value,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *BoolProto) Reset() { *m = BoolProto{} }
func (m *BoolProto) String() string { return proto.CompactTextString(m) }
func (*BoolProto) ProtoMessage() {}
func (m *BoolProto) GetValue() bool {
if m != nil && m.Value != nil {
return *m.Value
}
return false
}
type DoubleProto struct {
Value *float64 `protobuf:"fixed64,1,req,name=value" json:"value,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *DoubleProto) Reset() { *m = DoubleProto{} }
func (m *DoubleProto) String() string { return proto.CompactTextString(m) }
func (*DoubleProto) ProtoMessage() {}
func (m *DoubleProto) GetValue() float64 {
if m != nil && m.Value != nil {
return *m.Value
}
return 0
}
type BytesProto struct {
Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *BytesProto) Reset() { *m = BytesProto{} }
func (m *BytesProto) String() string { return proto.CompactTextString(m) }
func (*BytesProto) ProtoMessage() {}
func (m *BytesProto) GetValue() []byte {
if m != nil {
return m.Value
}
return nil
}
type VoidProto struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *VoidProto) Reset() { *m = VoidProto{} }
func (m *VoidProto) String() string { return proto.CompactTextString(m) }
func (*VoidProto) ProtoMessage() {}
func init() {
}

View File

@@ -1,33 +0,0 @@
// Built-in base types for API calls. Primarily useful as return types.
syntax = "proto2";
option go_package = "base";
package appengine.base;
message StringProto {
required string value = 1;
}
message Integer32Proto {
required int32 value = 1;
}
message Integer64Proto {
required int64 value = 1;
}
message BoolProto {
required bool value = 1;
}
message DoubleProto {
required double value = 1;
}
message BytesProto {
required bytes value = 1 [ctype=CORD];
}
message VoidProto {
}

View File

@@ -1,153 +0,0 @@
// Code generated by protoc-gen-go.
// source: google.golang.org/appengine/internal/channel/channel_service.proto
// DO NOT EDIT!
/*
Package channel is a generated protocol buffer package.
It is generated from these files:
google.golang.org/appengine/internal/channel/channel_service.proto
It has these top-level messages:
ChannelServiceError
CreateChannelRequest
CreateChannelResponse
SendMessageRequest
*/
package channel
import proto "github.com/golang/protobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
type ChannelServiceError_ErrorCode int32
const (
ChannelServiceError_OK ChannelServiceError_ErrorCode = 0
ChannelServiceError_INTERNAL_ERROR ChannelServiceError_ErrorCode = 1
ChannelServiceError_INVALID_CHANNEL_KEY ChannelServiceError_ErrorCode = 2
ChannelServiceError_BAD_MESSAGE ChannelServiceError_ErrorCode = 3
ChannelServiceError_INVALID_CHANNEL_TOKEN_DURATION ChannelServiceError_ErrorCode = 4
ChannelServiceError_APPID_ALIAS_REQUIRED ChannelServiceError_ErrorCode = 5
)
var ChannelServiceError_ErrorCode_name = map[int32]string{
0: "OK",
1: "INTERNAL_ERROR",
2: "INVALID_CHANNEL_KEY",
3: "BAD_MESSAGE",
4: "INVALID_CHANNEL_TOKEN_DURATION",
5: "APPID_ALIAS_REQUIRED",
}
var ChannelServiceError_ErrorCode_value = map[string]int32{
"OK": 0,
"INTERNAL_ERROR": 1,
"INVALID_CHANNEL_KEY": 2,
"BAD_MESSAGE": 3,
"INVALID_CHANNEL_TOKEN_DURATION": 4,
"APPID_ALIAS_REQUIRED": 5,
}
func (x ChannelServiceError_ErrorCode) Enum() *ChannelServiceError_ErrorCode {
p := new(ChannelServiceError_ErrorCode)
*p = x
return p
}
func (x ChannelServiceError_ErrorCode) String() string {
return proto.EnumName(ChannelServiceError_ErrorCode_name, int32(x))
}
func (x *ChannelServiceError_ErrorCode) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(ChannelServiceError_ErrorCode_value, data, "ChannelServiceError_ErrorCode")
if err != nil {
return err
}
*x = ChannelServiceError_ErrorCode(value)
return nil
}
type ChannelServiceError struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *ChannelServiceError) Reset() { *m = ChannelServiceError{} }
func (m *ChannelServiceError) String() string { return proto.CompactTextString(m) }
func (*ChannelServiceError) ProtoMessage() {}
type CreateChannelRequest struct {
ApplicationKey *string `protobuf:"bytes,1,req,name=application_key" json:"application_key,omitempty"`
DurationMinutes *int32 `protobuf:"varint,2,opt,name=duration_minutes" json:"duration_minutes,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *CreateChannelRequest) Reset() { *m = CreateChannelRequest{} }
func (m *CreateChannelRequest) String() string { return proto.CompactTextString(m) }
func (*CreateChannelRequest) ProtoMessage() {}
func (m *CreateChannelRequest) GetApplicationKey() string {
if m != nil && m.ApplicationKey != nil {
return *m.ApplicationKey
}
return ""
}
func (m *CreateChannelRequest) GetDurationMinutes() int32 {
if m != nil && m.DurationMinutes != nil {
return *m.DurationMinutes
}
return 0
}
type CreateChannelResponse struct {
Token *string `protobuf:"bytes,2,opt,name=token" json:"token,omitempty"`
DurationMinutes *int32 `protobuf:"varint,3,opt,name=duration_minutes" json:"duration_minutes,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *CreateChannelResponse) Reset() { *m = CreateChannelResponse{} }
func (m *CreateChannelResponse) String() string { return proto.CompactTextString(m) }
func (*CreateChannelResponse) ProtoMessage() {}
func (m *CreateChannelResponse) GetToken() string {
if m != nil && m.Token != nil {
return *m.Token
}
return ""
}
func (m *CreateChannelResponse) GetDurationMinutes() int32 {
if m != nil && m.DurationMinutes != nil {
return *m.DurationMinutes
}
return 0
}
type SendMessageRequest struct {
ApplicationKey *string `protobuf:"bytes,1,req,name=application_key" json:"application_key,omitempty"`
Message *string `protobuf:"bytes,2,req,name=message" json:"message,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *SendMessageRequest) Reset() { *m = SendMessageRequest{} }
func (m *SendMessageRequest) String() string { return proto.CompactTextString(m) }
func (*SendMessageRequest) ProtoMessage() {}
func (m *SendMessageRequest) GetApplicationKey() string {
if m != nil && m.ApplicationKey != nil {
return *m.ApplicationKey
}
return ""
}
func (m *SendMessageRequest) GetMessage() string {
if m != nil && m.Message != nil {
return *m.Message
}
return ""
}
func init() {
proto.RegisterEnum("appengine.ChannelServiceError_ErrorCode", ChannelServiceError_ErrorCode_name, ChannelServiceError_ErrorCode_value)
}

View File

@@ -1,30 +0,0 @@
syntax = "proto2";
option go_package = "channel";
package appengine;
message ChannelServiceError {
enum ErrorCode {
OK = 0;
INTERNAL_ERROR = 1;
INVALID_CHANNEL_KEY = 2;
BAD_MESSAGE = 3;
INVALID_CHANNEL_TOKEN_DURATION = 4;
APPID_ALIAS_REQUIRED = 5;
}
}
message CreateChannelRequest {
required string application_key = 1;
optional int32 duration_minutes = 2;
}
message CreateChannelResponse {
optional string token = 2;
optional int32 duration_minutes = 3;
}
message SendMessageRequest {
required string application_key = 1;
required string message = 2;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,541 +0,0 @@
syntax = "proto2";
option go_package = "datastore";
package appengine;
message Action{}
message PropertyValue {
optional int64 int64Value = 1;
optional bool booleanValue = 2;
optional string stringValue = 3;
optional double doubleValue = 4;
optional group PointValue = 5 {
required double x = 6;
required double y = 7;
}
optional group UserValue = 8 {
required string email = 9;
required string auth_domain = 10;
optional string nickname = 11;
optional string federated_identity = 21;
optional string federated_provider = 22;
}
optional group ReferenceValue = 12 {
required string app = 13;
optional string name_space = 20;
repeated group PathElement = 14 {
required string type = 15;
optional int64 id = 16;
optional string name = 17;
}
}
}
message Property {
enum Meaning {
NO_MEANING = 0;
BLOB = 14;
TEXT = 15;
BYTESTRING = 16;
ATOM_CATEGORY = 1;
ATOM_LINK = 2;
ATOM_TITLE = 3;
ATOM_CONTENT = 4;
ATOM_SUMMARY = 5;
ATOM_AUTHOR = 6;
GD_WHEN = 7;
GD_EMAIL = 8;
GEORSS_POINT = 9;
GD_IM = 10;
GD_PHONENUMBER = 11;
GD_POSTALADDRESS = 12;
GD_RATING = 13;
BLOBKEY = 17;
ENTITY_PROTO = 19;
INDEX_VALUE = 18;
};
optional Meaning meaning = 1 [default = NO_MEANING];
optional string meaning_uri = 2;
required string name = 3;
required PropertyValue value = 5;
required bool multiple = 4;
optional bool searchable = 6 [default=false];
enum FtsTokenizationOption {
HTML = 1;
ATOM = 2;
}
optional FtsTokenizationOption fts_tokenization_option = 8;
optional string locale = 9 [default = "en"];
}
message Path {
repeated group Element = 1 {
required string type = 2;
optional int64 id = 3;
optional string name = 4;
}
}
message Reference {
required string app = 13;
optional string name_space = 20;
required Path path = 14;
}
message User {
required string email = 1;
required string auth_domain = 2;
optional string nickname = 3;
optional string federated_identity = 6;
optional string federated_provider = 7;
}
message EntityProto {
required Reference key = 13;
required Path entity_group = 16;
optional User owner = 17;
enum Kind {
GD_CONTACT = 1;
GD_EVENT = 2;
GD_MESSAGE = 3;
}
optional Kind kind = 4;
optional string kind_uri = 5;
repeated Property property = 14;
repeated Property raw_property = 15;
optional int32 rank = 18;
}
message CompositeProperty {
required int64 index_id = 1;
repeated string value = 2;
}
message Index {
required string entity_type = 1;
required bool ancestor = 5;
repeated group Property = 2 {
required string name = 3;
enum Direction {
ASCENDING = 1;
DESCENDING = 2;
}
optional Direction direction = 4 [default = ASCENDING];
}
}
message CompositeIndex {
required string app_id = 1;
required int64 id = 2;
required Index definition = 3;
enum State {
WRITE_ONLY = 1;
READ_WRITE = 2;
DELETED = 3;
ERROR = 4;
}
required State state = 4;
optional bool only_use_if_required = 6 [default = false];
}
message IndexPostfix {
message IndexValue {
required string property_name = 1;
required PropertyValue value = 2;
}
repeated IndexValue index_value = 1;
optional Reference key = 2;
optional bool before = 3 [default=true];
}
message IndexPosition {
optional string key = 1;
optional bool before = 2 [default=true];
}
message Snapshot {
enum Status {
INACTIVE = 0;
ACTIVE = 1;
}
required int64 ts = 1;
}
message InternalHeader {
optional string qos = 1;
}
message Transaction {
optional InternalHeader header = 4;
required fixed64 handle = 1;
required string app = 2;
optional bool mark_changes = 3 [default = false];
}
message Query {
optional InternalHeader header = 39;
required string app = 1;
optional string name_space = 29;
optional string kind = 3;
optional Reference ancestor = 17;
repeated group Filter = 4 {
enum Operator {
LESS_THAN = 1;
LESS_THAN_OR_EQUAL = 2;
GREATER_THAN = 3;
GREATER_THAN_OR_EQUAL = 4;
EQUAL = 5;
IN = 6;
EXISTS = 7;
}
required Operator op = 6;
repeated Property property = 14;
}
optional string search_query = 8;
repeated group Order = 9 {
enum Direction {
ASCENDING = 1;
DESCENDING = 2;
}
required string property = 10;
optional Direction direction = 11 [default = ASCENDING];
}
enum Hint {
ORDER_FIRST = 1;
ANCESTOR_FIRST = 2;
FILTER_FIRST = 3;
}
optional Hint hint = 18;
optional int32 count = 23;
optional int32 offset = 12 [default = 0];
optional int32 limit = 16;
optional CompiledCursor compiled_cursor = 30;
optional CompiledCursor end_compiled_cursor = 31;
repeated CompositeIndex composite_index = 19;
optional bool require_perfect_plan = 20 [default = false];
optional bool keys_only = 21 [default = false];
optional Transaction transaction = 22;
optional bool compile = 25 [default = false];
optional int64 failover_ms = 26;
optional bool strong = 32;
repeated string property_name = 33;
repeated string group_by_property_name = 34;
optional bool distinct = 24;
optional int64 min_safe_time_seconds = 35;
repeated string safe_replica_name = 36;
optional bool persist_offset = 37 [default=false];
}
message CompiledQuery {
required group PrimaryScan = 1 {
optional string index_name = 2;
optional string start_key = 3;
optional bool start_inclusive = 4;
optional string end_key = 5;
optional bool end_inclusive = 6;
repeated string start_postfix_value = 22;
repeated string end_postfix_value = 23;
optional int64 end_unapplied_log_timestamp_us = 19;
}
repeated group MergeJoinScan = 7 {
required string index_name = 8;
repeated string prefix_value = 9;
optional bool value_prefix = 20 [default=false];
}
optional Index index_def = 21;
optional int32 offset = 10 [default = 0];
optional int32 limit = 11;
required bool keys_only = 12;
repeated string property_name = 24;
optional int32 distinct_infix_size = 25;
optional group EntityFilter = 13 {
optional bool distinct = 14 [default=false];
optional string kind = 17;
optional Reference ancestor = 18;
}
}
message CompiledCursor {
optional group Position = 2 {
optional string start_key = 27;
repeated group IndexValue = 29 {
optional string property = 30;
required PropertyValue value = 31;
}
optional Reference key = 32;
optional bool start_inclusive = 28 [default=true];
}
}
message Cursor {
required fixed64 cursor = 1;
optional string app = 2;
}
message Error {
enum ErrorCode {
BAD_REQUEST = 1;
CONCURRENT_TRANSACTION = 2;
INTERNAL_ERROR = 3;
NEED_INDEX = 4;
TIMEOUT = 5;
PERMISSION_DENIED = 6;
BIGTABLE_ERROR = 7;
COMMITTED_BUT_STILL_APPLYING = 8;
CAPABILITY_DISABLED = 9;
TRY_ALTERNATE_BACKEND = 10;
SAFE_TIME_TOO_OLD = 11;
}
}
message Cost {
optional int32 index_writes = 1;
optional int32 index_write_bytes = 2;
optional int32 entity_writes = 3;
optional int32 entity_write_bytes = 4;
optional group CommitCost = 5 {
optional int32 requested_entity_puts = 6;
optional int32 requested_entity_deletes = 7;
};
optional int32 approximate_storage_delta = 8;
optional int32 id_sequence_updates = 9;
}
message GetRequest {
optional InternalHeader header = 6;
repeated Reference key = 1;
optional Transaction transaction = 2;
optional int64 failover_ms = 3;
optional bool strong = 4;
optional bool allow_deferred = 5 [default=false];
}
message GetResponse {
repeated group Entity = 1 {
optional EntityProto entity = 2;
optional Reference key = 4;
optional int64 version = 3;
}
repeated Reference deferred = 5;
optional bool in_order = 6 [default=true];
}
message PutRequest {
optional InternalHeader header = 11;
repeated EntityProto entity = 1;
optional Transaction transaction = 2;
repeated CompositeIndex composite_index = 3;
optional bool trusted = 4 [default = false];
optional bool force = 7 [default = false];
optional bool mark_changes = 8 [default = false];
repeated Snapshot snapshot = 9;
enum AutoIdPolicy {
CURRENT = 0;
SEQUENTIAL = 1;
}
optional AutoIdPolicy auto_id_policy = 10 [default = CURRENT];
}
message PutResponse {
repeated Reference key = 1;
optional Cost cost = 2;
repeated int64 version = 3;
}
message TouchRequest {
optional InternalHeader header = 10;
repeated Reference key = 1;
repeated CompositeIndex composite_index = 2;
optional bool force = 3 [default = false];
repeated Snapshot snapshot = 9;
}
message TouchResponse {
optional Cost cost = 1;
}
message DeleteRequest {
optional InternalHeader header = 10;
repeated Reference key = 6;
optional Transaction transaction = 5;
optional bool trusted = 4 [default = false];
optional bool force = 7 [default = false];
optional bool mark_changes = 8 [default = false];
repeated Snapshot snapshot = 9;
}
message DeleteResponse {
optional Cost cost = 1;
repeated int64 version = 3;
}
message NextRequest {
optional InternalHeader header = 5;
required Cursor cursor = 1;
optional int32 count = 2;
optional int32 offset = 4 [default = 0];
optional bool compile = 3 [default = false];
}
message QueryResult {
optional Cursor cursor = 1;
repeated EntityProto result = 2;
optional int32 skipped_results = 7;
required bool more_results = 3;
optional bool keys_only = 4;
optional bool index_only = 9;
optional bool small_ops = 10;
optional CompiledQuery compiled_query = 5;
optional CompiledCursor compiled_cursor = 6;
repeated CompositeIndex index = 8;
repeated int64 version = 11;
}
message AllocateIdsRequest {
optional InternalHeader header = 4;
optional Reference model_key = 1;
optional int64 size = 2;
optional int64 max = 3;
repeated Reference reserve = 5;
}
message AllocateIdsResponse {
required int64 start = 1;
required int64 end = 2;
optional Cost cost = 3;
}
message CompositeIndices {
repeated CompositeIndex index = 1;
}
message AddActionsRequest {
optional InternalHeader header = 3;
required Transaction transaction = 1;
repeated Action action = 2;
}
message AddActionsResponse {
}
message BeginTransactionRequest {
optional InternalHeader header = 3;
required string app = 1;
optional bool allow_multiple_eg = 2 [default = false];
}
message CommitResponse {
optional Cost cost = 1;
repeated group Version = 3 {
required Reference root_entity_key = 4;
required int64 version = 5;
}
}

View File

@@ -1,12 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package internal
// These functions are implementations of the wrapper functions
// in ../appengine/identity.go. See that file for commentary.
func AppID(fqai string) string {
return appID(fqai)
}

View File

@@ -1,85 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package internal
import (
"net/http"
"os"
)
// These functions are implementations of the wrapper functions
// in ../appengine/identity.go. See that file for commentary.
const (
hDefaultVersionHostname = "X-AppEngine-Default-Version-Hostname"
hRequestLogId = "X-AppEngine-Request-Log-Id"
hDatacenter = "X-AppEngine-Datacenter"
)
func DefaultVersionHostname(req interface{}) string {
return req.(*http.Request).Header.Get(hDefaultVersionHostname)
}
func RequestID(req interface{}) string {
return req.(*http.Request).Header.Get(hRequestLogId)
}
func Datacenter(req interface{}) string {
return req.(*http.Request).Header.Get(hDatacenter)
}
func ServerSoftware() string {
// TODO(dsymonds): Remove fallback when we've verified this.
if s := os.Getenv("SERVER_SOFTWARE"); s != "" {
return s
}
return "Google App Engine/1.x.x"
}
// TODO(dsymonds): Remove the metadata fetches.
func ModuleName() string {
if s := os.Getenv("GAE_MODULE_NAME"); s != "" {
return s
}
return string(mustGetMetadata("instance/attributes/gae_backend_name"))
}
func VersionID() string {
if s := os.Getenv("GAE_MODULE_VERSION"); s != "" {
return s
}
return string(mustGetMetadata("instance/attributes/gae_backend_version"))
}
func InstanceID() string {
if s := os.Getenv("GAE_MODULE_INSTANCE"); s != "" {
return s
}
return string(mustGetMetadata("instance/attributes/gae_backend_instance"))
}
func partitionlessAppID() string {
// gae_project has everything except the partition prefix.
appID := os.Getenv("GAE_LONG_APP_ID")
if appID == "" {
appID = string(mustGetMetadata("instance/attributes/gae_project"))
}
return appID
}
func fullyQualifiedAppID() string {
appID := partitionlessAppID()
part := os.Getenv("GAE_PARTITION")
if part == "" {
part = string(mustGetMetadata("instance/attributes/gae_partition"))
}
if part != "" {
appID = part + "~" + appID
}
return appID
}

View File

@@ -1,848 +0,0 @@
// Code generated by protoc-gen-go.
// source: google.golang.org/appengine/internal/image/images_service.proto
// DO NOT EDIT!
/*
Package image is a generated protocol buffer package.
It is generated from these files:
google.golang.org/appengine/internal/image/images_service.proto
It has these top-level messages:
ImagesServiceError
ImagesServiceTransform
Transform
ImageData
InputSettings
OutputSettings
ImagesTransformRequest
ImagesTransformResponse
CompositeImageOptions
ImagesCanvas
ImagesCompositeRequest
ImagesCompositeResponse
ImagesHistogramRequest
ImagesHistogram
ImagesHistogramResponse
ImagesGetUrlBaseRequest
ImagesGetUrlBaseResponse
ImagesDeleteUrlBaseRequest
ImagesDeleteUrlBaseResponse
*/
package image
import proto "github.com/golang/protobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
type ImagesServiceError_ErrorCode int32
const (
ImagesServiceError_UNSPECIFIED_ERROR ImagesServiceError_ErrorCode = 1
ImagesServiceError_BAD_TRANSFORM_DATA ImagesServiceError_ErrorCode = 2
ImagesServiceError_NOT_IMAGE ImagesServiceError_ErrorCode = 3
ImagesServiceError_BAD_IMAGE_DATA ImagesServiceError_ErrorCode = 4
ImagesServiceError_IMAGE_TOO_LARGE ImagesServiceError_ErrorCode = 5
ImagesServiceError_INVALID_BLOB_KEY ImagesServiceError_ErrorCode = 6
ImagesServiceError_ACCESS_DENIED ImagesServiceError_ErrorCode = 7
ImagesServiceError_OBJECT_NOT_FOUND ImagesServiceError_ErrorCode = 8
)
var ImagesServiceError_ErrorCode_name = map[int32]string{
1: "UNSPECIFIED_ERROR",
2: "BAD_TRANSFORM_DATA",
3: "NOT_IMAGE",
4: "BAD_IMAGE_DATA",
5: "IMAGE_TOO_LARGE",
6: "INVALID_BLOB_KEY",
7: "ACCESS_DENIED",
8: "OBJECT_NOT_FOUND",
}
var ImagesServiceError_ErrorCode_value = map[string]int32{
"UNSPECIFIED_ERROR": 1,
"BAD_TRANSFORM_DATA": 2,
"NOT_IMAGE": 3,
"BAD_IMAGE_DATA": 4,
"IMAGE_TOO_LARGE": 5,
"INVALID_BLOB_KEY": 6,
"ACCESS_DENIED": 7,
"OBJECT_NOT_FOUND": 8,
}
func (x ImagesServiceError_ErrorCode) Enum() *ImagesServiceError_ErrorCode {
p := new(ImagesServiceError_ErrorCode)
*p = x
return p
}
func (x ImagesServiceError_ErrorCode) String() string {
return proto.EnumName(ImagesServiceError_ErrorCode_name, int32(x))
}
func (x *ImagesServiceError_ErrorCode) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(ImagesServiceError_ErrorCode_value, data, "ImagesServiceError_ErrorCode")
if err != nil {
return err
}
*x = ImagesServiceError_ErrorCode(value)
return nil
}
type ImagesServiceTransform_Type int32
const (
ImagesServiceTransform_RESIZE ImagesServiceTransform_Type = 1
ImagesServiceTransform_ROTATE ImagesServiceTransform_Type = 2
ImagesServiceTransform_HORIZONTAL_FLIP ImagesServiceTransform_Type = 3
ImagesServiceTransform_VERTICAL_FLIP ImagesServiceTransform_Type = 4
ImagesServiceTransform_CROP ImagesServiceTransform_Type = 5
ImagesServiceTransform_IM_FEELING_LUCKY ImagesServiceTransform_Type = 6
)
var ImagesServiceTransform_Type_name = map[int32]string{
1: "RESIZE",
2: "ROTATE",
3: "HORIZONTAL_FLIP",
4: "VERTICAL_FLIP",
5: "CROP",
6: "IM_FEELING_LUCKY",
}
var ImagesServiceTransform_Type_value = map[string]int32{
"RESIZE": 1,
"ROTATE": 2,
"HORIZONTAL_FLIP": 3,
"VERTICAL_FLIP": 4,
"CROP": 5,
"IM_FEELING_LUCKY": 6,
}
func (x ImagesServiceTransform_Type) Enum() *ImagesServiceTransform_Type {
p := new(ImagesServiceTransform_Type)
*p = x
return p
}
func (x ImagesServiceTransform_Type) String() string {
return proto.EnumName(ImagesServiceTransform_Type_name, int32(x))
}
func (x *ImagesServiceTransform_Type) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(ImagesServiceTransform_Type_value, data, "ImagesServiceTransform_Type")
if err != nil {
return err
}
*x = ImagesServiceTransform_Type(value)
return nil
}
type InputSettings_ORIENTATION_CORRECTION_TYPE int32
const (
InputSettings_UNCHANGED_ORIENTATION InputSettings_ORIENTATION_CORRECTION_TYPE = 0
InputSettings_CORRECT_ORIENTATION InputSettings_ORIENTATION_CORRECTION_TYPE = 1
)
var InputSettings_ORIENTATION_CORRECTION_TYPE_name = map[int32]string{
0: "UNCHANGED_ORIENTATION",
1: "CORRECT_ORIENTATION",
}
var InputSettings_ORIENTATION_CORRECTION_TYPE_value = map[string]int32{
"UNCHANGED_ORIENTATION": 0,
"CORRECT_ORIENTATION": 1,
}
func (x InputSettings_ORIENTATION_CORRECTION_TYPE) Enum() *InputSettings_ORIENTATION_CORRECTION_TYPE {
p := new(InputSettings_ORIENTATION_CORRECTION_TYPE)
*p = x
return p
}
func (x InputSettings_ORIENTATION_CORRECTION_TYPE) String() string {
return proto.EnumName(InputSettings_ORIENTATION_CORRECTION_TYPE_name, int32(x))
}
func (x *InputSettings_ORIENTATION_CORRECTION_TYPE) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(InputSettings_ORIENTATION_CORRECTION_TYPE_value, data, "InputSettings_ORIENTATION_CORRECTION_TYPE")
if err != nil {
return err
}
*x = InputSettings_ORIENTATION_CORRECTION_TYPE(value)
return nil
}
type OutputSettings_MIME_TYPE int32
const (
OutputSettings_PNG OutputSettings_MIME_TYPE = 0
OutputSettings_JPEG OutputSettings_MIME_TYPE = 1
OutputSettings_WEBP OutputSettings_MIME_TYPE = 2
)
var OutputSettings_MIME_TYPE_name = map[int32]string{
0: "PNG",
1: "JPEG",
2: "WEBP",
}
var OutputSettings_MIME_TYPE_value = map[string]int32{
"PNG": 0,
"JPEG": 1,
"WEBP": 2,
}
func (x OutputSettings_MIME_TYPE) Enum() *OutputSettings_MIME_TYPE {
p := new(OutputSettings_MIME_TYPE)
*p = x
return p
}
func (x OutputSettings_MIME_TYPE) String() string {
return proto.EnumName(OutputSettings_MIME_TYPE_name, int32(x))
}
func (x *OutputSettings_MIME_TYPE) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(OutputSettings_MIME_TYPE_value, data, "OutputSettings_MIME_TYPE")
if err != nil {
return err
}
*x = OutputSettings_MIME_TYPE(value)
return nil
}
type CompositeImageOptions_ANCHOR int32
const (
CompositeImageOptions_TOP_LEFT CompositeImageOptions_ANCHOR = 0
CompositeImageOptions_TOP CompositeImageOptions_ANCHOR = 1
CompositeImageOptions_TOP_RIGHT CompositeImageOptions_ANCHOR = 2
CompositeImageOptions_LEFT CompositeImageOptions_ANCHOR = 3
CompositeImageOptions_CENTER CompositeImageOptions_ANCHOR = 4
CompositeImageOptions_RIGHT CompositeImageOptions_ANCHOR = 5
CompositeImageOptions_BOTTOM_LEFT CompositeImageOptions_ANCHOR = 6
CompositeImageOptions_BOTTOM CompositeImageOptions_ANCHOR = 7
CompositeImageOptions_BOTTOM_RIGHT CompositeImageOptions_ANCHOR = 8
)
var CompositeImageOptions_ANCHOR_name = map[int32]string{
0: "TOP_LEFT",
1: "TOP",
2: "TOP_RIGHT",
3: "LEFT",
4: "CENTER",
5: "RIGHT",
6: "BOTTOM_LEFT",
7: "BOTTOM",
8: "BOTTOM_RIGHT",
}
var CompositeImageOptions_ANCHOR_value = map[string]int32{
"TOP_LEFT": 0,
"TOP": 1,
"TOP_RIGHT": 2,
"LEFT": 3,
"CENTER": 4,
"RIGHT": 5,
"BOTTOM_LEFT": 6,
"BOTTOM": 7,
"BOTTOM_RIGHT": 8,
}
func (x CompositeImageOptions_ANCHOR) Enum() *CompositeImageOptions_ANCHOR {
p := new(CompositeImageOptions_ANCHOR)
*p = x
return p
}
func (x CompositeImageOptions_ANCHOR) String() string {
return proto.EnumName(CompositeImageOptions_ANCHOR_name, int32(x))
}
func (x *CompositeImageOptions_ANCHOR) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(CompositeImageOptions_ANCHOR_value, data, "CompositeImageOptions_ANCHOR")
if err != nil {
return err
}
*x = CompositeImageOptions_ANCHOR(value)
return nil
}
type ImagesServiceError struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesServiceError) Reset() { *m = ImagesServiceError{} }
func (m *ImagesServiceError) String() string { return proto.CompactTextString(m) }
func (*ImagesServiceError) ProtoMessage() {}
type ImagesServiceTransform struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesServiceTransform) Reset() { *m = ImagesServiceTransform{} }
func (m *ImagesServiceTransform) String() string { return proto.CompactTextString(m) }
func (*ImagesServiceTransform) ProtoMessage() {}
type Transform struct {
Width *int32 `protobuf:"varint,1,opt,name=width" json:"width,omitempty"`
Height *int32 `protobuf:"varint,2,opt,name=height" json:"height,omitempty"`
CropToFit *bool `protobuf:"varint,11,opt,name=crop_to_fit,def=0" json:"crop_to_fit,omitempty"`
CropOffsetX *float32 `protobuf:"fixed32,12,opt,name=crop_offset_x,def=0.5" json:"crop_offset_x,omitempty"`
CropOffsetY *float32 `protobuf:"fixed32,13,opt,name=crop_offset_y,def=0.5" json:"crop_offset_y,omitempty"`
Rotate *int32 `protobuf:"varint,3,opt,name=rotate,def=0" json:"rotate,omitempty"`
HorizontalFlip *bool `protobuf:"varint,4,opt,name=horizontal_flip,def=0" json:"horizontal_flip,omitempty"`
VerticalFlip *bool `protobuf:"varint,5,opt,name=vertical_flip,def=0" json:"vertical_flip,omitempty"`
CropLeftX *float32 `protobuf:"fixed32,6,opt,name=crop_left_x,def=0" json:"crop_left_x,omitempty"`
CropTopY *float32 `protobuf:"fixed32,7,opt,name=crop_top_y,def=0" json:"crop_top_y,omitempty"`
CropRightX *float32 `protobuf:"fixed32,8,opt,name=crop_right_x,def=1" json:"crop_right_x,omitempty"`
CropBottomY *float32 `protobuf:"fixed32,9,opt,name=crop_bottom_y,def=1" json:"crop_bottom_y,omitempty"`
Autolevels *bool `protobuf:"varint,10,opt,name=autolevels,def=0" json:"autolevels,omitempty"`
AllowStretch *bool `protobuf:"varint,14,opt,name=allow_stretch,def=0" json:"allow_stretch,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Transform) Reset() { *m = Transform{} }
func (m *Transform) String() string { return proto.CompactTextString(m) }
func (*Transform) ProtoMessage() {}
const Default_Transform_CropToFit bool = false
const Default_Transform_CropOffsetX float32 = 0.5
const Default_Transform_CropOffsetY float32 = 0.5
const Default_Transform_Rotate int32 = 0
const Default_Transform_HorizontalFlip bool = false
const Default_Transform_VerticalFlip bool = false
const Default_Transform_CropLeftX float32 = 0
const Default_Transform_CropTopY float32 = 0
const Default_Transform_CropRightX float32 = 1
const Default_Transform_CropBottomY float32 = 1
const Default_Transform_Autolevels bool = false
const Default_Transform_AllowStretch bool = false
func (m *Transform) GetWidth() int32 {
if m != nil && m.Width != nil {
return *m.Width
}
return 0
}
func (m *Transform) GetHeight() int32 {
if m != nil && m.Height != nil {
return *m.Height
}
return 0
}
func (m *Transform) GetCropToFit() bool {
if m != nil && m.CropToFit != nil {
return *m.CropToFit
}
return Default_Transform_CropToFit
}
func (m *Transform) GetCropOffsetX() float32 {
if m != nil && m.CropOffsetX != nil {
return *m.CropOffsetX
}
return Default_Transform_CropOffsetX
}
func (m *Transform) GetCropOffsetY() float32 {
if m != nil && m.CropOffsetY != nil {
return *m.CropOffsetY
}
return Default_Transform_CropOffsetY
}
func (m *Transform) GetRotate() int32 {
if m != nil && m.Rotate != nil {
return *m.Rotate
}
return Default_Transform_Rotate
}
func (m *Transform) GetHorizontalFlip() bool {
if m != nil && m.HorizontalFlip != nil {
return *m.HorizontalFlip
}
return Default_Transform_HorizontalFlip
}
func (m *Transform) GetVerticalFlip() bool {
if m != nil && m.VerticalFlip != nil {
return *m.VerticalFlip
}
return Default_Transform_VerticalFlip
}
func (m *Transform) GetCropLeftX() float32 {
if m != nil && m.CropLeftX != nil {
return *m.CropLeftX
}
return Default_Transform_CropLeftX
}
func (m *Transform) GetCropTopY() float32 {
if m != nil && m.CropTopY != nil {
return *m.CropTopY
}
return Default_Transform_CropTopY
}
func (m *Transform) GetCropRightX() float32 {
if m != nil && m.CropRightX != nil {
return *m.CropRightX
}
return Default_Transform_CropRightX
}
func (m *Transform) GetCropBottomY() float32 {
if m != nil && m.CropBottomY != nil {
return *m.CropBottomY
}
return Default_Transform_CropBottomY
}
func (m *Transform) GetAutolevels() bool {
if m != nil && m.Autolevels != nil {
return *m.Autolevels
}
return Default_Transform_Autolevels
}
func (m *Transform) GetAllowStretch() bool {
if m != nil && m.AllowStretch != nil {
return *m.AllowStretch
}
return Default_Transform_AllowStretch
}
type ImageData struct {
Content []byte `protobuf:"bytes,1,req,name=content" json:"content,omitempty"`
BlobKey *string `protobuf:"bytes,2,opt,name=blob_key" json:"blob_key,omitempty"`
Width *int32 `protobuf:"varint,3,opt,name=width" json:"width,omitempty"`
Height *int32 `protobuf:"varint,4,opt,name=height" json:"height,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ImageData) Reset() { *m = ImageData{} }
func (m *ImageData) String() string { return proto.CompactTextString(m) }
func (*ImageData) ProtoMessage() {}
func (m *ImageData) GetContent() []byte {
if m != nil {
return m.Content
}
return nil
}
func (m *ImageData) GetBlobKey() string {
if m != nil && m.BlobKey != nil {
return *m.BlobKey
}
return ""
}
func (m *ImageData) GetWidth() int32 {
if m != nil && m.Width != nil {
return *m.Width
}
return 0
}
func (m *ImageData) GetHeight() int32 {
if m != nil && m.Height != nil {
return *m.Height
}
return 0
}
type InputSettings struct {
CorrectExifOrientation *InputSettings_ORIENTATION_CORRECTION_TYPE `protobuf:"varint,1,opt,name=correct_exif_orientation,enum=appengine.InputSettings_ORIENTATION_CORRECTION_TYPE,def=0" json:"correct_exif_orientation,omitempty"`
ParseMetadata *bool `protobuf:"varint,2,opt,name=parse_metadata,def=0" json:"parse_metadata,omitempty"`
TransparentSubstitutionRgb *int32 `protobuf:"varint,3,opt,name=transparent_substitution_rgb" json:"transparent_substitution_rgb,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *InputSettings) Reset() { *m = InputSettings{} }
func (m *InputSettings) String() string { return proto.CompactTextString(m) }
func (*InputSettings) ProtoMessage() {}
const Default_InputSettings_CorrectExifOrientation InputSettings_ORIENTATION_CORRECTION_TYPE = InputSettings_UNCHANGED_ORIENTATION
const Default_InputSettings_ParseMetadata bool = false
func (m *InputSettings) GetCorrectExifOrientation() InputSettings_ORIENTATION_CORRECTION_TYPE {
if m != nil && m.CorrectExifOrientation != nil {
return *m.CorrectExifOrientation
}
return Default_InputSettings_CorrectExifOrientation
}
func (m *InputSettings) GetParseMetadata() bool {
if m != nil && m.ParseMetadata != nil {
return *m.ParseMetadata
}
return Default_InputSettings_ParseMetadata
}
func (m *InputSettings) GetTransparentSubstitutionRgb() int32 {
if m != nil && m.TransparentSubstitutionRgb != nil {
return *m.TransparentSubstitutionRgb
}
return 0
}
type OutputSettings struct {
MimeType *OutputSettings_MIME_TYPE `protobuf:"varint,1,opt,name=mime_type,enum=appengine.OutputSettings_MIME_TYPE,def=0" json:"mime_type,omitempty"`
Quality *int32 `protobuf:"varint,2,opt,name=quality" json:"quality,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *OutputSettings) Reset() { *m = OutputSettings{} }
func (m *OutputSettings) String() string { return proto.CompactTextString(m) }
func (*OutputSettings) ProtoMessage() {}
const Default_OutputSettings_MimeType OutputSettings_MIME_TYPE = OutputSettings_PNG
func (m *OutputSettings) GetMimeType() OutputSettings_MIME_TYPE {
if m != nil && m.MimeType != nil {
return *m.MimeType
}
return Default_OutputSettings_MimeType
}
func (m *OutputSettings) GetQuality() int32 {
if m != nil && m.Quality != nil {
return *m.Quality
}
return 0
}
type ImagesTransformRequest struct {
Image *ImageData `protobuf:"bytes,1,req,name=image" json:"image,omitempty"`
Transform []*Transform `protobuf:"bytes,2,rep,name=transform" json:"transform,omitempty"`
Output *OutputSettings `protobuf:"bytes,3,req,name=output" json:"output,omitempty"`
Input *InputSettings `protobuf:"bytes,4,opt,name=input" json:"input,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesTransformRequest) Reset() { *m = ImagesTransformRequest{} }
func (m *ImagesTransformRequest) String() string { return proto.CompactTextString(m) }
func (*ImagesTransformRequest) ProtoMessage() {}
func (m *ImagesTransformRequest) GetImage() *ImageData {
if m != nil {
return m.Image
}
return nil
}
func (m *ImagesTransformRequest) GetTransform() []*Transform {
if m != nil {
return m.Transform
}
return nil
}
func (m *ImagesTransformRequest) GetOutput() *OutputSettings {
if m != nil {
return m.Output
}
return nil
}
func (m *ImagesTransformRequest) GetInput() *InputSettings {
if m != nil {
return m.Input
}
return nil
}
type ImagesTransformResponse struct {
Image *ImageData `protobuf:"bytes,1,req,name=image" json:"image,omitempty"`
SourceMetadata *string `protobuf:"bytes,2,opt,name=source_metadata" json:"source_metadata,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesTransformResponse) Reset() { *m = ImagesTransformResponse{} }
func (m *ImagesTransformResponse) String() string { return proto.CompactTextString(m) }
func (*ImagesTransformResponse) ProtoMessage() {}
func (m *ImagesTransformResponse) GetImage() *ImageData {
if m != nil {
return m.Image
}
return nil
}
func (m *ImagesTransformResponse) GetSourceMetadata() string {
if m != nil && m.SourceMetadata != nil {
return *m.SourceMetadata
}
return ""
}
type CompositeImageOptions struct {
SourceIndex *int32 `protobuf:"varint,1,req,name=source_index" json:"source_index,omitempty"`
XOffset *int32 `protobuf:"varint,2,req,name=x_offset" json:"x_offset,omitempty"`
YOffset *int32 `protobuf:"varint,3,req,name=y_offset" json:"y_offset,omitempty"`
Opacity *float32 `protobuf:"fixed32,4,req,name=opacity" json:"opacity,omitempty"`
Anchor *CompositeImageOptions_ANCHOR `protobuf:"varint,5,req,name=anchor,enum=appengine.CompositeImageOptions_ANCHOR" json:"anchor,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *CompositeImageOptions) Reset() { *m = CompositeImageOptions{} }
func (m *CompositeImageOptions) String() string { return proto.CompactTextString(m) }
func (*CompositeImageOptions) ProtoMessage() {}
func (m *CompositeImageOptions) GetSourceIndex() int32 {
if m != nil && m.SourceIndex != nil {
return *m.SourceIndex
}
return 0
}
func (m *CompositeImageOptions) GetXOffset() int32 {
if m != nil && m.XOffset != nil {
return *m.XOffset
}
return 0
}
func (m *CompositeImageOptions) GetYOffset() int32 {
if m != nil && m.YOffset != nil {
return *m.YOffset
}
return 0
}
func (m *CompositeImageOptions) GetOpacity() float32 {
if m != nil && m.Opacity != nil {
return *m.Opacity
}
return 0
}
func (m *CompositeImageOptions) GetAnchor() CompositeImageOptions_ANCHOR {
if m != nil && m.Anchor != nil {
return *m.Anchor
}
return CompositeImageOptions_TOP_LEFT
}
type ImagesCanvas struct {
Width *int32 `protobuf:"varint,1,req,name=width" json:"width,omitempty"`
Height *int32 `protobuf:"varint,2,req,name=height" json:"height,omitempty"`
Output *OutputSettings `protobuf:"bytes,3,req,name=output" json:"output,omitempty"`
Color *int32 `protobuf:"varint,4,opt,name=color,def=-1" json:"color,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesCanvas) Reset() { *m = ImagesCanvas{} }
func (m *ImagesCanvas) String() string { return proto.CompactTextString(m) }
func (*ImagesCanvas) ProtoMessage() {}
const Default_ImagesCanvas_Color int32 = -1
func (m *ImagesCanvas) GetWidth() int32 {
if m != nil && m.Width != nil {
return *m.Width
}
return 0
}
func (m *ImagesCanvas) GetHeight() int32 {
if m != nil && m.Height != nil {
return *m.Height
}
return 0
}
func (m *ImagesCanvas) GetOutput() *OutputSettings {
if m != nil {
return m.Output
}
return nil
}
func (m *ImagesCanvas) GetColor() int32 {
if m != nil && m.Color != nil {
return *m.Color
}
return Default_ImagesCanvas_Color
}
type ImagesCompositeRequest struct {
Image []*ImageData `protobuf:"bytes,1,rep,name=image" json:"image,omitempty"`
Options []*CompositeImageOptions `protobuf:"bytes,2,rep,name=options" json:"options,omitempty"`
Canvas *ImagesCanvas `protobuf:"bytes,3,req,name=canvas" json:"canvas,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesCompositeRequest) Reset() { *m = ImagesCompositeRequest{} }
func (m *ImagesCompositeRequest) String() string { return proto.CompactTextString(m) }
func (*ImagesCompositeRequest) ProtoMessage() {}
func (m *ImagesCompositeRequest) GetImage() []*ImageData {
if m != nil {
return m.Image
}
return nil
}
func (m *ImagesCompositeRequest) GetOptions() []*CompositeImageOptions {
if m != nil {
return m.Options
}
return nil
}
func (m *ImagesCompositeRequest) GetCanvas() *ImagesCanvas {
if m != nil {
return m.Canvas
}
return nil
}
type ImagesCompositeResponse struct {
Image *ImageData `protobuf:"bytes,1,req,name=image" json:"image,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesCompositeResponse) Reset() { *m = ImagesCompositeResponse{} }
func (m *ImagesCompositeResponse) String() string { return proto.CompactTextString(m) }
func (*ImagesCompositeResponse) ProtoMessage() {}
func (m *ImagesCompositeResponse) GetImage() *ImageData {
if m != nil {
return m.Image
}
return nil
}
type ImagesHistogramRequest struct {
Image *ImageData `protobuf:"bytes,1,req,name=image" json:"image,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesHistogramRequest) Reset() { *m = ImagesHistogramRequest{} }
func (m *ImagesHistogramRequest) String() string { return proto.CompactTextString(m) }
func (*ImagesHistogramRequest) ProtoMessage() {}
func (m *ImagesHistogramRequest) GetImage() *ImageData {
if m != nil {
return m.Image
}
return nil
}
type ImagesHistogram struct {
Red []int32 `protobuf:"varint,1,rep,name=red" json:"red,omitempty"`
Green []int32 `protobuf:"varint,2,rep,name=green" json:"green,omitempty"`
Blue []int32 `protobuf:"varint,3,rep,name=blue" json:"blue,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesHistogram) Reset() { *m = ImagesHistogram{} }
func (m *ImagesHistogram) String() string { return proto.CompactTextString(m) }
func (*ImagesHistogram) ProtoMessage() {}
func (m *ImagesHistogram) GetRed() []int32 {
if m != nil {
return m.Red
}
return nil
}
func (m *ImagesHistogram) GetGreen() []int32 {
if m != nil {
return m.Green
}
return nil
}
func (m *ImagesHistogram) GetBlue() []int32 {
if m != nil {
return m.Blue
}
return nil
}
type ImagesHistogramResponse struct {
Histogram *ImagesHistogram `protobuf:"bytes,1,req,name=histogram" json:"histogram,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesHistogramResponse) Reset() { *m = ImagesHistogramResponse{} }
func (m *ImagesHistogramResponse) String() string { return proto.CompactTextString(m) }
func (*ImagesHistogramResponse) ProtoMessage() {}
func (m *ImagesHistogramResponse) GetHistogram() *ImagesHistogram {
if m != nil {
return m.Histogram
}
return nil
}
type ImagesGetUrlBaseRequest struct {
BlobKey *string `protobuf:"bytes,1,req,name=blob_key" json:"blob_key,omitempty"`
CreateSecureUrl *bool `protobuf:"varint,2,opt,name=create_secure_url,def=0" json:"create_secure_url,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesGetUrlBaseRequest) Reset() { *m = ImagesGetUrlBaseRequest{} }
func (m *ImagesGetUrlBaseRequest) String() string { return proto.CompactTextString(m) }
func (*ImagesGetUrlBaseRequest) ProtoMessage() {}
const Default_ImagesGetUrlBaseRequest_CreateSecureUrl bool = false
func (m *ImagesGetUrlBaseRequest) GetBlobKey() string {
if m != nil && m.BlobKey != nil {
return *m.BlobKey
}
return ""
}
func (m *ImagesGetUrlBaseRequest) GetCreateSecureUrl() bool {
if m != nil && m.CreateSecureUrl != nil {
return *m.CreateSecureUrl
}
return Default_ImagesGetUrlBaseRequest_CreateSecureUrl
}
type ImagesGetUrlBaseResponse struct {
Url *string `protobuf:"bytes,1,req,name=url" json:"url,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesGetUrlBaseResponse) Reset() { *m = ImagesGetUrlBaseResponse{} }
func (m *ImagesGetUrlBaseResponse) String() string { return proto.CompactTextString(m) }
func (*ImagesGetUrlBaseResponse) ProtoMessage() {}
func (m *ImagesGetUrlBaseResponse) GetUrl() string {
if m != nil && m.Url != nil {
return *m.Url
}
return ""
}
type ImagesDeleteUrlBaseRequest struct {
BlobKey *string `protobuf:"bytes,1,req,name=blob_key" json:"blob_key,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesDeleteUrlBaseRequest) Reset() { *m = ImagesDeleteUrlBaseRequest{} }
func (m *ImagesDeleteUrlBaseRequest) String() string { return proto.CompactTextString(m) }
func (*ImagesDeleteUrlBaseRequest) ProtoMessage() {}
func (m *ImagesDeleteUrlBaseRequest) GetBlobKey() string {
if m != nil && m.BlobKey != nil {
return *m.BlobKey
}
return ""
}
type ImagesDeleteUrlBaseResponse struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *ImagesDeleteUrlBaseResponse) Reset() { *m = ImagesDeleteUrlBaseResponse{} }
func (m *ImagesDeleteUrlBaseResponse) String() string { return proto.CompactTextString(m) }
func (*ImagesDeleteUrlBaseResponse) ProtoMessage() {}
func init() {
proto.RegisterEnum("appengine.ImagesServiceError_ErrorCode", ImagesServiceError_ErrorCode_name, ImagesServiceError_ErrorCode_value)
proto.RegisterEnum("appengine.ImagesServiceTransform_Type", ImagesServiceTransform_Type_name, ImagesServiceTransform_Type_value)
proto.RegisterEnum("appengine.InputSettings_ORIENTATION_CORRECTION_TYPE", InputSettings_ORIENTATION_CORRECTION_TYPE_name, InputSettings_ORIENTATION_CORRECTION_TYPE_value)
proto.RegisterEnum("appengine.OutputSettings_MIME_TYPE", OutputSettings_MIME_TYPE_name, OutputSettings_MIME_TYPE_value)
proto.RegisterEnum("appengine.CompositeImageOptions_ANCHOR", CompositeImageOptions_ANCHOR_name, CompositeImageOptions_ANCHOR_value)
}

View File

@@ -1,162 +0,0 @@
syntax = "proto2";
option go_package = "image";
package appengine;
message ImagesServiceError {
enum ErrorCode {
UNSPECIFIED_ERROR = 1;
BAD_TRANSFORM_DATA = 2;
NOT_IMAGE = 3;
BAD_IMAGE_DATA = 4;
IMAGE_TOO_LARGE = 5;
INVALID_BLOB_KEY = 6;
ACCESS_DENIED = 7;
OBJECT_NOT_FOUND = 8;
}
}
message ImagesServiceTransform {
enum Type {
RESIZE = 1;
ROTATE = 2;
HORIZONTAL_FLIP = 3;
VERTICAL_FLIP = 4;
CROP = 5;
IM_FEELING_LUCKY = 6;
}
}
message Transform {
optional int32 width = 1;
optional int32 height = 2;
optional bool crop_to_fit = 11 [default = false];
optional float crop_offset_x = 12 [default = 0.5];
optional float crop_offset_y = 13 [default = 0.5];
optional int32 rotate = 3 [default = 0];
optional bool horizontal_flip = 4 [default = false];
optional bool vertical_flip = 5 [default = false];
optional float crop_left_x = 6 [default = 0.0];
optional float crop_top_y = 7 [default = 0.0];
optional float crop_right_x = 8 [default = 1.0];
optional float crop_bottom_y = 9 [default = 1.0];
optional bool autolevels = 10 [default = false];
optional bool allow_stretch = 14 [default = false];
}
message ImageData {
required bytes content = 1 [ctype=CORD];
optional string blob_key = 2;
optional int32 width = 3;
optional int32 height = 4;
}
message InputSettings {
enum ORIENTATION_CORRECTION_TYPE {
UNCHANGED_ORIENTATION = 0;
CORRECT_ORIENTATION = 1;
}
optional ORIENTATION_CORRECTION_TYPE correct_exif_orientation = 1
[default=UNCHANGED_ORIENTATION];
optional bool parse_metadata = 2 [default=false];
optional int32 transparent_substitution_rgb = 3;
}
message OutputSettings {
enum MIME_TYPE {
PNG = 0;
JPEG = 1;
WEBP = 2;
}
optional MIME_TYPE mime_type = 1 [default=PNG];
optional int32 quality = 2;
}
message ImagesTransformRequest {
required ImageData image = 1;
repeated Transform transform = 2;
required OutputSettings output = 3;
optional InputSettings input = 4;
}
message ImagesTransformResponse {
required ImageData image = 1;
optional string source_metadata = 2;
}
message CompositeImageOptions {
required int32 source_index = 1;
required int32 x_offset = 2;
required int32 y_offset = 3;
required float opacity = 4;
enum ANCHOR {
TOP_LEFT = 0;
TOP = 1;
TOP_RIGHT = 2;
LEFT = 3;
CENTER = 4;
RIGHT = 5;
BOTTOM_LEFT = 6;
BOTTOM = 7;
BOTTOM_RIGHT = 8;
}
required ANCHOR anchor = 5;
}
message ImagesCanvas {
required int32 width = 1;
required int32 height = 2;
required OutputSettings output = 3;
optional int32 color = 4 [default=-1];
}
message ImagesCompositeRequest {
repeated ImageData image = 1;
repeated CompositeImageOptions options = 2;
required ImagesCanvas canvas = 3;
}
message ImagesCompositeResponse {
required ImageData image = 1;
}
message ImagesHistogramRequest {
required ImageData image = 1;
}
message ImagesHistogram {
repeated int32 red = 1;
repeated int32 green = 2;
repeated int32 blue = 3;
}
message ImagesHistogramResponse {
required ImagesHistogram histogram = 1;
}
message ImagesGetUrlBaseRequest {
required string blob_key = 1;
optional bool create_secure_url = 2 [default = false];
}
message ImagesGetUrlBaseResponse {
required string url = 1;
}
message ImagesDeleteUrlBaseRequest {
required string blob_key = 1;
}
message ImagesDeleteUrlBaseResponse {
}

View File

@@ -1,165 +0,0 @@
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// Package internal provides support for package appengine.
//
// Programs should not use this package directly. Its API is not stable.
// Use packages appengine and appengine/* instead.
package internal
import (
"fmt"
"io"
"log"
"net/http"
"net/url"
"time"
"github.com/golang/protobuf/proto"
remotepb "google.golang.org/appengine/internal/remote_api"
)
type CallOptions struct {
Timeout time.Duration // if non-zero, overrides RPC default
}
// errorCodeMaps is a map of service name to the error code map for the service.
var errorCodeMaps = make(map[string]map[int32]string)
// RegisterErrorCodeMap is called from API implementations to register their
// error code map. This should only be called from init functions.
func RegisterErrorCodeMap(service string, m map[int32]string) {
errorCodeMaps[service] = m
}
type timeoutCodeKey struct {
service string
code int32
}
// timeoutCodes is the set of service+code pairs that represent timeouts.
var timeoutCodes = make(map[timeoutCodeKey]bool)
func RegisterTimeoutErrorCode(service string, code int32) {
timeoutCodes[timeoutCodeKey{service, code}] = true
}
// APIError is the type returned by appengine.Context's Call method
// when an API call fails in an API-specific way. This may be, for instance,
// a taskqueue API call failing with TaskQueueServiceError::UNKNOWN_QUEUE.
type APIError struct {
Service string
Detail string
Code int32 // API-specific error code
}
func (e *APIError) Error() string {
if e.Code == 0 {
if e.Detail == "" {
return "APIError <empty>"
}
return e.Detail
}
s := fmt.Sprintf("API error %d", e.Code)
if m, ok := errorCodeMaps[e.Service]; ok {
s += " (" + e.Service + ": " + m[e.Code] + ")"
} else {
// Shouldn't happen, but provide a bit more detail if it does.
s = e.Service + " " + s
}
if e.Detail != "" {
s += ": " + e.Detail
}
return s
}
func (e *APIError) IsTimeout() bool {
return timeoutCodes[timeoutCodeKey{e.Service, e.Code}]
}
// CallError is the type returned by appengine.Context's Call method when an
// API call fails in a generic way, such as RpcError::CAPABILITY_DISABLED.
type CallError struct {
Detail string
Code int32
// TODO: Remove this if we get a distinguishable error code.
Timeout bool
}
func (e *CallError) Error() string {
var msg string
switch remotepb.RpcError_ErrorCode(e.Code) {
case remotepb.RpcError_UNKNOWN:
return e.Detail
case remotepb.RpcError_OVER_QUOTA:
msg = "Over quota"
case remotepb.RpcError_CAPABILITY_DISABLED:
msg = "Capability disabled"
case remotepb.RpcError_CANCELLED:
msg = "Canceled"
default:
msg = fmt.Sprintf("Call error %d", e.Code)
}
s := msg + ": " + e.Detail
if e.Timeout {
s += " (timeout)"
}
return s
}
func (e *CallError) IsTimeout() bool {
return e.Timeout
}
// The comment below must not be changed.
// It is used by go-app-builder to recognise that this package has
// the internal.Main function to use in the synthetic main.
// The gophers party all night; the rabbits provide the beats.
// Main is designed so that the complete generated main package is:
//
// package main
//
// import (
// "google.golang.org/appengine/internal"
//
// _ "myapp/package0"
// _ "myapp/package1"
// )
//
// func main() {
// internal.Main()
// }
//
// The "myapp/packageX" packages are expected to register HTTP handlers
// in their init functions.
func Main() {
installHealthChecker(http.DefaultServeMux)
if err := http.ListenAndServe(":8080", http.HandlerFunc(handleHTTP)); err != nil {
log.Fatalf("http.ListenAndServe: %v", err)
}
}
func installHealthChecker(mux *http.ServeMux) {
// If no health check handler has been installed by this point, add a trivial one.
const healthPath = "/_ah/health"
hreq := &http.Request{
Method: "GET",
URL: &url.URL{
Path: healthPath,
},
}
if _, pat := mux.Handler(hreq); pat != healthPath {
mux.HandleFunc(healthPath, func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "ok")
})
}
}
// NamespaceMods is a map from API service to a function that will mutate an RPC request to attach a namespace.
// The function should be prepared to be called on the same message more than once; it should only modify the
// RPC request the first time.
var NamespaceMods = make(map[string]func(m proto.Message, namespace string))

View File

@@ -1,54 +0,0 @@
package internal
import (
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)
func TestInstallingHealthChecker(t *testing.T) {
try := func(desc string, mux *http.ServeMux, wantCode int, wantBody string) {
installHealthChecker(mux)
srv := httptest.NewServer(mux)
defer srv.Close()
resp, err := http.Get(srv.URL + "/_ah/health")
if err != nil {
t.Errorf("%s: http.Get: %v", desc, err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("%s: reading body: %v", desc, err)
return
}
if resp.StatusCode != wantCode {
t.Errorf("%s: got HTTP %d, want %d", desc, resp.StatusCode, wantCode)
return
}
if wantBody != "" && string(body) != wantBody {
t.Errorf("%s: got HTTP body %q, want %q", desc, body, wantBody)
return
}
}
// If there's no handlers, or only a root handler, a health checker should be installed.
try("empty mux", http.NewServeMux(), 200, "ok")
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "root handler")
})
try("mux with root handler", mux, 200, "ok")
// If there's a custom health check handler, one should not be installed.
mux = http.NewServeMux()
mux.HandleFunc("/_ah/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(418)
io.WriteString(w, "I'm short and stout!")
})
try("mux with custom health checker", mux, 418, "I'm short and stout!")
}

View File

@@ -1,898 +0,0 @@
// Code generated by protoc-gen-go.
// source: google.golang.org/appengine/internal/log/log_service.proto
// DO NOT EDIT!
/*
Package log is a generated protocol buffer package.
It is generated from these files:
google.golang.org/appengine/internal/log/log_service.proto
It has these top-level messages:
LogServiceError
UserAppLogLine
UserAppLogGroup
FlushRequest
SetStatusRequest
LogOffset
LogLine
RequestLog
LogModuleVersion
LogReadRequest
LogReadResponse
LogUsageRecord
LogUsageRequest
LogUsageResponse
*/
package log
import proto "github.com/golang/protobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
type LogServiceError_ErrorCode int32
const (
LogServiceError_OK LogServiceError_ErrorCode = 0
LogServiceError_INVALID_REQUEST LogServiceError_ErrorCode = 1
LogServiceError_STORAGE_ERROR LogServiceError_ErrorCode = 2
)
var LogServiceError_ErrorCode_name = map[int32]string{
0: "OK",
1: "INVALID_REQUEST",
2: "STORAGE_ERROR",
}
var LogServiceError_ErrorCode_value = map[string]int32{
"OK": 0,
"INVALID_REQUEST": 1,
"STORAGE_ERROR": 2,
}
func (x LogServiceError_ErrorCode) Enum() *LogServiceError_ErrorCode {
p := new(LogServiceError_ErrorCode)
*p = x
return p
}
func (x LogServiceError_ErrorCode) String() string {
return proto.EnumName(LogServiceError_ErrorCode_name, int32(x))
}
func (x *LogServiceError_ErrorCode) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(LogServiceError_ErrorCode_value, data, "LogServiceError_ErrorCode")
if err != nil {
return err
}
*x = LogServiceError_ErrorCode(value)
return nil
}
type LogServiceError struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *LogServiceError) Reset() { *m = LogServiceError{} }
func (m *LogServiceError) String() string { return proto.CompactTextString(m) }
func (*LogServiceError) ProtoMessage() {}
type UserAppLogLine struct {
TimestampUsec *int64 `protobuf:"varint,1,req,name=timestamp_usec" json:"timestamp_usec,omitempty"`
Level *int64 `protobuf:"varint,2,req,name=level" json:"level,omitempty"`
Message *string `protobuf:"bytes,3,req,name=message" json:"message,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *UserAppLogLine) Reset() { *m = UserAppLogLine{} }
func (m *UserAppLogLine) String() string { return proto.CompactTextString(m) }
func (*UserAppLogLine) ProtoMessage() {}
func (m *UserAppLogLine) GetTimestampUsec() int64 {
if m != nil && m.TimestampUsec != nil {
return *m.TimestampUsec
}
return 0
}
func (m *UserAppLogLine) GetLevel() int64 {
if m != nil && m.Level != nil {
return *m.Level
}
return 0
}
func (m *UserAppLogLine) GetMessage() string {
if m != nil && m.Message != nil {
return *m.Message
}
return ""
}
type UserAppLogGroup struct {
LogLine []*UserAppLogLine `protobuf:"bytes,2,rep,name=log_line" json:"log_line,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *UserAppLogGroup) Reset() { *m = UserAppLogGroup{} }
func (m *UserAppLogGroup) String() string { return proto.CompactTextString(m) }
func (*UserAppLogGroup) ProtoMessage() {}
func (m *UserAppLogGroup) GetLogLine() []*UserAppLogLine {
if m != nil {
return m.LogLine
}
return nil
}
type FlushRequest struct {
Logs []byte `protobuf:"bytes,1,opt,name=logs" json:"logs,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *FlushRequest) Reset() { *m = FlushRequest{} }
func (m *FlushRequest) String() string { return proto.CompactTextString(m) }
func (*FlushRequest) ProtoMessage() {}
func (m *FlushRequest) GetLogs() []byte {
if m != nil {
return m.Logs
}
return nil
}
type SetStatusRequest struct {
Status *string `protobuf:"bytes,1,req,name=status" json:"status,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *SetStatusRequest) Reset() { *m = SetStatusRequest{} }
func (m *SetStatusRequest) String() string { return proto.CompactTextString(m) }
func (*SetStatusRequest) ProtoMessage() {}
func (m *SetStatusRequest) GetStatus() string {
if m != nil && m.Status != nil {
return *m.Status
}
return ""
}
type LogOffset struct {
RequestId []byte `protobuf:"bytes,1,opt,name=request_id" json:"request_id,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogOffset) Reset() { *m = LogOffset{} }
func (m *LogOffset) String() string { return proto.CompactTextString(m) }
func (*LogOffset) ProtoMessage() {}
func (m *LogOffset) GetRequestId() []byte {
if m != nil {
return m.RequestId
}
return nil
}
type LogLine struct {
Time *int64 `protobuf:"varint,1,req,name=time" json:"time,omitempty"`
Level *int32 `protobuf:"varint,2,req,name=level" json:"level,omitempty"`
LogMessage *string `protobuf:"bytes,3,req,name=log_message" json:"log_message,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogLine) Reset() { *m = LogLine{} }
func (m *LogLine) String() string { return proto.CompactTextString(m) }
func (*LogLine) ProtoMessage() {}
func (m *LogLine) GetTime() int64 {
if m != nil && m.Time != nil {
return *m.Time
}
return 0
}
func (m *LogLine) GetLevel() int32 {
if m != nil && m.Level != nil {
return *m.Level
}
return 0
}
func (m *LogLine) GetLogMessage() string {
if m != nil && m.LogMessage != nil {
return *m.LogMessage
}
return ""
}
type RequestLog struct {
AppId *string `protobuf:"bytes,1,req,name=app_id" json:"app_id,omitempty"`
ModuleId *string `protobuf:"bytes,37,opt,name=module_id,def=default" json:"module_id,omitempty"`
VersionId *string `protobuf:"bytes,2,req,name=version_id" json:"version_id,omitempty"`
RequestId []byte `protobuf:"bytes,3,req,name=request_id" json:"request_id,omitempty"`
Offset *LogOffset `protobuf:"bytes,35,opt,name=offset" json:"offset,omitempty"`
Ip *string `protobuf:"bytes,4,req,name=ip" json:"ip,omitempty"`
Nickname *string `protobuf:"bytes,5,opt,name=nickname" json:"nickname,omitempty"`
StartTime *int64 `protobuf:"varint,6,req,name=start_time" json:"start_time,omitempty"`
EndTime *int64 `protobuf:"varint,7,req,name=end_time" json:"end_time,omitempty"`
Latency *int64 `protobuf:"varint,8,req,name=latency" json:"latency,omitempty"`
Mcycles *int64 `protobuf:"varint,9,req,name=mcycles" json:"mcycles,omitempty"`
Method *string `protobuf:"bytes,10,req,name=method" json:"method,omitempty"`
Resource *string `protobuf:"bytes,11,req,name=resource" json:"resource,omitempty"`
HttpVersion *string `protobuf:"bytes,12,req,name=http_version" json:"http_version,omitempty"`
Status *int32 `protobuf:"varint,13,req,name=status" json:"status,omitempty"`
ResponseSize *int64 `protobuf:"varint,14,req,name=response_size" json:"response_size,omitempty"`
Referrer *string `protobuf:"bytes,15,opt,name=referrer" json:"referrer,omitempty"`
UserAgent *string `protobuf:"bytes,16,opt,name=user_agent" json:"user_agent,omitempty"`
UrlMapEntry *string `protobuf:"bytes,17,req,name=url_map_entry" json:"url_map_entry,omitempty"`
Combined *string `protobuf:"bytes,18,req,name=combined" json:"combined,omitempty"`
ApiMcycles *int64 `protobuf:"varint,19,opt,name=api_mcycles" json:"api_mcycles,omitempty"`
Host *string `protobuf:"bytes,20,opt,name=host" json:"host,omitempty"`
Cost *float64 `protobuf:"fixed64,21,opt,name=cost" json:"cost,omitempty"`
TaskQueueName *string `protobuf:"bytes,22,opt,name=task_queue_name" json:"task_queue_name,omitempty"`
TaskName *string `protobuf:"bytes,23,opt,name=task_name" json:"task_name,omitempty"`
WasLoadingRequest *bool `protobuf:"varint,24,opt,name=was_loading_request" json:"was_loading_request,omitempty"`
PendingTime *int64 `protobuf:"varint,25,opt,name=pending_time" json:"pending_time,omitempty"`
ReplicaIndex *int32 `protobuf:"varint,26,opt,name=replica_index,def=-1" json:"replica_index,omitempty"`
Finished *bool `protobuf:"varint,27,opt,name=finished,def=1" json:"finished,omitempty"`
CloneKey []byte `protobuf:"bytes,28,opt,name=clone_key" json:"clone_key,omitempty"`
Line []*LogLine `protobuf:"bytes,29,rep,name=line" json:"line,omitempty"`
LinesIncomplete *bool `protobuf:"varint,36,opt,name=lines_incomplete" json:"lines_incomplete,omitempty"`
AppEngineRelease []byte `protobuf:"bytes,38,opt,name=app_engine_release" json:"app_engine_release,omitempty"`
ExitReason *int32 `protobuf:"varint,30,opt,name=exit_reason" json:"exit_reason,omitempty"`
WasThrottledForTime *bool `protobuf:"varint,31,opt,name=was_throttled_for_time" json:"was_throttled_for_time,omitempty"`
WasThrottledForRequests *bool `protobuf:"varint,32,opt,name=was_throttled_for_requests" json:"was_throttled_for_requests,omitempty"`
ThrottledTime *int64 `protobuf:"varint,33,opt,name=throttled_time" json:"throttled_time,omitempty"`
ServerName []byte `protobuf:"bytes,34,opt,name=server_name" json:"server_name,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *RequestLog) Reset() { *m = RequestLog{} }
func (m *RequestLog) String() string { return proto.CompactTextString(m) }
func (*RequestLog) ProtoMessage() {}
const Default_RequestLog_ModuleId string = "default"
const Default_RequestLog_ReplicaIndex int32 = -1
const Default_RequestLog_Finished bool = true
func (m *RequestLog) GetAppId() string {
if m != nil && m.AppId != nil {
return *m.AppId
}
return ""
}
func (m *RequestLog) GetModuleId() string {
if m != nil && m.ModuleId != nil {
return *m.ModuleId
}
return Default_RequestLog_ModuleId
}
func (m *RequestLog) GetVersionId() string {
if m != nil && m.VersionId != nil {
return *m.VersionId
}
return ""
}
func (m *RequestLog) GetRequestId() []byte {
if m != nil {
return m.RequestId
}
return nil
}
func (m *RequestLog) GetOffset() *LogOffset {
if m != nil {
return m.Offset
}
return nil
}
func (m *RequestLog) GetIp() string {
if m != nil && m.Ip != nil {
return *m.Ip
}
return ""
}
func (m *RequestLog) GetNickname() string {
if m != nil && m.Nickname != nil {
return *m.Nickname
}
return ""
}
func (m *RequestLog) GetStartTime() int64 {
if m != nil && m.StartTime != nil {
return *m.StartTime
}
return 0
}
func (m *RequestLog) GetEndTime() int64 {
if m != nil && m.EndTime != nil {
return *m.EndTime
}
return 0
}
func (m *RequestLog) GetLatency() int64 {
if m != nil && m.Latency != nil {
return *m.Latency
}
return 0
}
func (m *RequestLog) GetMcycles() int64 {
if m != nil && m.Mcycles != nil {
return *m.Mcycles
}
return 0
}
func (m *RequestLog) GetMethod() string {
if m != nil && m.Method != nil {
return *m.Method
}
return ""
}
func (m *RequestLog) GetResource() string {
if m != nil && m.Resource != nil {
return *m.Resource
}
return ""
}
func (m *RequestLog) GetHttpVersion() string {
if m != nil && m.HttpVersion != nil {
return *m.HttpVersion
}
return ""
}
func (m *RequestLog) GetStatus() int32 {
if m != nil && m.Status != nil {
return *m.Status
}
return 0
}
func (m *RequestLog) GetResponseSize() int64 {
if m != nil && m.ResponseSize != nil {
return *m.ResponseSize
}
return 0
}
func (m *RequestLog) GetReferrer() string {
if m != nil && m.Referrer != nil {
return *m.Referrer
}
return ""
}
func (m *RequestLog) GetUserAgent() string {
if m != nil && m.UserAgent != nil {
return *m.UserAgent
}
return ""
}
func (m *RequestLog) GetUrlMapEntry() string {
if m != nil && m.UrlMapEntry != nil {
return *m.UrlMapEntry
}
return ""
}
func (m *RequestLog) GetCombined() string {
if m != nil && m.Combined != nil {
return *m.Combined
}
return ""
}
func (m *RequestLog) GetApiMcycles() int64 {
if m != nil && m.ApiMcycles != nil {
return *m.ApiMcycles
}
return 0
}
func (m *RequestLog) GetHost() string {
if m != nil && m.Host != nil {
return *m.Host
}
return ""
}
func (m *RequestLog) GetCost() float64 {
if m != nil && m.Cost != nil {
return *m.Cost
}
return 0
}
func (m *RequestLog) GetTaskQueueName() string {
if m != nil && m.TaskQueueName != nil {
return *m.TaskQueueName
}
return ""
}
func (m *RequestLog) GetTaskName() string {
if m != nil && m.TaskName != nil {
return *m.TaskName
}
return ""
}
func (m *RequestLog) GetWasLoadingRequest() bool {
if m != nil && m.WasLoadingRequest != nil {
return *m.WasLoadingRequest
}
return false
}
func (m *RequestLog) GetPendingTime() int64 {
if m != nil && m.PendingTime != nil {
return *m.PendingTime
}
return 0
}
func (m *RequestLog) GetReplicaIndex() int32 {
if m != nil && m.ReplicaIndex != nil {
return *m.ReplicaIndex
}
return Default_RequestLog_ReplicaIndex
}
func (m *RequestLog) GetFinished() bool {
if m != nil && m.Finished != nil {
return *m.Finished
}
return Default_RequestLog_Finished
}
func (m *RequestLog) GetCloneKey() []byte {
if m != nil {
return m.CloneKey
}
return nil
}
func (m *RequestLog) GetLine() []*LogLine {
if m != nil {
return m.Line
}
return nil
}
func (m *RequestLog) GetLinesIncomplete() bool {
if m != nil && m.LinesIncomplete != nil {
return *m.LinesIncomplete
}
return false
}
func (m *RequestLog) GetAppEngineRelease() []byte {
if m != nil {
return m.AppEngineRelease
}
return nil
}
func (m *RequestLog) GetExitReason() int32 {
if m != nil && m.ExitReason != nil {
return *m.ExitReason
}
return 0
}
func (m *RequestLog) GetWasThrottledForTime() bool {
if m != nil && m.WasThrottledForTime != nil {
return *m.WasThrottledForTime
}
return false
}
func (m *RequestLog) GetWasThrottledForRequests() bool {
if m != nil && m.WasThrottledForRequests != nil {
return *m.WasThrottledForRequests
}
return false
}
func (m *RequestLog) GetThrottledTime() int64 {
if m != nil && m.ThrottledTime != nil {
return *m.ThrottledTime
}
return 0
}
func (m *RequestLog) GetServerName() []byte {
if m != nil {
return m.ServerName
}
return nil
}
type LogModuleVersion struct {
ModuleId *string `protobuf:"bytes,1,opt,name=module_id,def=default" json:"module_id,omitempty"`
VersionId *string `protobuf:"bytes,2,opt,name=version_id" json:"version_id,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogModuleVersion) Reset() { *m = LogModuleVersion{} }
func (m *LogModuleVersion) String() string { return proto.CompactTextString(m) }
func (*LogModuleVersion) ProtoMessage() {}
const Default_LogModuleVersion_ModuleId string = "default"
func (m *LogModuleVersion) GetModuleId() string {
if m != nil && m.ModuleId != nil {
return *m.ModuleId
}
return Default_LogModuleVersion_ModuleId
}
func (m *LogModuleVersion) GetVersionId() string {
if m != nil && m.VersionId != nil {
return *m.VersionId
}
return ""
}
type LogReadRequest struct {
AppId *string `protobuf:"bytes,1,req,name=app_id" json:"app_id,omitempty"`
VersionId []string `protobuf:"bytes,2,rep,name=version_id" json:"version_id,omitempty"`
ModuleVersion []*LogModuleVersion `protobuf:"bytes,19,rep,name=module_version" json:"module_version,omitempty"`
StartTime *int64 `protobuf:"varint,3,opt,name=start_time" json:"start_time,omitempty"`
EndTime *int64 `protobuf:"varint,4,opt,name=end_time" json:"end_time,omitempty"`
Offset *LogOffset `protobuf:"bytes,5,opt,name=offset" json:"offset,omitempty"`
RequestId [][]byte `protobuf:"bytes,6,rep,name=request_id" json:"request_id,omitempty"`
MinimumLogLevel *int32 `protobuf:"varint,7,opt,name=minimum_log_level" json:"minimum_log_level,omitempty"`
IncludeIncomplete *bool `protobuf:"varint,8,opt,name=include_incomplete" json:"include_incomplete,omitempty"`
Count *int64 `protobuf:"varint,9,opt,name=count" json:"count,omitempty"`
CombinedLogRegex *string `protobuf:"bytes,14,opt,name=combined_log_regex" json:"combined_log_regex,omitempty"`
HostRegex *string `protobuf:"bytes,15,opt,name=host_regex" json:"host_regex,omitempty"`
ReplicaIndex *int32 `protobuf:"varint,16,opt,name=replica_index" json:"replica_index,omitempty"`
IncludeAppLogs *bool `protobuf:"varint,10,opt,name=include_app_logs" json:"include_app_logs,omitempty"`
AppLogsPerRequest *int32 `protobuf:"varint,17,opt,name=app_logs_per_request" json:"app_logs_per_request,omitempty"`
IncludeHost *bool `protobuf:"varint,11,opt,name=include_host" json:"include_host,omitempty"`
IncludeAll *bool `protobuf:"varint,12,opt,name=include_all" json:"include_all,omitempty"`
CacheIterator *bool `protobuf:"varint,13,opt,name=cache_iterator" json:"cache_iterator,omitempty"`
NumShards *int32 `protobuf:"varint,18,opt,name=num_shards" json:"num_shards,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogReadRequest) Reset() { *m = LogReadRequest{} }
func (m *LogReadRequest) String() string { return proto.CompactTextString(m) }
func (*LogReadRequest) ProtoMessage() {}
func (m *LogReadRequest) GetAppId() string {
if m != nil && m.AppId != nil {
return *m.AppId
}
return ""
}
func (m *LogReadRequest) GetVersionId() []string {
if m != nil {
return m.VersionId
}
return nil
}
func (m *LogReadRequest) GetModuleVersion() []*LogModuleVersion {
if m != nil {
return m.ModuleVersion
}
return nil
}
func (m *LogReadRequest) GetStartTime() int64 {
if m != nil && m.StartTime != nil {
return *m.StartTime
}
return 0
}
func (m *LogReadRequest) GetEndTime() int64 {
if m != nil && m.EndTime != nil {
return *m.EndTime
}
return 0
}
func (m *LogReadRequest) GetOffset() *LogOffset {
if m != nil {
return m.Offset
}
return nil
}
func (m *LogReadRequest) GetRequestId() [][]byte {
if m != nil {
return m.RequestId
}
return nil
}
func (m *LogReadRequest) GetMinimumLogLevel() int32 {
if m != nil && m.MinimumLogLevel != nil {
return *m.MinimumLogLevel
}
return 0
}
func (m *LogReadRequest) GetIncludeIncomplete() bool {
if m != nil && m.IncludeIncomplete != nil {
return *m.IncludeIncomplete
}
return false
}
func (m *LogReadRequest) GetCount() int64 {
if m != nil && m.Count != nil {
return *m.Count
}
return 0
}
func (m *LogReadRequest) GetCombinedLogRegex() string {
if m != nil && m.CombinedLogRegex != nil {
return *m.CombinedLogRegex
}
return ""
}
func (m *LogReadRequest) GetHostRegex() string {
if m != nil && m.HostRegex != nil {
return *m.HostRegex
}
return ""
}
func (m *LogReadRequest) GetReplicaIndex() int32 {
if m != nil && m.ReplicaIndex != nil {
return *m.ReplicaIndex
}
return 0
}
func (m *LogReadRequest) GetIncludeAppLogs() bool {
if m != nil && m.IncludeAppLogs != nil {
return *m.IncludeAppLogs
}
return false
}
func (m *LogReadRequest) GetAppLogsPerRequest() int32 {
if m != nil && m.AppLogsPerRequest != nil {
return *m.AppLogsPerRequest
}
return 0
}
func (m *LogReadRequest) GetIncludeHost() bool {
if m != nil && m.IncludeHost != nil {
return *m.IncludeHost
}
return false
}
func (m *LogReadRequest) GetIncludeAll() bool {
if m != nil && m.IncludeAll != nil {
return *m.IncludeAll
}
return false
}
func (m *LogReadRequest) GetCacheIterator() bool {
if m != nil && m.CacheIterator != nil {
return *m.CacheIterator
}
return false
}
func (m *LogReadRequest) GetNumShards() int32 {
if m != nil && m.NumShards != nil {
return *m.NumShards
}
return 0
}
type LogReadResponse struct {
Log []*RequestLog `protobuf:"bytes,1,rep,name=log" json:"log,omitempty"`
Offset *LogOffset `protobuf:"bytes,2,opt,name=offset" json:"offset,omitempty"`
LastEndTime *int64 `protobuf:"varint,3,opt,name=last_end_time" json:"last_end_time,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogReadResponse) Reset() { *m = LogReadResponse{} }
func (m *LogReadResponse) String() string { return proto.CompactTextString(m) }
func (*LogReadResponse) ProtoMessage() {}
func (m *LogReadResponse) GetLog() []*RequestLog {
if m != nil {
return m.Log
}
return nil
}
func (m *LogReadResponse) GetOffset() *LogOffset {
if m != nil {
return m.Offset
}
return nil
}
func (m *LogReadResponse) GetLastEndTime() int64 {
if m != nil && m.LastEndTime != nil {
return *m.LastEndTime
}
return 0
}
type LogUsageRecord struct {
VersionId *string `protobuf:"bytes,1,opt,name=version_id" json:"version_id,omitempty"`
StartTime *int32 `protobuf:"varint,2,opt,name=start_time" json:"start_time,omitempty"`
EndTime *int32 `protobuf:"varint,3,opt,name=end_time" json:"end_time,omitempty"`
Count *int64 `protobuf:"varint,4,opt,name=count" json:"count,omitempty"`
TotalSize *int64 `protobuf:"varint,5,opt,name=total_size" json:"total_size,omitempty"`
Records *int32 `protobuf:"varint,6,opt,name=records" json:"records,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogUsageRecord) Reset() { *m = LogUsageRecord{} }
func (m *LogUsageRecord) String() string { return proto.CompactTextString(m) }
func (*LogUsageRecord) ProtoMessage() {}
func (m *LogUsageRecord) GetVersionId() string {
if m != nil && m.VersionId != nil {
return *m.VersionId
}
return ""
}
func (m *LogUsageRecord) GetStartTime() int32 {
if m != nil && m.StartTime != nil {
return *m.StartTime
}
return 0
}
func (m *LogUsageRecord) GetEndTime() int32 {
if m != nil && m.EndTime != nil {
return *m.EndTime
}
return 0
}
func (m *LogUsageRecord) GetCount() int64 {
if m != nil && m.Count != nil {
return *m.Count
}
return 0
}
func (m *LogUsageRecord) GetTotalSize() int64 {
if m != nil && m.TotalSize != nil {
return *m.TotalSize
}
return 0
}
func (m *LogUsageRecord) GetRecords() int32 {
if m != nil && m.Records != nil {
return *m.Records
}
return 0
}
type LogUsageRequest struct {
AppId *string `protobuf:"bytes,1,req,name=app_id" json:"app_id,omitempty"`
VersionId []string `protobuf:"bytes,2,rep,name=version_id" json:"version_id,omitempty"`
StartTime *int32 `protobuf:"varint,3,opt,name=start_time" json:"start_time,omitempty"`
EndTime *int32 `protobuf:"varint,4,opt,name=end_time" json:"end_time,omitempty"`
ResolutionHours *uint32 `protobuf:"varint,5,opt,name=resolution_hours,def=1" json:"resolution_hours,omitempty"`
CombineVersions *bool `protobuf:"varint,6,opt,name=combine_versions" json:"combine_versions,omitempty"`
UsageVersion *int32 `protobuf:"varint,7,opt,name=usage_version" json:"usage_version,omitempty"`
VersionsOnly *bool `protobuf:"varint,8,opt,name=versions_only" json:"versions_only,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogUsageRequest) Reset() { *m = LogUsageRequest{} }
func (m *LogUsageRequest) String() string { return proto.CompactTextString(m) }
func (*LogUsageRequest) ProtoMessage() {}
const Default_LogUsageRequest_ResolutionHours uint32 = 1
func (m *LogUsageRequest) GetAppId() string {
if m != nil && m.AppId != nil {
return *m.AppId
}
return ""
}
func (m *LogUsageRequest) GetVersionId() []string {
if m != nil {
return m.VersionId
}
return nil
}
func (m *LogUsageRequest) GetStartTime() int32 {
if m != nil && m.StartTime != nil {
return *m.StartTime
}
return 0
}
func (m *LogUsageRequest) GetEndTime() int32 {
if m != nil && m.EndTime != nil {
return *m.EndTime
}
return 0
}
func (m *LogUsageRequest) GetResolutionHours() uint32 {
if m != nil && m.ResolutionHours != nil {
return *m.ResolutionHours
}
return Default_LogUsageRequest_ResolutionHours
}
func (m *LogUsageRequest) GetCombineVersions() bool {
if m != nil && m.CombineVersions != nil {
return *m.CombineVersions
}
return false
}
func (m *LogUsageRequest) GetUsageVersion() int32 {
if m != nil && m.UsageVersion != nil {
return *m.UsageVersion
}
return 0
}
func (m *LogUsageRequest) GetVersionsOnly() bool {
if m != nil && m.VersionsOnly != nil {
return *m.VersionsOnly
}
return false
}
type LogUsageResponse struct {
Usage []*LogUsageRecord `protobuf:"bytes,1,rep,name=usage" json:"usage,omitempty"`
Summary *LogUsageRecord `protobuf:"bytes,2,opt,name=summary" json:"summary,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogUsageResponse) Reset() { *m = LogUsageResponse{} }
func (m *LogUsageResponse) String() string { return proto.CompactTextString(m) }
func (*LogUsageResponse) ProtoMessage() {}
func (m *LogUsageResponse) GetUsage() []*LogUsageRecord {
if m != nil {
return m.Usage
}
return nil
}
func (m *LogUsageResponse) GetSummary() *LogUsageRecord {
if m != nil {
return m.Summary
}
return nil
}
func init() {
proto.RegisterEnum("appengine.LogServiceError_ErrorCode", LogServiceError_ErrorCode_name, LogServiceError_ErrorCode_value)
}

View File

@@ -1,150 +0,0 @@
syntax = "proto2";
option go_package = "log";
package appengine;
message LogServiceError {
enum ErrorCode {
OK = 0;
INVALID_REQUEST = 1;
STORAGE_ERROR = 2;
}
}
message UserAppLogLine {
required int64 timestamp_usec = 1;
required int64 level = 2;
required string message = 3;
}
message UserAppLogGroup {
repeated UserAppLogLine log_line = 2;
}
message FlushRequest {
optional bytes logs = 1;
}
message SetStatusRequest {
required string status = 1;
}
message LogOffset {
optional bytes request_id = 1;
}
message LogLine {
required int64 time = 1;
required int32 level = 2;
required string log_message = 3;
}
message RequestLog {
required string app_id = 1;
optional string module_id = 37 [default="default"];
required string version_id = 2;
required bytes request_id = 3;
optional LogOffset offset = 35;
required string ip = 4;
optional string nickname = 5;
required int64 start_time = 6;
required int64 end_time = 7;
required int64 latency = 8;
required int64 mcycles = 9;
required string method = 10;
required string resource = 11;
required string http_version = 12;
required int32 status = 13;
required int64 response_size = 14;
optional string referrer = 15;
optional string user_agent = 16;
required string url_map_entry = 17;
required string combined = 18;
optional int64 api_mcycles = 19;
optional string host = 20;
optional double cost = 21;
optional string task_queue_name = 22;
optional string task_name = 23;
optional bool was_loading_request = 24;
optional int64 pending_time = 25;
optional int32 replica_index = 26 [default = -1];
optional bool finished = 27 [default = true];
optional bytes clone_key = 28;
repeated LogLine line = 29;
optional bool lines_incomplete = 36;
optional bytes app_engine_release = 38;
optional int32 exit_reason = 30;
optional bool was_throttled_for_time = 31;
optional bool was_throttled_for_requests = 32;
optional int64 throttled_time = 33;
optional bytes server_name = 34;
}
message LogModuleVersion {
optional string module_id = 1 [default="default"];
optional string version_id = 2;
}
message LogReadRequest {
required string app_id = 1;
repeated string version_id = 2;
repeated LogModuleVersion module_version = 19;
optional int64 start_time = 3;
optional int64 end_time = 4;
optional LogOffset offset = 5;
repeated bytes request_id = 6;
optional int32 minimum_log_level = 7;
optional bool include_incomplete = 8;
optional int64 count = 9;
optional string combined_log_regex = 14;
optional string host_regex = 15;
optional int32 replica_index = 16;
optional bool include_app_logs = 10;
optional int32 app_logs_per_request = 17;
optional bool include_host = 11;
optional bool include_all = 12;
optional bool cache_iterator = 13;
optional int32 num_shards = 18;
}
message LogReadResponse {
repeated RequestLog log = 1;
optional LogOffset offset = 2;
optional int64 last_end_time = 3;
}
message LogUsageRecord {
optional string version_id = 1;
optional int32 start_time = 2;
optional int32 end_time = 3;
optional int64 count = 4;
optional int64 total_size = 5;
optional int32 records = 6;
}
message LogUsageRequest {
required string app_id = 1;
repeated string version_id = 2;
optional int32 start_time = 3;
optional int32 end_time = 4;
optional uint32 resolution_hours = 5 [default = 1];
optional bool combine_versions = 6;
optional int32 usage_version = 7;
optional bool versions_only = 8;
}
message LogUsageResponse {
repeated LogUsageRecord usage = 1;
optional LogUsageRecord summary = 2;
}

View File

@@ -1,228 +0,0 @@
// Code generated by protoc-gen-go.
// source: google.golang.org/appengine/internal/mail/mail_service.proto
// DO NOT EDIT!
/*
Package mail is a generated protocol buffer package.
It is generated from these files:
google.golang.org/appengine/internal/mail/mail_service.proto
It has these top-level messages:
MailServiceError
MailAttachment
MailHeader
MailMessage
*/
package mail
import proto "github.com/golang/protobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
type MailServiceError_ErrorCode int32
const (
MailServiceError_OK MailServiceError_ErrorCode = 0
MailServiceError_INTERNAL_ERROR MailServiceError_ErrorCode = 1
MailServiceError_BAD_REQUEST MailServiceError_ErrorCode = 2
MailServiceError_UNAUTHORIZED_SENDER MailServiceError_ErrorCode = 3
MailServiceError_INVALID_ATTACHMENT_TYPE MailServiceError_ErrorCode = 4
MailServiceError_INVALID_HEADER_NAME MailServiceError_ErrorCode = 5
MailServiceError_INVALID_CONTENT_ID MailServiceError_ErrorCode = 6
)
var MailServiceError_ErrorCode_name = map[int32]string{
0: "OK",
1: "INTERNAL_ERROR",
2: "BAD_REQUEST",
3: "UNAUTHORIZED_SENDER",
4: "INVALID_ATTACHMENT_TYPE",
5: "INVALID_HEADER_NAME",
6: "INVALID_CONTENT_ID",
}
var MailServiceError_ErrorCode_value = map[string]int32{
"OK": 0,
"INTERNAL_ERROR": 1,
"BAD_REQUEST": 2,
"UNAUTHORIZED_SENDER": 3,
"INVALID_ATTACHMENT_TYPE": 4,
"INVALID_HEADER_NAME": 5,
"INVALID_CONTENT_ID": 6,
}
func (x MailServiceError_ErrorCode) Enum() *MailServiceError_ErrorCode {
p := new(MailServiceError_ErrorCode)
*p = x
return p
}
func (x MailServiceError_ErrorCode) String() string {
return proto.EnumName(MailServiceError_ErrorCode_name, int32(x))
}
func (x *MailServiceError_ErrorCode) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(MailServiceError_ErrorCode_value, data, "MailServiceError_ErrorCode")
if err != nil {
return err
}
*x = MailServiceError_ErrorCode(value)
return nil
}
type MailServiceError struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *MailServiceError) Reset() { *m = MailServiceError{} }
func (m *MailServiceError) String() string { return proto.CompactTextString(m) }
func (*MailServiceError) ProtoMessage() {}
type MailAttachment struct {
FileName *string `protobuf:"bytes,1,req" json:"FileName,omitempty"`
Data []byte `protobuf:"bytes,2,req" json:"Data,omitempty"`
ContentID *string `protobuf:"bytes,3,opt" json:"ContentID,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MailAttachment) Reset() { *m = MailAttachment{} }
func (m *MailAttachment) String() string { return proto.CompactTextString(m) }
func (*MailAttachment) ProtoMessage() {}
func (m *MailAttachment) GetFileName() string {
if m != nil && m.FileName != nil {
return *m.FileName
}
return ""
}
func (m *MailAttachment) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
func (m *MailAttachment) GetContentID() string {
if m != nil && m.ContentID != nil {
return *m.ContentID
}
return ""
}
type MailHeader struct {
Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
Value *string `protobuf:"bytes,2,req,name=value" json:"value,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MailHeader) Reset() { *m = MailHeader{} }
func (m *MailHeader) String() string { return proto.CompactTextString(m) }
func (*MailHeader) ProtoMessage() {}
func (m *MailHeader) GetName() string {
if m != nil && m.Name != nil {
return *m.Name
}
return ""
}
func (m *MailHeader) GetValue() string {
if m != nil && m.Value != nil {
return *m.Value
}
return ""
}
type MailMessage struct {
Sender *string `protobuf:"bytes,1,req" json:"Sender,omitempty"`
ReplyTo *string `protobuf:"bytes,2,opt" json:"ReplyTo,omitempty"`
To []string `protobuf:"bytes,3,rep" json:"To,omitempty"`
Cc []string `protobuf:"bytes,4,rep" json:"Cc,omitempty"`
Bcc []string `protobuf:"bytes,5,rep" json:"Bcc,omitempty"`
Subject *string `protobuf:"bytes,6,req" json:"Subject,omitempty"`
TextBody *string `protobuf:"bytes,7,opt" json:"TextBody,omitempty"`
HtmlBody *string `protobuf:"bytes,8,opt" json:"HtmlBody,omitempty"`
Attachment []*MailAttachment `protobuf:"bytes,9,rep" json:"Attachment,omitempty"`
Header []*MailHeader `protobuf:"bytes,10,rep" json:"Header,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MailMessage) Reset() { *m = MailMessage{} }
func (m *MailMessage) String() string { return proto.CompactTextString(m) }
func (*MailMessage) ProtoMessage() {}
func (m *MailMessage) GetSender() string {
if m != nil && m.Sender != nil {
return *m.Sender
}
return ""
}
func (m *MailMessage) GetReplyTo() string {
if m != nil && m.ReplyTo != nil {
return *m.ReplyTo
}
return ""
}
func (m *MailMessage) GetTo() []string {
if m != nil {
return m.To
}
return nil
}
func (m *MailMessage) GetCc() []string {
if m != nil {
return m.Cc
}
return nil
}
func (m *MailMessage) GetBcc() []string {
if m != nil {
return m.Bcc
}
return nil
}
func (m *MailMessage) GetSubject() string {
if m != nil && m.Subject != nil {
return *m.Subject
}
return ""
}
func (m *MailMessage) GetTextBody() string {
if m != nil && m.TextBody != nil {
return *m.TextBody
}
return ""
}
func (m *MailMessage) GetHtmlBody() string {
if m != nil && m.HtmlBody != nil {
return *m.HtmlBody
}
return ""
}
func (m *MailMessage) GetAttachment() []*MailAttachment {
if m != nil {
return m.Attachment
}
return nil
}
func (m *MailMessage) GetHeader() []*MailHeader {
if m != nil {
return m.Header
}
return nil
}
func init() {
proto.RegisterEnum("appengine.MailServiceError_ErrorCode", MailServiceError_ErrorCode_name, MailServiceError_ErrorCode_value)
}

View File

@@ -1,45 +0,0 @@
syntax = "proto2";
option go_package = "mail";
package appengine;
message MailServiceError {
enum ErrorCode {
OK = 0;
INTERNAL_ERROR = 1;
BAD_REQUEST = 2;
UNAUTHORIZED_SENDER = 3;
INVALID_ATTACHMENT_TYPE = 4;
INVALID_HEADER_NAME = 5;
INVALID_CONTENT_ID = 6;
}
}
message MailAttachment {
required string FileName = 1;
required bytes Data = 2;
optional string ContentID = 3;
}
message MailHeader {
required string name = 1;
required string value = 2;
}
message MailMessage {
required string Sender = 1;
optional string ReplyTo = 2;
repeated string To = 3;
repeated string Cc = 4;
repeated string Bcc = 5;
required string Subject = 6;
optional string TextBody = 7;
optional string HtmlBody = 8;
repeated MailAttachment Attachment = 9;
repeated MailHeader Header = 10;
}

View File

@@ -1,942 +0,0 @@
// Code generated by protoc-gen-go.
// source: google.golang.org/appengine/internal/memcache/memcache_service.proto
// DO NOT EDIT!
/*
Package memcache is a generated protocol buffer package.
It is generated from these files:
google.golang.org/appengine/internal/memcache/memcache_service.proto
It has these top-level messages:
MemcacheServiceError
AppOverride
MemcacheGetRequest
MemcacheGetResponse
MemcacheSetRequest
MemcacheSetResponse
MemcacheDeleteRequest
MemcacheDeleteResponse
MemcacheIncrementRequest
MemcacheIncrementResponse
MemcacheBatchIncrementRequest
MemcacheBatchIncrementResponse
MemcacheFlushRequest
MemcacheFlushResponse
MemcacheStatsRequest
MergedNamespaceStats
MemcacheStatsResponse
MemcacheGrabTailRequest
MemcacheGrabTailResponse
*/
package memcache
import proto "github.com/golang/protobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
type MemcacheServiceError_ErrorCode int32
const (
MemcacheServiceError_OK MemcacheServiceError_ErrorCode = 0
MemcacheServiceError_UNSPECIFIED_ERROR MemcacheServiceError_ErrorCode = 1
MemcacheServiceError_NAMESPACE_NOT_SET MemcacheServiceError_ErrorCode = 2
MemcacheServiceError_PERMISSION_DENIED MemcacheServiceError_ErrorCode = 3
MemcacheServiceError_INVALID_VALUE MemcacheServiceError_ErrorCode = 6
)
var MemcacheServiceError_ErrorCode_name = map[int32]string{
0: "OK",
1: "UNSPECIFIED_ERROR",
2: "NAMESPACE_NOT_SET",
3: "PERMISSION_DENIED",
6: "INVALID_VALUE",
}
var MemcacheServiceError_ErrorCode_value = map[string]int32{
"OK": 0,
"UNSPECIFIED_ERROR": 1,
"NAMESPACE_NOT_SET": 2,
"PERMISSION_DENIED": 3,
"INVALID_VALUE": 6,
}
func (x MemcacheServiceError_ErrorCode) Enum() *MemcacheServiceError_ErrorCode {
p := new(MemcacheServiceError_ErrorCode)
*p = x
return p
}
func (x MemcacheServiceError_ErrorCode) String() string {
return proto.EnumName(MemcacheServiceError_ErrorCode_name, int32(x))
}
func (x *MemcacheServiceError_ErrorCode) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(MemcacheServiceError_ErrorCode_value, data, "MemcacheServiceError_ErrorCode")
if err != nil {
return err
}
*x = MemcacheServiceError_ErrorCode(value)
return nil
}
type MemcacheSetRequest_SetPolicy int32
const (
MemcacheSetRequest_SET MemcacheSetRequest_SetPolicy = 1
MemcacheSetRequest_ADD MemcacheSetRequest_SetPolicy = 2
MemcacheSetRequest_REPLACE MemcacheSetRequest_SetPolicy = 3
MemcacheSetRequest_CAS MemcacheSetRequest_SetPolicy = 4
)
var MemcacheSetRequest_SetPolicy_name = map[int32]string{
1: "SET",
2: "ADD",
3: "REPLACE",
4: "CAS",
}
var MemcacheSetRequest_SetPolicy_value = map[string]int32{
"SET": 1,
"ADD": 2,
"REPLACE": 3,
"CAS": 4,
}
func (x MemcacheSetRequest_SetPolicy) Enum() *MemcacheSetRequest_SetPolicy {
p := new(MemcacheSetRequest_SetPolicy)
*p = x
return p
}
func (x MemcacheSetRequest_SetPolicy) String() string {
return proto.EnumName(MemcacheSetRequest_SetPolicy_name, int32(x))
}
func (x *MemcacheSetRequest_SetPolicy) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(MemcacheSetRequest_SetPolicy_value, data, "MemcacheSetRequest_SetPolicy")
if err != nil {
return err
}
*x = MemcacheSetRequest_SetPolicy(value)
return nil
}
type MemcacheSetResponse_SetStatusCode int32
const (
MemcacheSetResponse_STORED MemcacheSetResponse_SetStatusCode = 1
MemcacheSetResponse_NOT_STORED MemcacheSetResponse_SetStatusCode = 2
MemcacheSetResponse_ERROR MemcacheSetResponse_SetStatusCode = 3
MemcacheSetResponse_EXISTS MemcacheSetResponse_SetStatusCode = 4
)
var MemcacheSetResponse_SetStatusCode_name = map[int32]string{
1: "STORED",
2: "NOT_STORED",
3: "ERROR",
4: "EXISTS",
}
var MemcacheSetResponse_SetStatusCode_value = map[string]int32{
"STORED": 1,
"NOT_STORED": 2,
"ERROR": 3,
"EXISTS": 4,
}
func (x MemcacheSetResponse_SetStatusCode) Enum() *MemcacheSetResponse_SetStatusCode {
p := new(MemcacheSetResponse_SetStatusCode)
*p = x
return p
}
func (x MemcacheSetResponse_SetStatusCode) String() string {
return proto.EnumName(MemcacheSetResponse_SetStatusCode_name, int32(x))
}
func (x *MemcacheSetResponse_SetStatusCode) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(MemcacheSetResponse_SetStatusCode_value, data, "MemcacheSetResponse_SetStatusCode")
if err != nil {
return err
}
*x = MemcacheSetResponse_SetStatusCode(value)
return nil
}
type MemcacheDeleteResponse_DeleteStatusCode int32
const (
MemcacheDeleteResponse_DELETED MemcacheDeleteResponse_DeleteStatusCode = 1
MemcacheDeleteResponse_NOT_FOUND MemcacheDeleteResponse_DeleteStatusCode = 2
)
var MemcacheDeleteResponse_DeleteStatusCode_name = map[int32]string{
1: "DELETED",
2: "NOT_FOUND",
}
var MemcacheDeleteResponse_DeleteStatusCode_value = map[string]int32{
"DELETED": 1,
"NOT_FOUND": 2,
}
func (x MemcacheDeleteResponse_DeleteStatusCode) Enum() *MemcacheDeleteResponse_DeleteStatusCode {
p := new(MemcacheDeleteResponse_DeleteStatusCode)
*p = x
return p
}
func (x MemcacheDeleteResponse_DeleteStatusCode) String() string {
return proto.EnumName(MemcacheDeleteResponse_DeleteStatusCode_name, int32(x))
}
func (x *MemcacheDeleteResponse_DeleteStatusCode) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(MemcacheDeleteResponse_DeleteStatusCode_value, data, "MemcacheDeleteResponse_DeleteStatusCode")
if err != nil {
return err
}
*x = MemcacheDeleteResponse_DeleteStatusCode(value)
return nil
}
type MemcacheIncrementRequest_Direction int32
const (
MemcacheIncrementRequest_INCREMENT MemcacheIncrementRequest_Direction = 1
MemcacheIncrementRequest_DECREMENT MemcacheIncrementRequest_Direction = 2
)
var MemcacheIncrementRequest_Direction_name = map[int32]string{
1: "INCREMENT",
2: "DECREMENT",
}
var MemcacheIncrementRequest_Direction_value = map[string]int32{
"INCREMENT": 1,
"DECREMENT": 2,
}
func (x MemcacheIncrementRequest_Direction) Enum() *MemcacheIncrementRequest_Direction {
p := new(MemcacheIncrementRequest_Direction)
*p = x
return p
}
func (x MemcacheIncrementRequest_Direction) String() string {
return proto.EnumName(MemcacheIncrementRequest_Direction_name, int32(x))
}
func (x *MemcacheIncrementRequest_Direction) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(MemcacheIncrementRequest_Direction_value, data, "MemcacheIncrementRequest_Direction")
if err != nil {
return err
}
*x = MemcacheIncrementRequest_Direction(value)
return nil
}
type MemcacheIncrementResponse_IncrementStatusCode int32
const (
MemcacheIncrementResponse_OK MemcacheIncrementResponse_IncrementStatusCode = 1
MemcacheIncrementResponse_NOT_CHANGED MemcacheIncrementResponse_IncrementStatusCode = 2
MemcacheIncrementResponse_ERROR MemcacheIncrementResponse_IncrementStatusCode = 3
)
var MemcacheIncrementResponse_IncrementStatusCode_name = map[int32]string{
1: "OK",
2: "NOT_CHANGED",
3: "ERROR",
}
var MemcacheIncrementResponse_IncrementStatusCode_value = map[string]int32{
"OK": 1,
"NOT_CHANGED": 2,
"ERROR": 3,
}
func (x MemcacheIncrementResponse_IncrementStatusCode) Enum() *MemcacheIncrementResponse_IncrementStatusCode {
p := new(MemcacheIncrementResponse_IncrementStatusCode)
*p = x
return p
}
func (x MemcacheIncrementResponse_IncrementStatusCode) String() string {
return proto.EnumName(MemcacheIncrementResponse_IncrementStatusCode_name, int32(x))
}
func (x *MemcacheIncrementResponse_IncrementStatusCode) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(MemcacheIncrementResponse_IncrementStatusCode_value, data, "MemcacheIncrementResponse_IncrementStatusCode")
if err != nil {
return err
}
*x = MemcacheIncrementResponse_IncrementStatusCode(value)
return nil
}
type MemcacheServiceError struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheServiceError) Reset() { *m = MemcacheServiceError{} }
func (m *MemcacheServiceError) String() string { return proto.CompactTextString(m) }
func (*MemcacheServiceError) ProtoMessage() {}
type AppOverride struct {
AppId *string `protobuf:"bytes,1,req,name=app_id" json:"app_id,omitempty"`
NumMemcachegBackends *int32 `protobuf:"varint,2,opt,name=num_memcacheg_backends" json:"num_memcacheg_backends,omitempty"`
IgnoreShardlock *bool `protobuf:"varint,3,opt,name=ignore_shardlock" json:"ignore_shardlock,omitempty"`
MemcachePoolHint *string `protobuf:"bytes,4,opt,name=memcache_pool_hint" json:"memcache_pool_hint,omitempty"`
MemcacheShardingStrategy []byte `protobuf:"bytes,5,opt,name=memcache_sharding_strategy" json:"memcache_sharding_strategy,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *AppOverride) Reset() { *m = AppOverride{} }
func (m *AppOverride) String() string { return proto.CompactTextString(m) }
func (*AppOverride) ProtoMessage() {}
func (m *AppOverride) GetAppId() string {
if m != nil && m.AppId != nil {
return *m.AppId
}
return ""
}
func (m *AppOverride) GetNumMemcachegBackends() int32 {
if m != nil && m.NumMemcachegBackends != nil {
return *m.NumMemcachegBackends
}
return 0
}
func (m *AppOverride) GetIgnoreShardlock() bool {
if m != nil && m.IgnoreShardlock != nil {
return *m.IgnoreShardlock
}
return false
}
func (m *AppOverride) GetMemcachePoolHint() string {
if m != nil && m.MemcachePoolHint != nil {
return *m.MemcachePoolHint
}
return ""
}
func (m *AppOverride) GetMemcacheShardingStrategy() []byte {
if m != nil {
return m.MemcacheShardingStrategy
}
return nil
}
type MemcacheGetRequest struct {
Key [][]byte `protobuf:"bytes,1,rep,name=key" json:"key,omitempty"`
NameSpace *string `protobuf:"bytes,2,opt,name=name_space,def=" json:"name_space,omitempty"`
ForCas *bool `protobuf:"varint,4,opt,name=for_cas" json:"for_cas,omitempty"`
Override *AppOverride `protobuf:"bytes,5,opt,name=override" json:"override,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheGetRequest) Reset() { *m = MemcacheGetRequest{} }
func (m *MemcacheGetRequest) String() string { return proto.CompactTextString(m) }
func (*MemcacheGetRequest) ProtoMessage() {}
func (m *MemcacheGetRequest) GetKey() [][]byte {
if m != nil {
return m.Key
}
return nil
}
func (m *MemcacheGetRequest) GetNameSpace() string {
if m != nil && m.NameSpace != nil {
return *m.NameSpace
}
return ""
}
func (m *MemcacheGetRequest) GetForCas() bool {
if m != nil && m.ForCas != nil {
return *m.ForCas
}
return false
}
func (m *MemcacheGetRequest) GetOverride() *AppOverride {
if m != nil {
return m.Override
}
return nil
}
type MemcacheGetResponse struct {
Item []*MemcacheGetResponse_Item `protobuf:"group,1,rep" json:"item,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheGetResponse) Reset() { *m = MemcacheGetResponse{} }
func (m *MemcacheGetResponse) String() string { return proto.CompactTextString(m) }
func (*MemcacheGetResponse) ProtoMessage() {}
func (m *MemcacheGetResponse) GetItem() []*MemcacheGetResponse_Item {
if m != nil {
return m.Item
}
return nil
}
type MemcacheGetResponse_Item struct {
Key []byte `protobuf:"bytes,2,req,name=key" json:"key,omitempty"`
Value []byte `protobuf:"bytes,3,req,name=value" json:"value,omitempty"`
Flags *uint32 `protobuf:"fixed32,4,opt,name=flags" json:"flags,omitempty"`
CasId *uint64 `protobuf:"fixed64,5,opt,name=cas_id" json:"cas_id,omitempty"`
ExpiresInSeconds *int32 `protobuf:"varint,6,opt,name=expires_in_seconds" json:"expires_in_seconds,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheGetResponse_Item) Reset() { *m = MemcacheGetResponse_Item{} }
func (m *MemcacheGetResponse_Item) String() string { return proto.CompactTextString(m) }
func (*MemcacheGetResponse_Item) ProtoMessage() {}
func (m *MemcacheGetResponse_Item) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *MemcacheGetResponse_Item) GetValue() []byte {
if m != nil {
return m.Value
}
return nil
}
func (m *MemcacheGetResponse_Item) GetFlags() uint32 {
if m != nil && m.Flags != nil {
return *m.Flags
}
return 0
}
func (m *MemcacheGetResponse_Item) GetCasId() uint64 {
if m != nil && m.CasId != nil {
return *m.CasId
}
return 0
}
func (m *MemcacheGetResponse_Item) GetExpiresInSeconds() int32 {
if m != nil && m.ExpiresInSeconds != nil {
return *m.ExpiresInSeconds
}
return 0
}
type MemcacheSetRequest struct {
Item []*MemcacheSetRequest_Item `protobuf:"group,1,rep" json:"item,omitempty"`
NameSpace *string `protobuf:"bytes,7,opt,name=name_space,def=" json:"name_space,omitempty"`
Override *AppOverride `protobuf:"bytes,10,opt,name=override" json:"override,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheSetRequest) Reset() { *m = MemcacheSetRequest{} }
func (m *MemcacheSetRequest) String() string { return proto.CompactTextString(m) }
func (*MemcacheSetRequest) ProtoMessage() {}
func (m *MemcacheSetRequest) GetItem() []*MemcacheSetRequest_Item {
if m != nil {
return m.Item
}
return nil
}
func (m *MemcacheSetRequest) GetNameSpace() string {
if m != nil && m.NameSpace != nil {
return *m.NameSpace
}
return ""
}
func (m *MemcacheSetRequest) GetOverride() *AppOverride {
if m != nil {
return m.Override
}
return nil
}
type MemcacheSetRequest_Item struct {
Key []byte `protobuf:"bytes,2,req,name=key" json:"key,omitempty"`
Value []byte `protobuf:"bytes,3,req,name=value" json:"value,omitempty"`
Flags *uint32 `protobuf:"fixed32,4,opt,name=flags" json:"flags,omitempty"`
SetPolicy *MemcacheSetRequest_SetPolicy `protobuf:"varint,5,opt,name=set_policy,enum=appengine.MemcacheSetRequest_SetPolicy,def=1" json:"set_policy,omitempty"`
ExpirationTime *uint32 `protobuf:"fixed32,6,opt,name=expiration_time,def=0" json:"expiration_time,omitempty"`
CasId *uint64 `protobuf:"fixed64,8,opt,name=cas_id" json:"cas_id,omitempty"`
ForCas *bool `protobuf:"varint,9,opt,name=for_cas" json:"for_cas,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheSetRequest_Item) Reset() { *m = MemcacheSetRequest_Item{} }
func (m *MemcacheSetRequest_Item) String() string { return proto.CompactTextString(m) }
func (*MemcacheSetRequest_Item) ProtoMessage() {}
const Default_MemcacheSetRequest_Item_SetPolicy MemcacheSetRequest_SetPolicy = MemcacheSetRequest_SET
const Default_MemcacheSetRequest_Item_ExpirationTime uint32 = 0
func (m *MemcacheSetRequest_Item) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *MemcacheSetRequest_Item) GetValue() []byte {
if m != nil {
return m.Value
}
return nil
}
func (m *MemcacheSetRequest_Item) GetFlags() uint32 {
if m != nil && m.Flags != nil {
return *m.Flags
}
return 0
}
func (m *MemcacheSetRequest_Item) GetSetPolicy() MemcacheSetRequest_SetPolicy {
if m != nil && m.SetPolicy != nil {
return *m.SetPolicy
}
return Default_MemcacheSetRequest_Item_SetPolicy
}
func (m *MemcacheSetRequest_Item) GetExpirationTime() uint32 {
if m != nil && m.ExpirationTime != nil {
return *m.ExpirationTime
}
return Default_MemcacheSetRequest_Item_ExpirationTime
}
func (m *MemcacheSetRequest_Item) GetCasId() uint64 {
if m != nil && m.CasId != nil {
return *m.CasId
}
return 0
}
func (m *MemcacheSetRequest_Item) GetForCas() bool {
if m != nil && m.ForCas != nil {
return *m.ForCas
}
return false
}
type MemcacheSetResponse struct {
SetStatus []MemcacheSetResponse_SetStatusCode `protobuf:"varint,1,rep,name=set_status,enum=appengine.MemcacheSetResponse_SetStatusCode" json:"set_status,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheSetResponse) Reset() { *m = MemcacheSetResponse{} }
func (m *MemcacheSetResponse) String() string { return proto.CompactTextString(m) }
func (*MemcacheSetResponse) ProtoMessage() {}
func (m *MemcacheSetResponse) GetSetStatus() []MemcacheSetResponse_SetStatusCode {
if m != nil {
return m.SetStatus
}
return nil
}
type MemcacheDeleteRequest struct {
Item []*MemcacheDeleteRequest_Item `protobuf:"group,1,rep" json:"item,omitempty"`
NameSpace *string `protobuf:"bytes,4,opt,name=name_space,def=" json:"name_space,omitempty"`
Override *AppOverride `protobuf:"bytes,5,opt,name=override" json:"override,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheDeleteRequest) Reset() { *m = MemcacheDeleteRequest{} }
func (m *MemcacheDeleteRequest) String() string { return proto.CompactTextString(m) }
func (*MemcacheDeleteRequest) ProtoMessage() {}
func (m *MemcacheDeleteRequest) GetItem() []*MemcacheDeleteRequest_Item {
if m != nil {
return m.Item
}
return nil
}
func (m *MemcacheDeleteRequest) GetNameSpace() string {
if m != nil && m.NameSpace != nil {
return *m.NameSpace
}
return ""
}
func (m *MemcacheDeleteRequest) GetOverride() *AppOverride {
if m != nil {
return m.Override
}
return nil
}
type MemcacheDeleteRequest_Item struct {
Key []byte `protobuf:"bytes,2,req,name=key" json:"key,omitempty"`
DeleteTime *uint32 `protobuf:"fixed32,3,opt,name=delete_time,def=0" json:"delete_time,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheDeleteRequest_Item) Reset() { *m = MemcacheDeleteRequest_Item{} }
func (m *MemcacheDeleteRequest_Item) String() string { return proto.CompactTextString(m) }
func (*MemcacheDeleteRequest_Item) ProtoMessage() {}
const Default_MemcacheDeleteRequest_Item_DeleteTime uint32 = 0
func (m *MemcacheDeleteRequest_Item) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *MemcacheDeleteRequest_Item) GetDeleteTime() uint32 {
if m != nil && m.DeleteTime != nil {
return *m.DeleteTime
}
return Default_MemcacheDeleteRequest_Item_DeleteTime
}
type MemcacheDeleteResponse struct {
DeleteStatus []MemcacheDeleteResponse_DeleteStatusCode `protobuf:"varint,1,rep,name=delete_status,enum=appengine.MemcacheDeleteResponse_DeleteStatusCode" json:"delete_status,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheDeleteResponse) Reset() { *m = MemcacheDeleteResponse{} }
func (m *MemcacheDeleteResponse) String() string { return proto.CompactTextString(m) }
func (*MemcacheDeleteResponse) ProtoMessage() {}
func (m *MemcacheDeleteResponse) GetDeleteStatus() []MemcacheDeleteResponse_DeleteStatusCode {
if m != nil {
return m.DeleteStatus
}
return nil
}
type MemcacheIncrementRequest struct {
Key []byte `protobuf:"bytes,1,req,name=key" json:"key,omitempty"`
NameSpace *string `protobuf:"bytes,4,opt,name=name_space,def=" json:"name_space,omitempty"`
Delta *uint64 `protobuf:"varint,2,opt,name=delta,def=1" json:"delta,omitempty"`
Direction *MemcacheIncrementRequest_Direction `protobuf:"varint,3,opt,name=direction,enum=appengine.MemcacheIncrementRequest_Direction,def=1" json:"direction,omitempty"`
InitialValue *uint64 `protobuf:"varint,5,opt,name=initial_value" json:"initial_value,omitempty"`
InitialFlags *uint32 `protobuf:"fixed32,6,opt,name=initial_flags" json:"initial_flags,omitempty"`
Override *AppOverride `protobuf:"bytes,7,opt,name=override" json:"override,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheIncrementRequest) Reset() { *m = MemcacheIncrementRequest{} }
func (m *MemcacheIncrementRequest) String() string { return proto.CompactTextString(m) }
func (*MemcacheIncrementRequest) ProtoMessage() {}
const Default_MemcacheIncrementRequest_Delta uint64 = 1
const Default_MemcacheIncrementRequest_Direction MemcacheIncrementRequest_Direction = MemcacheIncrementRequest_INCREMENT
func (m *MemcacheIncrementRequest) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *MemcacheIncrementRequest) GetNameSpace() string {
if m != nil && m.NameSpace != nil {
return *m.NameSpace
}
return ""
}
func (m *MemcacheIncrementRequest) GetDelta() uint64 {
if m != nil && m.Delta != nil {
return *m.Delta
}
return Default_MemcacheIncrementRequest_Delta
}
func (m *MemcacheIncrementRequest) GetDirection() MemcacheIncrementRequest_Direction {
if m != nil && m.Direction != nil {
return *m.Direction
}
return Default_MemcacheIncrementRequest_Direction
}
func (m *MemcacheIncrementRequest) GetInitialValue() uint64 {
if m != nil && m.InitialValue != nil {
return *m.InitialValue
}
return 0
}
func (m *MemcacheIncrementRequest) GetInitialFlags() uint32 {
if m != nil && m.InitialFlags != nil {
return *m.InitialFlags
}
return 0
}
func (m *MemcacheIncrementRequest) GetOverride() *AppOverride {
if m != nil {
return m.Override
}
return nil
}
type MemcacheIncrementResponse struct {
NewValue *uint64 `protobuf:"varint,1,opt,name=new_value" json:"new_value,omitempty"`
IncrementStatus *MemcacheIncrementResponse_IncrementStatusCode `protobuf:"varint,2,opt,name=increment_status,enum=appengine.MemcacheIncrementResponse_IncrementStatusCode" json:"increment_status,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheIncrementResponse) Reset() { *m = MemcacheIncrementResponse{} }
func (m *MemcacheIncrementResponse) String() string { return proto.CompactTextString(m) }
func (*MemcacheIncrementResponse) ProtoMessage() {}
func (m *MemcacheIncrementResponse) GetNewValue() uint64 {
if m != nil && m.NewValue != nil {
return *m.NewValue
}
return 0
}
func (m *MemcacheIncrementResponse) GetIncrementStatus() MemcacheIncrementResponse_IncrementStatusCode {
if m != nil && m.IncrementStatus != nil {
return *m.IncrementStatus
}
return MemcacheIncrementResponse_OK
}
type MemcacheBatchIncrementRequest struct {
NameSpace *string `protobuf:"bytes,1,opt,name=name_space,def=" json:"name_space,omitempty"`
Item []*MemcacheIncrementRequest `protobuf:"bytes,2,rep,name=item" json:"item,omitempty"`
Override *AppOverride `protobuf:"bytes,3,opt,name=override" json:"override,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheBatchIncrementRequest) Reset() { *m = MemcacheBatchIncrementRequest{} }
func (m *MemcacheBatchIncrementRequest) String() string { return proto.CompactTextString(m) }
func (*MemcacheBatchIncrementRequest) ProtoMessage() {}
func (m *MemcacheBatchIncrementRequest) GetNameSpace() string {
if m != nil && m.NameSpace != nil {
return *m.NameSpace
}
return ""
}
func (m *MemcacheBatchIncrementRequest) GetItem() []*MemcacheIncrementRequest {
if m != nil {
return m.Item
}
return nil
}
func (m *MemcacheBatchIncrementRequest) GetOverride() *AppOverride {
if m != nil {
return m.Override
}
return nil
}
type MemcacheBatchIncrementResponse struct {
Item []*MemcacheIncrementResponse `protobuf:"bytes,1,rep,name=item" json:"item,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheBatchIncrementResponse) Reset() { *m = MemcacheBatchIncrementResponse{} }
func (m *MemcacheBatchIncrementResponse) String() string { return proto.CompactTextString(m) }
func (*MemcacheBatchIncrementResponse) ProtoMessage() {}
func (m *MemcacheBatchIncrementResponse) GetItem() []*MemcacheIncrementResponse {
if m != nil {
return m.Item
}
return nil
}
type MemcacheFlushRequest struct {
Override *AppOverride `protobuf:"bytes,1,opt,name=override" json:"override,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheFlushRequest) Reset() { *m = MemcacheFlushRequest{} }
func (m *MemcacheFlushRequest) String() string { return proto.CompactTextString(m) }
func (*MemcacheFlushRequest) ProtoMessage() {}
func (m *MemcacheFlushRequest) GetOverride() *AppOverride {
if m != nil {
return m.Override
}
return nil
}
type MemcacheFlushResponse struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheFlushResponse) Reset() { *m = MemcacheFlushResponse{} }
func (m *MemcacheFlushResponse) String() string { return proto.CompactTextString(m) }
func (*MemcacheFlushResponse) ProtoMessage() {}
type MemcacheStatsRequest struct {
Override *AppOverride `protobuf:"bytes,1,opt,name=override" json:"override,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheStatsRequest) Reset() { *m = MemcacheStatsRequest{} }
func (m *MemcacheStatsRequest) String() string { return proto.CompactTextString(m) }
func (*MemcacheStatsRequest) ProtoMessage() {}
func (m *MemcacheStatsRequest) GetOverride() *AppOverride {
if m != nil {
return m.Override
}
return nil
}
type MergedNamespaceStats struct {
Hits *uint64 `protobuf:"varint,1,req,name=hits" json:"hits,omitempty"`
Misses *uint64 `protobuf:"varint,2,req,name=misses" json:"misses,omitempty"`
ByteHits *uint64 `protobuf:"varint,3,req,name=byte_hits" json:"byte_hits,omitempty"`
Items *uint64 `protobuf:"varint,4,req,name=items" json:"items,omitempty"`
Bytes *uint64 `protobuf:"varint,5,req,name=bytes" json:"bytes,omitempty"`
OldestItemAge *uint32 `protobuf:"fixed32,6,req,name=oldest_item_age" json:"oldest_item_age,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MergedNamespaceStats) Reset() { *m = MergedNamespaceStats{} }
func (m *MergedNamespaceStats) String() string { return proto.CompactTextString(m) }
func (*MergedNamespaceStats) ProtoMessage() {}
func (m *MergedNamespaceStats) GetHits() uint64 {
if m != nil && m.Hits != nil {
return *m.Hits
}
return 0
}
func (m *MergedNamespaceStats) GetMisses() uint64 {
if m != nil && m.Misses != nil {
return *m.Misses
}
return 0
}
func (m *MergedNamespaceStats) GetByteHits() uint64 {
if m != nil && m.ByteHits != nil {
return *m.ByteHits
}
return 0
}
func (m *MergedNamespaceStats) GetItems() uint64 {
if m != nil && m.Items != nil {
return *m.Items
}
return 0
}
func (m *MergedNamespaceStats) GetBytes() uint64 {
if m != nil && m.Bytes != nil {
return *m.Bytes
}
return 0
}
func (m *MergedNamespaceStats) GetOldestItemAge() uint32 {
if m != nil && m.OldestItemAge != nil {
return *m.OldestItemAge
}
return 0
}
type MemcacheStatsResponse struct {
Stats *MergedNamespaceStats `protobuf:"bytes,1,opt,name=stats" json:"stats,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheStatsResponse) Reset() { *m = MemcacheStatsResponse{} }
func (m *MemcacheStatsResponse) String() string { return proto.CompactTextString(m) }
func (*MemcacheStatsResponse) ProtoMessage() {}
func (m *MemcacheStatsResponse) GetStats() *MergedNamespaceStats {
if m != nil {
return m.Stats
}
return nil
}
type MemcacheGrabTailRequest struct {
ItemCount *int32 `protobuf:"varint,1,req,name=item_count" json:"item_count,omitempty"`
NameSpace *string `protobuf:"bytes,2,opt,name=name_space,def=" json:"name_space,omitempty"`
Override *AppOverride `protobuf:"bytes,3,opt,name=override" json:"override,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheGrabTailRequest) Reset() { *m = MemcacheGrabTailRequest{} }
func (m *MemcacheGrabTailRequest) String() string { return proto.CompactTextString(m) }
func (*MemcacheGrabTailRequest) ProtoMessage() {}
func (m *MemcacheGrabTailRequest) GetItemCount() int32 {
if m != nil && m.ItemCount != nil {
return *m.ItemCount
}
return 0
}
func (m *MemcacheGrabTailRequest) GetNameSpace() string {
if m != nil && m.NameSpace != nil {
return *m.NameSpace
}
return ""
}
func (m *MemcacheGrabTailRequest) GetOverride() *AppOverride {
if m != nil {
return m.Override
}
return nil
}
type MemcacheGrabTailResponse struct {
Item []*MemcacheGrabTailResponse_Item `protobuf:"group,1,rep" json:"item,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheGrabTailResponse) Reset() { *m = MemcacheGrabTailResponse{} }
func (m *MemcacheGrabTailResponse) String() string { return proto.CompactTextString(m) }
func (*MemcacheGrabTailResponse) ProtoMessage() {}
func (m *MemcacheGrabTailResponse) GetItem() []*MemcacheGrabTailResponse_Item {
if m != nil {
return m.Item
}
return nil
}
type MemcacheGrabTailResponse_Item struct {
Value []byte `protobuf:"bytes,2,req,name=value" json:"value,omitempty"`
Flags *uint32 `protobuf:"fixed32,3,opt,name=flags" json:"flags,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *MemcacheGrabTailResponse_Item) Reset() { *m = MemcacheGrabTailResponse_Item{} }
func (m *MemcacheGrabTailResponse_Item) String() string { return proto.CompactTextString(m) }
func (*MemcacheGrabTailResponse_Item) ProtoMessage() {}
func (m *MemcacheGrabTailResponse_Item) GetValue() []byte {
if m != nil {
return m.Value
}
return nil
}
func (m *MemcacheGrabTailResponse_Item) GetFlags() uint32 {
if m != nil && m.Flags != nil {
return *m.Flags
}
return 0
}
func init() {
proto.RegisterEnum("appengine.MemcacheServiceError_ErrorCode", MemcacheServiceError_ErrorCode_name, MemcacheServiceError_ErrorCode_value)
proto.RegisterEnum("appengine.MemcacheSetRequest_SetPolicy", MemcacheSetRequest_SetPolicy_name, MemcacheSetRequest_SetPolicy_value)
proto.RegisterEnum("appengine.MemcacheSetResponse_SetStatusCode", MemcacheSetResponse_SetStatusCode_name, MemcacheSetResponse_SetStatusCode_value)
proto.RegisterEnum("appengine.MemcacheDeleteResponse_DeleteStatusCode", MemcacheDeleteResponse_DeleteStatusCode_name, MemcacheDeleteResponse_DeleteStatusCode_value)
proto.RegisterEnum("appengine.MemcacheIncrementRequest_Direction", MemcacheIncrementRequest_Direction_name, MemcacheIncrementRequest_Direction_value)
proto.RegisterEnum("appengine.MemcacheIncrementResponse_IncrementStatusCode", MemcacheIncrementResponse_IncrementStatusCode_name, MemcacheIncrementResponse_IncrementStatusCode_value)
}

View File

@@ -1,165 +0,0 @@
syntax = "proto2";
option go_package = "memcache";
package appengine;
message MemcacheServiceError {
enum ErrorCode {
OK = 0;
UNSPECIFIED_ERROR = 1;
NAMESPACE_NOT_SET = 2;
PERMISSION_DENIED = 3;
INVALID_VALUE = 6;
}
}
message AppOverride {
required string app_id = 1;
optional int32 num_memcacheg_backends = 2 [deprecated=true];
optional bool ignore_shardlock = 3 [deprecated=true];
optional string memcache_pool_hint = 4 [deprecated=true];
optional bytes memcache_sharding_strategy = 5 [deprecated=true];
}
message MemcacheGetRequest {
repeated bytes key = 1;
optional string name_space = 2 [default = ""];
optional bool for_cas = 4;
optional AppOverride override = 5;
}
message MemcacheGetResponse {
repeated group Item = 1 {
required bytes key = 2;
required bytes value = 3;
optional fixed32 flags = 4;
optional fixed64 cas_id = 5;
optional int32 expires_in_seconds = 6;
}
}
message MemcacheSetRequest {
enum SetPolicy {
SET = 1;
ADD = 2;
REPLACE = 3;
CAS = 4;
}
repeated group Item = 1 {
required bytes key = 2;
required bytes value = 3;
optional fixed32 flags = 4;
optional SetPolicy set_policy = 5 [default = SET];
optional fixed32 expiration_time = 6 [default = 0];
optional fixed64 cas_id = 8;
optional bool for_cas = 9;
}
optional string name_space = 7 [default = ""];
optional AppOverride override = 10;
}
message MemcacheSetResponse {
enum SetStatusCode {
STORED = 1;
NOT_STORED = 2;
ERROR = 3;
EXISTS = 4;
}
repeated SetStatusCode set_status = 1;
}
message MemcacheDeleteRequest {
repeated group Item = 1 {
required bytes key = 2;
optional fixed32 delete_time = 3 [default = 0];
}
optional string name_space = 4 [default = ""];
optional AppOverride override = 5;
}
message MemcacheDeleteResponse {
enum DeleteStatusCode {
DELETED = 1;
NOT_FOUND = 2;
}
repeated DeleteStatusCode delete_status = 1;
}
message MemcacheIncrementRequest {
enum Direction {
INCREMENT = 1;
DECREMENT = 2;
}
required bytes key = 1;
optional string name_space = 4 [default = ""];
optional uint64 delta = 2 [default = 1];
optional Direction direction = 3 [default = INCREMENT];
optional uint64 initial_value = 5;
optional fixed32 initial_flags = 6;
optional AppOverride override = 7;
}
message MemcacheIncrementResponse {
enum IncrementStatusCode {
OK = 1;
NOT_CHANGED = 2;
ERROR = 3;
}
optional uint64 new_value = 1;
optional IncrementStatusCode increment_status = 2;
}
message MemcacheBatchIncrementRequest {
optional string name_space = 1 [default = ""];
repeated MemcacheIncrementRequest item = 2;
optional AppOverride override = 3;
}
message MemcacheBatchIncrementResponse {
repeated MemcacheIncrementResponse item = 1;
}
message MemcacheFlushRequest {
optional AppOverride override = 1;
}
message MemcacheFlushResponse {
}
message MemcacheStatsRequest {
optional AppOverride override = 1;
}
message MergedNamespaceStats {
required uint64 hits = 1;
required uint64 misses = 2;
required uint64 byte_hits = 3;
required uint64 items = 4;
required uint64 bytes = 5;
required fixed32 oldest_item_age = 6;
}
message MemcacheStatsResponse {
optional MergedNamespaceStats stats = 1;
}
message MemcacheGrabTailRequest {
required int32 item_count = 1;
optional string name_space = 2 [default = ""];
optional AppOverride override = 3;
}
message MemcacheGrabTailResponse {
repeated group Item = 1 {
required bytes value = 2;
optional fixed32 flags = 3;
}
}

View File

@@ -1,61 +0,0 @@
// Copyright 2014 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package internal
// This file has code for accessing metadata.
//
// References:
// https://cloud.google.com/compute/docs/metadata
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
)
const (
metadataHost = "metadata"
metadataPath = "/computeMetadata/v1/"
)
var (
metadataRequestHeaders = http.Header{
"X-Google-Metadata-Request": []string{"True"},
}
)
// TODO(dsymonds): Do we need to support default values, like Python?
func mustGetMetadata(key string) []byte {
b, err := getMetadata(key)
if err != nil {
log.Fatalf("Metadata fetch failed: %v", err)
}
return b
}
func getMetadata(key string) ([]byte, error) {
// TODO(dsymonds): May need to use url.Parse to support keys with query args.
req := &http.Request{
Method: "GET",
URL: &url.URL{
Scheme: "http",
Host: metadataHost,
Path: metadataPath + key,
},
Header: metadataRequestHeaders,
Host: metadataHost,
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("metadata server returned HTTP %d", resp.StatusCode)
}
return ioutil.ReadAll(resp.Body)
}

View File

@@ -1,374 +0,0 @@
// Code generated by protoc-gen-go.
// source: google.golang.org/appengine/internal/modules/modules_service.proto
// DO NOT EDIT!
/*
Package modules is a generated protocol buffer package.
It is generated from these files:
google.golang.org/appengine/internal/modules/modules_service.proto
It has these top-level messages:
ModulesServiceError
GetModulesRequest
GetModulesResponse
GetVersionsRequest
GetVersionsResponse
GetDefaultVersionRequest
GetDefaultVersionResponse
GetNumInstancesRequest
GetNumInstancesResponse
SetNumInstancesRequest
SetNumInstancesResponse
StartModuleRequest
StartModuleResponse
StopModuleRequest
StopModuleResponse
GetHostnameRequest
GetHostnameResponse
*/
package modules
import proto "github.com/golang/protobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
type ModulesServiceError_ErrorCode int32
const (
ModulesServiceError_OK ModulesServiceError_ErrorCode = 0
ModulesServiceError_INVALID_MODULE ModulesServiceError_ErrorCode = 1
ModulesServiceError_INVALID_VERSION ModulesServiceError_ErrorCode = 2
ModulesServiceError_INVALID_INSTANCES ModulesServiceError_ErrorCode = 3
ModulesServiceError_TRANSIENT_ERROR ModulesServiceError_ErrorCode = 4
ModulesServiceError_UNEXPECTED_STATE ModulesServiceError_ErrorCode = 5
)
var ModulesServiceError_ErrorCode_name = map[int32]string{
0: "OK",
1: "INVALID_MODULE",
2: "INVALID_VERSION",
3: "INVALID_INSTANCES",
4: "TRANSIENT_ERROR",
5: "UNEXPECTED_STATE",
}
var ModulesServiceError_ErrorCode_value = map[string]int32{
"OK": 0,
"INVALID_MODULE": 1,
"INVALID_VERSION": 2,
"INVALID_INSTANCES": 3,
"TRANSIENT_ERROR": 4,
"UNEXPECTED_STATE": 5,
}
func (x ModulesServiceError_ErrorCode) Enum() *ModulesServiceError_ErrorCode {
p := new(ModulesServiceError_ErrorCode)
*p = x
return p
}
func (x ModulesServiceError_ErrorCode) String() string {
return proto.EnumName(ModulesServiceError_ErrorCode_name, int32(x))
}
func (x *ModulesServiceError_ErrorCode) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(ModulesServiceError_ErrorCode_value, data, "ModulesServiceError_ErrorCode")
if err != nil {
return err
}
*x = ModulesServiceError_ErrorCode(value)
return nil
}
type ModulesServiceError struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *ModulesServiceError) Reset() { *m = ModulesServiceError{} }
func (m *ModulesServiceError) String() string { return proto.CompactTextString(m) }
func (*ModulesServiceError) ProtoMessage() {}
type GetModulesRequest struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *GetModulesRequest) Reset() { *m = GetModulesRequest{} }
func (m *GetModulesRequest) String() string { return proto.CompactTextString(m) }
func (*GetModulesRequest) ProtoMessage() {}
type GetModulesResponse struct {
Module []string `protobuf:"bytes,1,rep,name=module" json:"module,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetModulesResponse) Reset() { *m = GetModulesResponse{} }
func (m *GetModulesResponse) String() string { return proto.CompactTextString(m) }
func (*GetModulesResponse) ProtoMessage() {}
func (m *GetModulesResponse) GetModule() []string {
if m != nil {
return m.Module
}
return nil
}
type GetVersionsRequest struct {
Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetVersionsRequest) Reset() { *m = GetVersionsRequest{} }
func (m *GetVersionsRequest) String() string { return proto.CompactTextString(m) }
func (*GetVersionsRequest) ProtoMessage() {}
func (m *GetVersionsRequest) GetModule() string {
if m != nil && m.Module != nil {
return *m.Module
}
return ""
}
type GetVersionsResponse struct {
Version []string `protobuf:"bytes,1,rep,name=version" json:"version,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetVersionsResponse) Reset() { *m = GetVersionsResponse{} }
func (m *GetVersionsResponse) String() string { return proto.CompactTextString(m) }
func (*GetVersionsResponse) ProtoMessage() {}
func (m *GetVersionsResponse) GetVersion() []string {
if m != nil {
return m.Version
}
return nil
}
type GetDefaultVersionRequest struct {
Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetDefaultVersionRequest) Reset() { *m = GetDefaultVersionRequest{} }
func (m *GetDefaultVersionRequest) String() string { return proto.CompactTextString(m) }
func (*GetDefaultVersionRequest) ProtoMessage() {}
func (m *GetDefaultVersionRequest) GetModule() string {
if m != nil && m.Module != nil {
return *m.Module
}
return ""
}
type GetDefaultVersionResponse struct {
Version *string `protobuf:"bytes,1,req,name=version" json:"version,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetDefaultVersionResponse) Reset() { *m = GetDefaultVersionResponse{} }
func (m *GetDefaultVersionResponse) String() string { return proto.CompactTextString(m) }
func (*GetDefaultVersionResponse) ProtoMessage() {}
func (m *GetDefaultVersionResponse) GetVersion() string {
if m != nil && m.Version != nil {
return *m.Version
}
return ""
}
type GetNumInstancesRequest struct {
Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"`
Version *string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetNumInstancesRequest) Reset() { *m = GetNumInstancesRequest{} }
func (m *GetNumInstancesRequest) String() string { return proto.CompactTextString(m) }
func (*GetNumInstancesRequest) ProtoMessage() {}
func (m *GetNumInstancesRequest) GetModule() string {
if m != nil && m.Module != nil {
return *m.Module
}
return ""
}
func (m *GetNumInstancesRequest) GetVersion() string {
if m != nil && m.Version != nil {
return *m.Version
}
return ""
}
type GetNumInstancesResponse struct {
Instances *int64 `protobuf:"varint,1,req,name=instances" json:"instances,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetNumInstancesResponse) Reset() { *m = GetNumInstancesResponse{} }
func (m *GetNumInstancesResponse) String() string { return proto.CompactTextString(m) }
func (*GetNumInstancesResponse) ProtoMessage() {}
func (m *GetNumInstancesResponse) GetInstances() int64 {
if m != nil && m.Instances != nil {
return *m.Instances
}
return 0
}
type SetNumInstancesRequest struct {
Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"`
Version *string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"`
Instances *int64 `protobuf:"varint,3,req,name=instances" json:"instances,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *SetNumInstancesRequest) Reset() { *m = SetNumInstancesRequest{} }
func (m *SetNumInstancesRequest) String() string { return proto.CompactTextString(m) }
func (*SetNumInstancesRequest) ProtoMessage() {}
func (m *SetNumInstancesRequest) GetModule() string {
if m != nil && m.Module != nil {
return *m.Module
}
return ""
}
func (m *SetNumInstancesRequest) GetVersion() string {
if m != nil && m.Version != nil {
return *m.Version
}
return ""
}
func (m *SetNumInstancesRequest) GetInstances() int64 {
if m != nil && m.Instances != nil {
return *m.Instances
}
return 0
}
type SetNumInstancesResponse struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *SetNumInstancesResponse) Reset() { *m = SetNumInstancesResponse{} }
func (m *SetNumInstancesResponse) String() string { return proto.CompactTextString(m) }
func (*SetNumInstancesResponse) ProtoMessage() {}
type StartModuleRequest struct {
Module *string `protobuf:"bytes,1,req,name=module" json:"module,omitempty"`
Version *string `protobuf:"bytes,2,req,name=version" json:"version,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StartModuleRequest) Reset() { *m = StartModuleRequest{} }
func (m *StartModuleRequest) String() string { return proto.CompactTextString(m) }
func (*StartModuleRequest) ProtoMessage() {}
func (m *StartModuleRequest) GetModule() string {
if m != nil && m.Module != nil {
return *m.Module
}
return ""
}
func (m *StartModuleRequest) GetVersion() string {
if m != nil && m.Version != nil {
return *m.Version
}
return ""
}
type StartModuleResponse struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *StartModuleResponse) Reset() { *m = StartModuleResponse{} }
func (m *StartModuleResponse) String() string { return proto.CompactTextString(m) }
func (*StartModuleResponse) ProtoMessage() {}
type StopModuleRequest struct {
Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"`
Version *string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StopModuleRequest) Reset() { *m = StopModuleRequest{} }
func (m *StopModuleRequest) String() string { return proto.CompactTextString(m) }
func (*StopModuleRequest) ProtoMessage() {}
func (m *StopModuleRequest) GetModule() string {
if m != nil && m.Module != nil {
return *m.Module
}
return ""
}
func (m *StopModuleRequest) GetVersion() string {
if m != nil && m.Version != nil {
return *m.Version
}
return ""
}
type StopModuleResponse struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *StopModuleResponse) Reset() { *m = StopModuleResponse{} }
func (m *StopModuleResponse) String() string { return proto.CompactTextString(m) }
func (*StopModuleResponse) ProtoMessage() {}
type GetHostnameRequest struct {
Module *string `protobuf:"bytes,1,opt,name=module" json:"module,omitempty"`
Version *string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"`
Instance *string `protobuf:"bytes,3,opt,name=instance" json:"instance,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetHostnameRequest) Reset() { *m = GetHostnameRequest{} }
func (m *GetHostnameRequest) String() string { return proto.CompactTextString(m) }
func (*GetHostnameRequest) ProtoMessage() {}
func (m *GetHostnameRequest) GetModule() string {
if m != nil && m.Module != nil {
return *m.Module
}
return ""
}
func (m *GetHostnameRequest) GetVersion() string {
if m != nil && m.Version != nil {
return *m.Version
}
return ""
}
func (m *GetHostnameRequest) GetInstance() string {
if m != nil && m.Instance != nil {
return *m.Instance
}
return ""
}
type GetHostnameResponse struct {
Hostname *string `protobuf:"bytes,1,req,name=hostname" json:"hostname,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *GetHostnameResponse) Reset() { *m = GetHostnameResponse{} }
func (m *GetHostnameResponse) String() string { return proto.CompactTextString(m) }
func (*GetHostnameResponse) ProtoMessage() {}
func (m *GetHostnameResponse) GetHostname() string {
if m != nil && m.Hostname != nil {
return *m.Hostname
}
return ""
}
func init() {
proto.RegisterEnum("appengine.ModulesServiceError_ErrorCode", ModulesServiceError_ErrorCode_name, ModulesServiceError_ErrorCode_value)
}

View File

@@ -1,80 +0,0 @@
syntax = "proto2";
option go_package = "modules";
package appengine;
message ModulesServiceError {
enum ErrorCode {
OK = 0;
INVALID_MODULE = 1;
INVALID_VERSION = 2;
INVALID_INSTANCES = 3;
TRANSIENT_ERROR = 4;
UNEXPECTED_STATE = 5;
}
}
message GetModulesRequest {
}
message GetModulesResponse {
repeated string module = 1;
}
message GetVersionsRequest {
optional string module = 1;
}
message GetVersionsResponse {
repeated string version = 1;
}
message GetDefaultVersionRequest {
optional string module = 1;
}
message GetDefaultVersionResponse {
required string version = 1;
}
message GetNumInstancesRequest {
optional string module = 1;
optional string version = 2;
}
message GetNumInstancesResponse {
required int64 instances = 1;
}
message SetNumInstancesRequest {
optional string module = 1;
optional string version = 2;
required int64 instances = 3;
}
message SetNumInstancesResponse {}
message StartModuleRequest {
required string module = 1;
required string version = 2;
}
message StartModuleResponse {}
message StopModuleRequest {
optional string module = 1;
optional string version = 2;
}
message StopModuleResponse {}
message GetHostnameRequest {
optional string module = 1;
optional string version = 2;
optional string instance = 3;
}
message GetHostnameResponse {
required string hostname = 1;
}

View File

@@ -1,63 +0,0 @@
// Copyright 2014 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package internal
// This file implements a network dialer that limits the number of concurrent connections.
// It is only used for API calls.
import (
"log"
"net"
"runtime"
"sync"
"time"
)
var limitSem = make(chan int, 100) // TODO(dsymonds): Use environment variable.
func limitRelease() {
// non-blocking
select {
case <-limitSem:
default:
// This should not normally happen.
log.Print("appengine: unbalanced limitSem release!")
}
}
func limitDial(network, addr string) (net.Conn, error) {
limitSem <- 1
// Dial with a timeout in case the API host is MIA.
// The connection should normally be very fast.
conn, err := net.DialTimeout(network, addr, 500*time.Millisecond)
if err != nil {
limitRelease()
return nil, err
}
lc := &limitConn{Conn: conn}
runtime.SetFinalizer(lc, (*limitConn).Close) // shouldn't usually be required
return lc, nil
}
type limitConn struct {
mu sync.Mutex // only for closing the net.Conn
net.Conn
}
func (lc *limitConn) Close() error {
lc.mu.Lock()
defer lc.mu.Unlock()
if lc.Conn == nil {
// Silently ignore double close.
return nil
}
limitRelease()
err := lc.Conn.Close()
lc.Conn = nil
runtime.SetFinalizer(lc, nil)
return err
}

View File

@@ -1,55 +0,0 @@
// Copyright 2014 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package internal
import (
"sync"
"testing"
"time"
basepb "google.golang.org/appengine/internal/base"
)
func TestDialLimit(t *testing.T) {
// Fill up semaphore with false acquisitions to permit only two TCP connections at a time.
// We don't replace limitSem because that results in a data race when net/http lazily closes connections.
nFake := cap(limitSem) - 2
for i := 0; i < nFake; i++ {
limitSem <- 1
}
defer func() {
for i := 0; i < nFake; i++ {
<-limitSem
}
}()
f, c, cleanup := setup() // setup is in api_test.go
defer cleanup()
f.hang = make(chan int)
// If we make two RunSlowly RPCs (which will wait for f.hang to be strobed),
// then the simple Non200 RPC should hang.
var wg sync.WaitGroup
wg.Add(2)
for i := 0; i < 2; i++ {
go func() {
defer wg.Done()
c.Call("errors", "RunSlowly", &basepb.VoidProto{}, &basepb.VoidProto{}, nil)
}()
}
time.Sleep(50 * time.Millisecond) // let those two RPCs start
err := c.Call("errors", "Non200", &basepb.VoidProto{}, &basepb.VoidProto{}, &CallOptions{
Timeout: 50 * time.Millisecond,
})
if err != errTimeout {
t.Errorf("Non200 RPC returned with err %v, want errTimeout", err)
}
// Drain the two RunSlowly calls.
f.hang <- 1
f.hang <- 1
wg.Wait()
}

Some files were not shown because too many files have changed in this diff Show More