<commit_before>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// execprog executes a single program or a set of programs
// and optionally prints information about execution.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"
	"sync"
	"time"

	"github.com/google/syzkaller/pkg/cover"
	"github.com/google/syzkaller/pkg/csource"
	"github.com/google/syzkaller/pkg/host"
	"github.com/google/syzkaller/pkg/ipc"
	"github.com/google/syzkaller/pkg/ipc/ipcconfig"
	"github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/pkg/tool"
	"github.com/google/syzkaller/prog"
	_ "github.com/google/syzkaller/sys"
)

var (
	flagOS        = flag.String("os", runtime.GOOS, "target os")
	flagArch      = flag.String("arch", runtime.GOARCH, "target arch")
	flagCoverFile = flag.String("coverfile", "", "write coverage to the file")
	flagRepeat    = flag.Int("repeat", 1, "repeat execution that many times (0 for infinite loop)")
	flagProcs     = flag.Int("procs", 1, "number of parallel processes to execute programs")
	flagOutput    = flag.Bool("output", false, "write programs and results to stdout")
	flagHints     = flag.Bool("hints", false, "do a hints-generation run")
	flagEnable    = flag.String("enable", "none", "enable only listed additional features")
	flagDisable   = flag.String("disable", "none", "enable all additional features except listed")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage: execprog [flags] file-with-programs+\n")
		flag.PrintDefaults()
		csource.PrintAvailableFeaturesFlags()
	}
	defer tool.Init()()
	if len(flag.Args()) == 0 {
		flag.Usage()
		os.Exit(1)
	}
	featuresFlags, err := csource.ParseFeaturesFlags(*flagEnable, *flagDisable, true)
	if err != nil {
		log.Fatalf("%v", err)
	}

	target, err := prog.GetTarget(*flagOS, *flagArch)
	if err != nil {
		log.Fatalf("%v", err)
	}
	entries := loadPrograms(target, flag.Args())
	if len(entries) == 0 {
		return
	}
	features, err := host.Check(target)
	if err != nil {
		log.Fatalf("%v", err)
	}
	if *flagOutput {
		for _, feat := range features.Supported() {
			log.Logf(0, "%-24v: %v", feat.Name, feat.Reason)
		}
	}
	config, execOpts := createConfig(target, features, featuresFlags)
	if err = host.Setup(target, features, featuresFlags, config.Executor); err != nil {
		log.Fatal(err)
	}
	var gateCallback func()
	if features[host.FeatureLeak].Enabled {
		gateCallback = func() {
			output, err := osutil.RunCmd(10*time.Minute, "", config.Executor, "leak")
			if err != nil {
				os.Stdout.Write(output)
				os.Exit(1)
			}
		}
	}
	ctx := &Context{
		entries:  entries,
		config:   config,
		execOpts: execOpts,
		gate:     ipc.NewGate(2**flagProcs, gateCallback),
		shutdown: make(chan struct{}),
		repeat:   *flagRepeat,
	}
	var wg sync.WaitGroup
	wg.Add(*flagProcs)
	for p := 0; p < *flagProcs; p++ {
		pid := p
		go func() {
			defer wg.Done()
			ctx.run(pid)
		}()
	}
	osutil.HandleInterrupts(ctx.shutdown)
	wg.Wait()
}

type Context struct {
	entries   []*prog.LogEntry
	config    *ipc.Config
	execOpts  *ipc.ExecOpts
	gate      *ipc.Gate
	shutdown  chan struct{}
	logMu     sync.Mutex
	posMu     sync.Mutex
	repeat    int
	pos       int
	lastPrint time.Time
}

func (ctx *Context) run(pid int) {
	env, err := ipc.MakeEnv(ctx.config, pid)
	if err != nil {
		log.Fatalf("failed to create ipc env: %v", err)
	}
	defer env.Close()
	for {
		select {
		case <-ctx.shutdown:
			return
		default:
		}
		idx := ctx.getProgramIndex()
		if ctx.repeat > 0 && idx >= len(ctx.entries)*ctx.repeat {
			return
		}
		entry := ctx.entries[idx%len(ctx.entries)]
		ctx.execute(pid, env, entry)
	}
}

func (ctx *Context) execute(pid int, env *ipc.Env, entry *prog.LogEntry) {
	// Limit concurrency window.
	ticket := ctx.gate.Enter()
	defer ctx.gate.Leave(ticket)

	callOpts := ctx.execOpts
	if *flagOutput {
		ctx.logProgram(pid, entry.P, callOpts)
	}
	// This mimics the syz-fuzzer logic. This is important for reproduction.
	for try := 0; ; try++ {
		output, info, hanged, err := env.Exec(callOpts, entry.P)
		if err != nil && err != prog.ErrExecBufferTooSmall {
			if try > 10 {
				log.Fatalf("executor failed %v times: %v\n%s", try, err, output)
			}
			// Don't print err/output in this case as it may contain "SYZFAIL" and we want to fail yet.
			log.Logf(1, "executor failed, retrying")
			time.Sleep(time.Second)
			continue
		}
		if ctx.config.Flags&ipc.FlagDebug != 0 || err != nil {
			log.Logf(0, "result: hanged=%v err=%v\n\n%s", hanged, err, output)
		}
		if info != nil {
			ctx.printCallResults(info)
			if *flagHints {
				ctx.printHints(entry.P, info)
			}
			if *flagCoverFile != "" {
				ctx.dumpCoverage(*flagCoverFile, info)
			}
		} else {
			log.Logf(1, "RESULT: no calls executed")
		}
		break
	}
}

func (ctx *Context) logProgram(pid int, p *prog.Prog, callOpts *ipc.ExecOpts) {
	data := p.Serialize()
	ctx.logMu.Lock()
	log.Logf(0, "executing program %v:\n%s", pid, data)
	ctx.logMu.Unlock()
}

func (ctx *Context) printCallResults(info *ipc.ProgInfo) {
	for i, inf := range info.Calls {
		if inf.Flags&ipc.CallExecuted == 0 {
			continue
		}
		flags := ""
		if inf.Flags&ipc.CallFinished == 0 {
			flags += " unfinished"
		}
		if inf.Flags&ipc.CallBlocked != 0 {
			flags += " blocked"
		}
		if inf.Flags&ipc.CallFaultInjected != 0 {
			flags += " faulted"
		}
		log.Logf(1, "CALL %v: signal %v, coverage %v errno %v%v",
			i, len(inf.Signal), len(inf.Cover), inf.Errno, flags)
	}
}

func (ctx *Context) printHints(p *prog.Prog, info *ipc.ProgInfo) {
	ncomps, ncandidates := 0, 0
	for i := range p.Calls {
		if *flagOutput {
			fmt.Printf("call %v:\n", i)
		}
		comps := info.Calls[i].Comps
		for v, args := range comps {
			ncomps += len(args)
			if *flagOutput {
				fmt.Printf("comp 0x%x:", v)
				for arg := range args {
					fmt.Printf(" 0x%x", arg)
				}
				fmt.Printf("\n")
			}
		}
		p.MutateWithHints(i, comps, func(p *prog.Prog) {
			ncandidates++
			if *flagOutput {
				log.Logf(1, "PROGRAM:\n%s", p.Serialize())
			}
		})
	}
	log.Logf(0, "ncomps=%v ncandidates=%v", ncomps, ncandidates)
}

func (ctx *Context) dumpCallCoverage(coverFile string, info *ipc.CallInfo) {
	if len(info.Cover) == 0 {
		return
	}
	buf := new(bytes.Buffer)
	for _, pc := range info.Cover {
		fmt.Fprintf(buf, "0x%x\n", cover.RestorePC(pc, 0xffffffff))
	}
	err := osutil.WriteFile(coverFile, buf.Bytes())
	if err != nil {
		log.Fatalf("failed to write coverage file: %v", err)
	}
}

func (ctx *Context) dumpCoverage(coverFile string, info *ipc.ProgInfo) {
	for i, inf := range info.Calls {
		log.Logf(0, "call #%v: signal %v, coverage %v", i, len(inf.Signal), len(inf.Cover))
		ctx.dumpCallCoverage(fmt.Sprintf("%v.%v", coverFile, i), &inf)
	}
	log.Logf(0, "extra: signal %v, coverage %v", len(info.Extra.Signal), len(info.Extra.Cover))
	ctx.dumpCallCoverage(fmt.Sprintf("%v.extra", coverFile), &info.Extra)
}

func (ctx *Context) getProgramIndex() int {
	ctx.posMu.Lock()
	idx := ctx.pos
	ctx.pos++
	if idx%len(ctx.entries) == 0 && time.Since(ctx.lastPrint) > 5*time.Second {
		log.Logf(0, "executed programs: %v", idx)
		ctx.lastPrint = time.Now()
	}
	ctx.posMu.Unlock()
	return idx
}

func loadPrograms(target *prog.Target, files []string) []*prog.LogEntry {
	var entries []*prog.LogEntry
	for _, fn := range files {
		data, err := ioutil.ReadFile(fn)
		if err != nil {
			log.Fatalf("failed to read log file: %v", err)
		}
		entries = append(entries, target.ParseLog(data)...)
	}
	log.Logf(0, "parsed %v programs", len(entries))
	return entries
}

func createConfig(target *prog.Target, features *host.Features, featuresFlags csource.Features) (
	*ipc.Config, *ipc.ExecOpts) {
	config, execOpts, err := ipcconfig.Default(target)
	if err != nil {
		log.Fatalf("%v", err)
	}
	if config.Flags&ipc.FlagSignal != 0 {
		execOpts.Flags |= ipc.FlagCollectCover
	}
	if *flagCoverFile != "" {
		config.Flags |= ipc.FlagSignal
		execOpts.Flags |= ipc.FlagCollectCover
		execOpts.Flags &^= ipc.FlagDedupCover
	}
	if *flagHints {
		if execOpts.Flags&ipc.FlagCollectCover != 0 {
			execOpts.Flags ^= ipc.FlagCollectCover
		}
		execOpts.Flags |= ipc.FlagCollectComps
	}
	if features[host.FeatureExtraCoverage].Enabled {
		config.Flags |= ipc.FlagExtraCover
	}
	if featuresFlags["tun"].Enabled && features[host.FeatureNetInjection].Enabled {
		config.Flags |= ipc.FlagEnableTun
	}
	if featuresFlags["net_dev"].Enabled && features[host.FeatureNetDevices].Enabled {
		config.Flags |= ipc.FlagEnableNetDev
	}
	if featuresFlags["net_reset"].Enabled {
		config.Flags |= ipc.FlagEnableNetReset
	}
	if featuresFlags["cgroups"].Enabled {
		config.Flags |= ipc.FlagEnableCgroups
	}
	if featuresFlags["close_fds"].Enabled {
		config.Flags |= ipc.FlagEnableCloseFds
	}
	if featuresFlags["devlink_pci"].Enabled && features[host.FeatureDevlinkPCI].Enabled {
		config.Flags |= ipc.FlagEnableDevlinkPCI
	}
	if featuresFlags["vhci"].Enabled && features[host.FeatureVhciInjection].Enabled {
		config.Flags |= ipc.FlagEnableVhciInjection
	}
	if featuresFlags["wifi"].Enabled && features[host.FeatureWifiEmulation].Enabled {
		config.Flags |= ipc.FlagEnableWifi
	}
	return config, execOpts
}
<commit_msg>tools/syz-execprog: don't store prog.LogEntry's<commit_after>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// execprog executes a single program or a set of programs
// and optionally prints information about execution.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"
	"sync"
	"time"

	"github.com/google/syzkaller/pkg/cover"
	"github.com/google/syzkaller/pkg/csource"
	"github.com/google/syzkaller/pkg/host"
	"github.com/google/syzkaller/pkg/ipc"
	"github.com/google/syzkaller/pkg/ipc/ipcconfig"
	"github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/pkg/tool"
	"github.com/google/syzkaller/prog"
	_ "github.com/google/syzkaller/sys"
)

var (
	flagOS        = flag.String("os", runtime.GOOS, "target os")
	flagArch      = flag.String("arch", runtime.GOARCH, "target arch")
	flagCoverFile = flag.String("coverfile", "", "write coverage to the file")
	flagRepeat    = flag.Int("repeat", 1, "repeat execution that many times (0 for infinite loop)")
	flagProcs     = flag.Int("procs", 1, "number of parallel processes to execute programs")
	flagOutput    = flag.Bool("output", false, "write programs and results to stdout")
	flagHints     = flag.Bool("hints", false, "do a hints-generation run")
	flagEnable    = flag.String("enable", "none", "enable only listed additional features")
	flagDisable   = flag.String("disable", "none", "enable all additional features except listed")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage: execprog [flags] file-with-programs+\n")
		flag.PrintDefaults()
		csource.PrintAvailableFeaturesFlags()
	}
	defer tool.Init()()
	if len(flag.Args()) == 0 {
		flag.Usage()
		os.Exit(1)
	}
	featuresFlags, err := csource.ParseFeaturesFlags(*flagEnable, *flagDisable, true)
	if err != nil {
		log.Fatalf("%v", err)
	}

	target, err := prog.GetTarget(*flagOS, *flagArch)
	if err != nil {
		log.Fatalf("%v", err)
	}
	progs := loadPrograms(target, flag.Args())
	if len(progs) == 0 {
		return
	}
	features, err := host.Check(target)
	if err != nil {
		log.Fatalf("%v", err)
	}
	if *flagOutput {
		for _, feat := range features.Supported() {
			log.Logf(0, "%-24v: %v", feat.Name, feat.Reason)
		}
	}
	config, execOpts := createConfig(target, features, featuresFlags)
	if err = host.Setup(target, features, featuresFlags, config.Executor); err != nil {
		log.Fatal(err)
	}
	var gateCallback func()
	if features[host.FeatureLeak].Enabled {
		gateCallback = func() {
			output, err := osutil.RunCmd(10*time.Minute, "", config.Executor, "leak")
			if err != nil {
				os.Stdout.Write(output)
				os.Exit(1)
			}
		}
	}
	ctx := &Context{
		progs:    progs,
		config:   config,
		execOpts: execOpts,
		gate:     ipc.NewGate(2**flagProcs, gateCallback),
		shutdown: make(chan struct{}),
		repeat:   *flagRepeat,
	}
	var wg sync.WaitGroup
	wg.Add(*flagProcs)
	for p := 0; p < *flagProcs; p++ {
		pid := p
		go func() {
			defer wg.Done()
			ctx.run(pid)
		}()
	}
	osutil.HandleInterrupts(ctx.shutdown)
	wg.Wait()
}

type Context struct {
	progs     []*prog.Prog
	config    *ipc.Config
	execOpts  *ipc.ExecOpts
	gate      *ipc.Gate
	shutdown  chan struct{}
	logMu     sync.Mutex
	posMu     sync.Mutex
	repeat    int
	pos       int
	lastPrint time.Time
}

func (ctx *Context) run(pid int) {
	env, err := ipc.MakeEnv(ctx.config, pid)
	if err != nil {
		log.Fatalf("failed to create ipc env: %v", err)
	}
	defer env.Close()
	for {
		select {
		case <-ctx.shutdown:
			return
		default:
		}
		idx := ctx.getProgramIndex()
		if ctx.repeat > 0 && idx >= len(ctx.progs)*ctx.repeat {
			return
		}
		entry := ctx.progs[idx%len(ctx.progs)]
		ctx.execute(pid, env, entry)
	}
}

func (ctx *Context) execute(pid int, env *ipc.Env, p *prog.Prog) {
	// Limit concurrency window.
	ticket := ctx.gate.Enter()
	defer ctx.gate.Leave(ticket)

	callOpts := ctx.execOpts
	if *flagOutput {
		ctx.logProgram(pid, p, callOpts)
	}
	// This mimics the syz-fuzzer logic. This is important for reproduction.
	for try := 0; ; try++ {
		output, info, hanged, err := env.Exec(callOpts, p)
		if err != nil && err != prog.ErrExecBufferTooSmall {
			if try > 10 {
				log.Fatalf("executor failed %v times: %v\n%s", try, err, output)
			}
			// Don't print err/output in this case as it may contain "SYZFAIL" and we want to fail yet.
			log.Logf(1, "executor failed, retrying")
			time.Sleep(time.Second)
			continue
		}
		if ctx.config.Flags&ipc.FlagDebug != 0 || err != nil {
			log.Logf(0, "result: hanged=%v err=%v\n\n%s", hanged, err, output)
		}
		if info != nil {
			ctx.printCallResults(info)
			if *flagHints {
				ctx.printHints(p, info)
			}
			if *flagCoverFile != "" {
				ctx.dumpCoverage(*flagCoverFile, info)
			}
		} else {
			log.Logf(1, "RESULT: no calls executed")
		}
		break
	}
}

func (ctx *Context) logProgram(pid int, p *prog.Prog, callOpts *ipc.ExecOpts) {
	data := p.Serialize()
	ctx.logMu.Lock()
	log.Logf(0, "executing program %v:\n%s", pid, data)
	ctx.logMu.Unlock()
}

func (ctx *Context) printCallResults(info *ipc.ProgInfo) {
	for i, inf := range info.Calls {
		if inf.Flags&ipc.CallExecuted == 0 {
			continue
		}
		flags := ""
		if inf.Flags&ipc.CallFinished == 0 {
			flags += " unfinished"
		}
		if inf.Flags&ipc.CallBlocked != 0 {
			flags += " blocked"
		}
		if inf.Flags&ipc.CallFaultInjected != 0 {
			flags += " faulted"
		}
		log.Logf(1, "CALL %v: signal %v, coverage %v errno %v%v",
			i, len(inf.Signal), len(inf.Cover), inf.Errno, flags)
	}
}

func (ctx *Context) printHints(p *prog.Prog, info *ipc.ProgInfo) {
	ncomps, ncandidates := 0, 0
	for i := range p.Calls {
		if *flagOutput {
			fmt.Printf("call %v:\n", i)
		}
		comps := info.Calls[i].Comps
		for v, args := range comps {
			ncomps += len(args)
			if *flagOutput {
				fmt.Printf("comp 0x%x:", v)
				for arg := range args {
					fmt.Printf(" 0x%x", arg)
				}
				fmt.Printf("\n")
			}
		}
		p.MutateWithHints(i, comps, func(p *prog.Prog) {
			ncandidates++
			if *flagOutput {
				log.Logf(1, "PROGRAM:\n%s", p.Serialize())
			}
		})
	}
	log.Logf(0, "ncomps=%v ncandidates=%v", ncomps, ncandidates)
}

func (ctx *Context) dumpCallCoverage(coverFile string, info *ipc.CallInfo) {
	if len(info.Cover) == 0 {
		return
	}
	buf := new(bytes.Buffer)
	for _, pc := range info.Cover {
		fmt.Fprintf(buf, "0x%x\n", cover.RestorePC(pc, 0xffffffff))
	}
	err := osutil.WriteFile(coverFile, buf.Bytes())
	if err != nil {
		log.Fatalf("failed to write coverage file: %v", err)
	}
}

func (ctx *Context) dumpCoverage(coverFile string, info *ipc.ProgInfo) {
	for i, inf := range info.Calls {
		log.Logf(0, "call #%v: signal %v, coverage %v", i, len(inf.Signal), len(inf.Cover))
		ctx.dumpCallCoverage(fmt.Sprintf("%v.%v", coverFile, i), &inf)
	}
	log.Logf(0, "extra: signal %v, coverage %v", len(info.Extra.Signal), len(info.Extra.Cover))
	ctx.dumpCallCoverage(fmt.Sprintf("%v.extra", coverFile), &info.Extra)
}

func (ctx *Context) getProgramIndex() int {
	ctx.posMu.Lock()
	idx := ctx.pos
	ctx.pos++
	if idx%len(ctx.progs) == 0 && time.Since(ctx.lastPrint) > 5*time.Second {
		log.Logf(0, "executed programs: %v", idx)
		ctx.lastPrint = time.Now()
	}
	ctx.posMu.Unlock()
	return idx
}

func loadPrograms(target *prog.Target, files []string) []*prog.Prog {
	var progs []*prog.Prog
	for _, fn := range files {
		data, err := ioutil.ReadFile(fn)
		if err != nil {
			log.Fatalf("failed to read log file: %v", err)
		}
		for _, entry := range target.ParseLog(data) {
			progs = append(progs, entry.P)
		}
	}
	log.Logf(0, "parsed %v programs", len(progs))
	return progs
}

func createConfig(target *prog.Target, features *host.Features, featuresFlags csource.Features) (
	*ipc.Config, *ipc.ExecOpts) {
	config, execOpts, err := ipcconfig.Default(target)
	if err != nil {
		log.Fatalf("%v", err)
	}
	if config.Flags&ipc.FlagSignal != 0 {
		execOpts.Flags |= ipc.FlagCollectCover
	}
	if *flagCoverFile != "" {
		config.Flags |= ipc.FlagSignal
		execOpts.Flags |= ipc.FlagCollectCover
		execOpts.Flags &^= ipc.FlagDedupCover
	}
	if *flagHints {
		if execOpts.Flags&ipc.FlagCollectCover != 0 {
			execOpts.Flags ^= ipc.FlagCollectCover
		}
		execOpts.Flags |= ipc.FlagCollectComps
	}
	if features[host.FeatureExtraCoverage].Enabled {
		config.Flags |= ipc.FlagExtraCover
	}
	if featuresFlags["tun"].Enabled && features[host.FeatureNetInjection].Enabled {
		config.Flags |= ipc.FlagEnableTun
	}
	if featuresFlags["net_dev"].Enabled && features[host.FeatureNetDevices].Enabled {
		config.Flags |= ipc.FlagEnableNetDev
	}
	if featuresFlags["net_reset"].Enabled {
		config.Flags |= ipc.FlagEnableNetReset
	}
	if featuresFlags["cgroups"].Enabled {
		config.Flags |= ipc.FlagEnableCgroups
	}
	if featuresFlags["close_fds"].Enabled {
		config.Flags |= ipc.FlagEnableCloseFds
	}
	if featuresFlags["devlink_pci"].Enabled && features[host.FeatureDevlinkPCI].Enabled {
		config.Flags |= ipc.FlagEnableDevlinkPCI
	}
	if featuresFlags["vhci"].Enabled && features[host.FeatureVhciInjection].Enabled {
		config.Flags |= ipc.FlagEnableVhciInjection
	}
	if featuresFlags["wifi"].Enabled && features[host.FeatureWifiEmulation].Enabled {
		config.Flags |= ipc.FlagEnableWifi
	}
	return config, execOpts
}
<|endoftext|>


<commit_before>/*
 * Copyright 2018 IBM Corp.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 k8s

import (
	"github.com/skydive-project/skydive/logging"
	"github.com/skydive-project/skydive/probe"
	"github.com/skydive-project/skydive/topology"
	"github.com/skydive-project/skydive/topology/graph"
)

// ClusterName is the name of the k8s cluster
const ClusterName = "cluster"

var clusterEventHandler = graph.NewEventHandler(100)

type clusterCache struct {
	*graph.EventHandler
	graph *graph.Graph
}

func (c *clusterCache) Start() {
	c.graph.Lock()
	defer c.graph.Unlock()

	m := NewMetadata(Manager, "cluster", nil, ClusterName)
	node := c.graph.NewNode(graph.GenID(), m, "")
	c.NotifyEvent(graph.NodeAdded, node)
	logging.GetLogger().Debugf("Added cluster{Name: %s}", ClusterName)
}

func (c *clusterCache) Stop() {
}

func newClusterProbe(clientset interface{}, g *graph.Graph) Subprobe {
	return &clusterCache{
		EventHandler: clusterEventHandler,
		graph:        g,
	}
}

func newClusterLinker(g *graph.Graph, manager string, types ...string) probe.Probe {
	clusterIndexer := graph.NewMetadataIndexer(g, clusterEventHandler, graph.Metadata{"Manager": Manager, "Type": "cluster"})
	clusterIndexer.Start()

	objectFilter := newTypesFilter(manager, types...)
	objectIndexer := newObjectIndexerFromFilter(g, g, objectFilter)
	objectIndexer.Start()

	return graph.NewMetadataIndexerLinker(g, clusterIndexer, objectIndexer, topology.OwnershipMetadata())
}
<commit_msg>k8s: new cluster linker<commit_after>/*
 * Copyright 2018 IBM Corp.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 k8s

import (
	"github.com/skydive-project/skydive/logging"
	"github.com/skydive-project/skydive/probe"
	"github.com/skydive-project/skydive/topology"
	"github.com/skydive-project/skydive/topology/graph"
)

// ClusterName is the name of the k8s cluster
const ClusterName = "cluster"

var clusterEventHandler = graph.NewEventHandler(100)

var clusterNode *graph.Node

type clusterCache struct {
	*graph.EventHandler
	graph *graph.Graph
}

func (c *clusterCache) addClusterNode() {
	c.graph.Lock()
	defer c.graph.Unlock()

	m := NewMetadata(Manager, "cluster", nil, ClusterName)
	clusterNode = c.graph.NewNode(graph.GenID(), m, "")
	c.NotifyEvent(graph.NodeAdded, clusterNode)
	logging.GetLogger().Debugf("Added cluster{Name: %s}", ClusterName)
}

func (c *clusterCache) Start() {
}

func (c *clusterCache) Stop() {
}

func newClusterProbe(clientset interface{}, g *graph.Graph) Subprobe {
	c := &clusterCache{
		EventHandler: clusterEventHandler,
		graph:        g,
	}
	c.addClusterNode()
	return c
}

type clusterLinker struct {
	graph.DefaultLinker
	*graph.ResourceLinker
	g             *graph.Graph
	objectIndexer *graph.MetadataIndexer
}

func (linker *clusterLinker) createEdge(cluster, object *graph.Node) *graph.Edge {
	id := graph.GenID(string(cluster.ID), string(object.ID))
	return linker.g.CreateEdge(id, cluster, object, topology.OwnershipMetadata(), graph.TimeUTC(), "")
}

// GetBALinks returns all the incoming links for a node
func (linker *clusterLinker) GetBALinks(objectNode *graph.Node) (edges []*graph.Edge) {
	edges = append(edges, linker.createEdge(clusterNode, objectNode))
	return
}

func newClusterLinker(g *graph.Graph, manager string, types ...string) probe.Probe {
	objectIndexer := newObjectIndexerFromFilter(g, g, newTypesFilter(manager, types...))
	objectIndexer.Start()

	return graph.NewResourceLinker(
		g,
		nil,
		objectIndexer,
		&clusterLinker{g: g},
		topology.OwnershipMetadata(),
	)
}
<|endoftext|>


<commit_before>package excelize

import "encoding/xml"

// xmlxWorkbookRels contains xmlxWorkbookRelations which maps sheet id and sheet XML.
type xlsxWorkbookRels struct {
	XMLName       xml.Name               `xml:"http://schemas.openxmlformats.org/package/2006/relationships Relationships"`
	Relationships []xlsxWorkbookRelation `xml:"Relationship"`
}

// xmlxWorkbookRelation maps sheet id and xl/worksheets/_rels/sheet%d.xml.rels
type xlsxWorkbookRelation struct {
	ID         string `xml:"Id,attr"`
	Target     string `xml:",attr"`
	Type       string `xml:",attr"`
	TargetMode string `xml:",attr,omitempty"`
}

// xlsxWorkbook directly maps the workbook element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.
type xlsxWorkbook struct {
	XMLName            xml.Name                `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main workbook"`
	FileVersion        *xlsxFileVersion        `xml:"fileVersion"`
	WorkbookPr         *xlsxWorkbookPr         `xml:"workbookPr"`
	WorkbookProtection *xlsxWorkbookProtection `xml:"workbookProtection"`
	BookViews          xlsxBookViews           `xml:"bookViews"`
	Sheets             xlsxSheets              `xml:"sheets"`
	ExternalReferences *xlsxExternalReferences `xml:"externalReferences"`
	DefinedNames       *xlsxDefinedNames       `xml:"definedNames"`
	CalcPr             *xlsxCalcPr             `xml:"calcPr"`
	PivotCaches        *xlsxPivotCaches        `xml:"pivotCaches"`
	ExtLst             *xlsxExtLst             `xml:"extLst"`
	FileRecoveryPr     *xlsxFileRecoveryPr     `xml:"fileRecoveryPr"`
}

// xlsxFileRecoveryPr maps sheet recovery information. This element defines
// properties that track the state of the workbook file, such as whether the
// file was saved during a crash, or whether it should be opened in auto-recover
// mode.
type xlsxFileRecoveryPr struct {
	AutoRecover     bool `xml:"autoRecover,attr,omitempty"`
	CrashSave       bool `xml:"crashSave,attr,omitempty"`
	DataExtractLoad bool `xml:"dataExtractLoad,attr,omitempty"`
	RepairLoad      bool `xml:"repairLoad,attr,omitempty"`
}

// xlsxWorkbookProtection directly maps the workbookProtection element. This
// element specifies options for protecting data in the workbook. Applications
// might use workbook protection to prevent anyone from accidentally changing,
// moving, or deleting important data. This protection can be ignored by
// applications which choose not to support this optional protection mechanism.
// When a password is to be hashed and stored in this element, it shall be
// hashed as defined below, starting from a UTF-16LE encoded string value. If
// there is a leading BOM character (U+FEFF) in the encoded password it is
// removed before hash calculation.
type xlsxWorkbookProtection struct {
	LockRevision           bool   `xml:"lockRevision,attr,omitempty"`
	LockStructure          bool   `xml:"lockStructure,attr,omitempty"`
	LockWindows            bool   `xml:"lockWindows,attr,omitempty"`
	RevisionsAlgorithmName string `xml:"revisionsAlgorithmName,attr,omitempty"`
	RevisionsHashValue     string `xml:"revisionsHashValue,attr,omitempty"`
	RevisionsSaltValue     string `xml:"revisionsSaltValue,attr,omitempty"`
	RevisionsSpinCount     int    `xml:"revisionsSpinCount,attr,omitempty"`
	WorkbookAlgorithmName  string `xml:"workbookAlgorithmName,attr,omitempty"`
	WorkbookHashValue      string `xml:"workbookHashValue,attr,omitempty"`
	WorkbookSaltValue      string `xml:"workbookSaltValue,attr,omitempty"`
	WorkbookSpinCount      int    `xml:"workbookSpinCount,attr,omitempty"`
}

// xlsxFileVersion directly maps the fileVersion element. This element defines
// properties that track which version of the application accessed the data and
// source code contained in the file.
type xlsxFileVersion struct {
	AppName      string `xml:"appName,attr,omitempty"`
	CodeName     string `xml:"codeName,attr,omitempty"`
	LastEdited   string `xml:"lastEdited,attr,omitempty"`
	LowestEdited string `xml:"lowestEdited,attr,omitempty"`
	RupBuild     string `xml:"rupBuild,attr,omitempty"`
}

// xlsxWorkbookPr directly maps the workbookPr element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main This element
// defines a collection of workbook properties.
type xlsxWorkbookPr struct {
	AllowRefreshQuery          bool   `xml:"allowRefreshQuery,attr,omitempty"`
	AutoCompressPictures       bool   `xml:"autoCompressPictures,attr,omitempty"`
	BackupFile                 bool   `xml:"backupFile,attr,omitempty"`
	CheckCompatibility         bool   `xml:"checkCompatibility,attr,omitempty"`
	CodeName                   string `xml:"codeName,attr,omitempty"`
	Date1904                   bool   `xml:"date1904,attr,omitempty"`
	DefaultThemeVersion        string `xml:"defaultThemeVersion,attr,omitempty"`
	FilterPrivacy              bool   `xml:"filterPrivacy,attr,omitempty"`
	HidePivotFieldList         bool   `xml:"hidePivotFieldList,attr,omitempty"`
	PromptedSolutions          bool   `xml:"promptedSolutions,attr,omitempty"`
	PublishItems               bool   `xml:"publishItems,attr,omitempty"`
	RefreshAllConnections      bool   `xml:"refreshAllConnections,attr,omitempty"`
	SaveExternalLinkValues     bool   `xml:"saveExternalLinkValues,attr,omitempty"`
	ShowBorderUnselectedTables bool   `xml:"showBorderUnselectedTables,attr,omitempty"`
	ShowInkAnnotation          bool   `xml:"showInkAnnotation,attr,omitempty"`
	ShowObjects                string `xml:"showObjects,attr,omitempty"`
	ShowPivotChartFilter       bool   `xml:"showPivotChartFilter,attr,omitempty"`
	UpdateLinks                string `xml:"updateLinks,attr,omitempty"`
}

// xlsxBookViews directly maps the bookViews element. This element specifies the
// collection of workbook views of the enclosing workbook. Each view can specify
// a window position, filter options, and other configurations. There is no
// limit on the number of workbook views that can be defined for a workbook.
type xlsxBookViews struct {
	WorkBookView []xlsxWorkBookView `xml:"workbookView"`
}

// xlsxWorkBookView directly maps the workbookView element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main This element
// specifies a single Workbook view.
type xlsxWorkBookView struct {
	ActiveTab              int    `xml:"activeTab,attr,omitempty"`
	AutoFilterDateGrouping bool   `xml:"autoFilterDateGrouping,attr,omitempty"`
	FirstSheet             int    `xml:"firstSheet,attr,omitempty"`
	Minimized              bool   `xml:"minimized,attr,omitempty"`
	ShowHorizontalScroll   bool   `xml:"showHorizontalScroll,attr,omitempty"`
	ShowSheetTabs          bool   `xml:"showSheetTabs,attr,omitempty"`
	ShowVerticalScroll     bool   `xml:"showVerticalScroll,attr,omitempty"`
	TabRatio               int    `xml:"tabRatio,attr,omitempty"`
	Visibility             string `xml:"visibility,attr,omitempty"`
	WindowHeight           int    `xml:"windowHeight,attr,omitempty"`
	WindowWidth            int    `xml:"windowWidth,attr,omitempty"`
	XWindow                string `xml:"xWindow,attr,omitempty"`
	YWindow                string `xml:"yWindow,attr,omitempty"`
}

// xlsxSheets directly maps the sheets element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main.
type xlsxSheets struct {
	Sheet []xlsxSheet `xml:"sheet"`
}

// xlsxSheet directly maps the sheet element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.
type xlsxSheet struct {
	Name    string `xml:"name,attr,omitempty"`
	SheetID string `xml:"sheetId,attr,omitempty"`
	ID      string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
	State   string `xml:"state,attr,omitempty"`
}

// xlsxExternalReferences directly maps the externalReferences element of the
// external workbook references part.
type xlsxExternalReferences struct {
	ExternalReference []xlsxExternalReference `xml:"externalReference"`
}

// xlsxExternalReference directly maps the externalReference element of the
// external workbook references part.
type xlsxExternalReference struct {
	RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}

// xlsxPivotCaches element enumerates pivot cache definition parts used by pivot
// tables and formulas in this workbook.
type xlsxPivotCaches struct {
	PivotCache []xlsxPivotCache `xml:"pivotCache"`
}

// xlsxPivotCache directly maps the pivotCache element.
type xlsxPivotCache struct {
	CacheID int    `xml:"cacheId,attr,omitempty"`
	RID     string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}

// extLst element provides a convention for extending spreadsheetML in
// predefined locations. The locations shall be denoted with the extLst element,
// and are called extension lists. Extension list locations within the markup
// document are specified in the markup specification and can be used to store
// extensions to the markup specification, whether those are future version
// extensions of the markup specification or are private extensions implemented
// independently from the markup specification. Markup within an extension might
// not be understood by a consumer.
type xlsxExtLst struct {
	Ext string `xml:",innerxml"`
}

// xlsxDefinedNames directly maps the definedNames element. This element defines
// the collection of defined names for this workbook. Defined names are
// descriptive names to represent cells, ranges of cells, formulas, or constant
// values. Defined names can be used to represent a range on any worksheet.
type xlsxDefinedNames struct {
	DefinedName []xlsxDefinedName `xml:"definedName"`
}

// xlsxDefinedName directly maps the definedName element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main This element
// defines a defined name within this workbook. A defined name is descriptive
// text that is used to represents a cell, range of cells, formula, or constant
// value. For a descriptions of the attributes see https://msdn.microsoft.com/en-us/library/office/documentformat.openxml.spreadsheet.definedname.aspx
type xlsxDefinedName struct {
	Comment           string `xml:"comment,attr,omitempty"`
	CustomMenu        string `xml:"customMenu,attr,omitempty"`
	Description       string `xml:"description,attr,omitempty"`
	Function          bool   `xml:"function,attr,omitempty"`
	FunctionGroupID   int    `xml:"functionGroupId,attr,omitempty"`
	Help              string `xml:"help,attr,omitempty"`
	Hidden            bool   `xml:"hidden,attr,omitempty"`
	LocalSheetID      *int   `xml:"localSheetId,attr"`
	Name              string `xml:"name,attr,omitempty"`
	PublishToServer   bool   `xml:"publishToServer,attr,omitempty"`
	ShortcutKey       string `xml:"shortcutKey,attr,omitempty"`
	StatusBar         string `xml:"statusBar,attr,omitempty"`
	VbProcedure       bool   `xml:"vbProcedure,attr,omitempty"`
	WorkbookParameter bool   `xml:"workbookParameter,attr,omitempty"`
	Xlm               bool   `xml:"xml,attr,omitempty"`
	Data              string `xml:",chardata"`
}

// xlsxCalcPr directly maps the calcPr element. This element defines the
// collection of properties the application uses to record calculation status
// and details. Calculation is the process of computing formulas and then
// displaying the results as values in the cells that contain the formulas.
type xlsxCalcPr struct {
	CalcCompleted         bool    `xml:"calcCompleted,attr,omitempty"`
	CalcID                string  `xml:"calcId,attr,omitempty"`
	CalcMode              string  `xml:"calcMode,attr,omitempty"`
	CalcOnSave            bool    `xml:"calcOnSave,attr,omitempty"`
	ConcurrentCalc        *bool   `xml:"concurrentCalc,attr"`
	ConcurrentManualCount int     `xml:"concurrentManualCount,attr,omitempty"`
	ForceFullCalc         bool    `xml:"forceFullCalc,attr,omitempty"`
	FullCalcOnLoad        bool    `xml:"fullCalcOnLoad,attr,omitempty"`
	FullPrecision         bool    `xml:"fullPrecision,attr,omitempty"`
	Iterate               bool    `xml:"iterate,attr,omitempty"`
	IterateCount          int     `xml:"iterateCount,attr,omitempty"`
	IterateDelta          float64 `xml:"iterateDelta,attr,omitempty"`
	RefMode               string  `xml:"refMode,attr,omitempty"`
}
<commit_msg>Add missing XML attributes `customWorkbookViews` and `customWorkbookView` in the workbook, relate issue #152.<commit_after>package excelize

import "encoding/xml"

// xmlxWorkbookRels contains xmlxWorkbookRelations which maps sheet id and sheet XML.
type xlsxWorkbookRels struct {
	XMLName       xml.Name               `xml:"http://schemas.openxmlformats.org/package/2006/relationships Relationships"`
	Relationships []xlsxWorkbookRelation `xml:"Relationship"`
}

// xmlxWorkbookRelation maps sheet id and xl/worksheets/_rels/sheet%d.xml.rels
type xlsxWorkbookRelation struct {
	ID         string `xml:"Id,attr"`
	Target     string `xml:",attr"`
	Type       string `xml:",attr"`
	TargetMode string `xml:",attr,omitempty"`
}

// xlsxWorkbook directly maps the workbook element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.
type xlsxWorkbook struct {
	XMLName             xml.Name                 `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main workbook"`
	FileVersion         *xlsxFileVersion         `xml:"fileVersion"`
	WorkbookPr          *xlsxWorkbookPr          `xml:"workbookPr"`
	WorkbookProtection  *xlsxWorkbookProtection  `xml:"workbookProtection"`
	BookViews           xlsxBookViews            `xml:"bookViews"`
	Sheets              xlsxSheets               `xml:"sheets"`
	ExternalReferences  *xlsxExternalReferences  `xml:"externalReferences"`
	DefinedNames        *xlsxDefinedNames        `xml:"definedNames"`
	CalcPr              *xlsxCalcPr              `xml:"calcPr"`
	CustomWorkbookViews *xlsxCustomWorkbookViews `xml:"customWorkbookViews"`
	PivotCaches         *xlsxPivotCaches         `xml:"pivotCaches"`
	ExtLst              *xlsxExtLst              `xml:"extLst"`
	FileRecoveryPr      *xlsxFileRecoveryPr      `xml:"fileRecoveryPr"`
}

// xlsxFileRecoveryPr maps sheet recovery information. This element defines
// properties that track the state of the workbook file, such as whether the
// file was saved during a crash, or whether it should be opened in auto-recover
// mode.
type xlsxFileRecoveryPr struct {
	AutoRecover     bool `xml:"autoRecover,attr,omitempty"`
	CrashSave       bool `xml:"crashSave,attr,omitempty"`
	DataExtractLoad bool `xml:"dataExtractLoad,attr,omitempty"`
	RepairLoad      bool `xml:"repairLoad,attr,omitempty"`
}

// xlsxWorkbookProtection directly maps the workbookProtection element. This
// element specifies options for protecting data in the workbook. Applications
// might use workbook protection to prevent anyone from accidentally changing,
// moving, or deleting important data. This protection can be ignored by
// applications which choose not to support this optional protection mechanism.
// When a password is to be hashed and stored in this element, it shall be
// hashed as defined below, starting from a UTF-16LE encoded string value. If
// there is a leading BOM character (U+FEFF) in the encoded password it is
// removed before hash calculation.
type xlsxWorkbookProtection struct {
	LockRevision           bool   `xml:"lockRevision,attr,omitempty"`
	LockStructure          bool   `xml:"lockStructure,attr,omitempty"`
	LockWindows            bool   `xml:"lockWindows,attr,omitempty"`
	RevisionsAlgorithmName string `xml:"revisionsAlgorithmName,attr,omitempty"`
	RevisionsHashValue     string `xml:"revisionsHashValue,attr,omitempty"`
	RevisionsSaltValue     string `xml:"revisionsSaltValue,attr,omitempty"`
	RevisionsSpinCount     int    `xml:"revisionsSpinCount,attr,omitempty"`
	WorkbookAlgorithmName  string `xml:"workbookAlgorithmName,attr,omitempty"`
	WorkbookHashValue      string `xml:"workbookHashValue,attr,omitempty"`
	WorkbookSaltValue      string `xml:"workbookSaltValue,attr,omitempty"`
	WorkbookSpinCount      int    `xml:"workbookSpinCount,attr,omitempty"`
}

// xlsxFileVersion directly maps the fileVersion element. This element defines
// properties that track which version of the application accessed the data and
// source code contained in the file.
type xlsxFileVersion struct {
	AppName      string `xml:"appName,attr,omitempty"`
	CodeName     string `xml:"codeName,attr,omitempty"`
	LastEdited   string `xml:"lastEdited,attr,omitempty"`
	LowestEdited string `xml:"lowestEdited,attr,omitempty"`
	RupBuild     string `xml:"rupBuild,attr,omitempty"`
}

// xlsxWorkbookPr directly maps the workbookPr element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main This element
// defines a collection of workbook properties.
type xlsxWorkbookPr struct {
	AllowRefreshQuery          bool   `xml:"allowRefreshQuery,attr,omitempty"`
	AutoCompressPictures       bool   `xml:"autoCompressPictures,attr,omitempty"`
	BackupFile                 bool   `xml:"backupFile,attr,omitempty"`
	CheckCompatibility         bool   `xml:"checkCompatibility,attr,omitempty"`
	CodeName                   string `xml:"codeName,attr,omitempty"`
	Date1904                   bool   `xml:"date1904,attr,omitempty"`
	DefaultThemeVersion        string `xml:"defaultThemeVersion,attr,omitempty"`
	FilterPrivacy              bool   `xml:"filterPrivacy,attr,omitempty"`
	HidePivotFieldList         bool   `xml:"hidePivotFieldList,attr,omitempty"`
	PromptedSolutions          bool   `xml:"promptedSolutions,attr,omitempty"`
	PublishItems               bool   `xml:"publishItems,attr,omitempty"`
	RefreshAllConnections      bool   `xml:"refreshAllConnections,attr,omitempty"`
	SaveExternalLinkValues     bool   `xml:"saveExternalLinkValues,attr,omitempty"`
	ShowBorderUnselectedTables bool   `xml:"showBorderUnselectedTables,attr,omitempty"`
	ShowInkAnnotation          bool   `xml:"showInkAnnotation,attr,omitempty"`
	ShowObjects                string `xml:"showObjects,attr,omitempty"`
	ShowPivotChartFilter       bool   `xml:"showPivotChartFilter,attr,omitempty"`
	UpdateLinks                string `xml:"updateLinks,attr,omitempty"`
}

// xlsxBookViews directly maps the bookViews element. This element specifies the
// collection of workbook views of the enclosing workbook. Each view can specify
// a window position, filter options, and other configurations. There is no
// limit on the number of workbook views that can be defined for a workbook.
type xlsxBookViews struct {
	WorkBookView []xlsxWorkBookView `xml:"workbookView"`
}

// xlsxWorkBookView directly maps the workbookView element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main This element
// specifies a single Workbook view.
type xlsxWorkBookView struct {
	ActiveTab              int    `xml:"activeTab,attr,omitempty"`
	AutoFilterDateGrouping bool   `xml:"autoFilterDateGrouping,attr,omitempty"`
	FirstSheet             int    `xml:"firstSheet,attr,omitempty"`
	Minimized              bool   `xml:"minimized,attr,omitempty"`
	ShowHorizontalScroll   bool   `xml:"showHorizontalScroll,attr,omitempty"`
	ShowSheetTabs          bool   `xml:"showSheetTabs,attr,omitempty"`
	ShowVerticalScroll     bool   `xml:"showVerticalScroll,attr,omitempty"`
	TabRatio               int    `xml:"tabRatio,attr,omitempty"`
	Visibility             string `xml:"visibility,attr,omitempty"`
	WindowHeight           int    `xml:"windowHeight,attr,omitempty"`
	WindowWidth            int    `xml:"windowWidth,attr,omitempty"`
	XWindow                string `xml:"xWindow,attr,omitempty"`
	YWindow                string `xml:"yWindow,attr,omitempty"`
}

// xlsxSheets directly maps the sheets element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main.
type xlsxSheets struct {
	Sheet []xlsxSheet `xml:"sheet"`
}

// xlsxSheet directly maps the sheet element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.
type xlsxSheet struct {
	Name    string `xml:"name,attr,omitempty"`
	SheetID string `xml:"sheetId,attr,omitempty"`
	ID      string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
	State   string `xml:"state,attr,omitempty"`
}

// xlsxExternalReferences directly maps the externalReferences element of the
// external workbook references part.
type xlsxExternalReferences struct {
	ExternalReference []xlsxExternalReference `xml:"externalReference"`
}

// xlsxExternalReference directly maps the externalReference element of the
// external workbook references part.
type xlsxExternalReference struct {
	RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}

// xlsxPivotCaches element enumerates pivot cache definition parts used by pivot
// tables and formulas in this workbook.
type xlsxPivotCaches struct {
	PivotCache []xlsxPivotCache `xml:"pivotCache"`
}

// xlsxPivotCache directly maps the pivotCache element.
type xlsxPivotCache struct {
	CacheID int    `xml:"cacheId,attr,omitempty"`
	RID     string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
}

// extLst element provides a convention for extending spreadsheetML in
// predefined locations. The locations shall be denoted with the extLst element,
// and are called extension lists. Extension list locations within the markup
// document are specified in the markup specification and can be used to store
// extensions to the markup specification, whether those are future version
// extensions of the markup specification or are private extensions implemented
// independently from the markup specification. Markup within an extension might
// not be understood by a consumer.
type xlsxExtLst struct {
	Ext string `xml:",innerxml"`
}

// xlsxDefinedNames directly maps the definedNames element. This element defines
// the collection of defined names for this workbook. Defined names are
// descriptive names to represent cells, ranges of cells, formulas, or constant
// values. Defined names can be used to represent a range on any worksheet.
type xlsxDefinedNames struct {
	DefinedName []xlsxDefinedName `xml:"definedName"`
}

// xlsxDefinedName directly maps the definedName element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main This element
// defines a defined name within this workbook. A defined name is descriptive
// text that is used to represents a cell, range of cells, formula, or constant
// value. For a descriptions of the attributes see https://msdn.microsoft.com/en-us/library/office/documentformat.openxml.spreadsheet.definedname.aspx
type xlsxDefinedName struct {
	Comment           string `xml:"comment,attr,omitempty"`
	CustomMenu        string `xml:"customMenu,attr,omitempty"`
	Description       string `xml:"description,attr,omitempty"`
	Function          bool   `xml:"function,attr,omitempty"`
	FunctionGroupID   int    `xml:"functionGroupId,attr,omitempty"`
	Help              string `xml:"help,attr,omitempty"`
	Hidden            bool   `xml:"hidden,attr,omitempty"`
	LocalSheetID      *int   `xml:"localSheetId,attr"`
	Name              string `xml:"name,attr,omitempty"`
	PublishToServer   bool   `xml:"publishToServer,attr,omitempty"`
	ShortcutKey       string `xml:"shortcutKey,attr,omitempty"`
	StatusBar         string `xml:"statusBar,attr,omitempty"`
	VbProcedure       bool   `xml:"vbProcedure,attr,omitempty"`
	WorkbookParameter bool   `xml:"workbookParameter,attr,omitempty"`
	Xlm               bool   `xml:"xml,attr,omitempty"`
	Data              string `xml:",chardata"`
}

// xlsxCalcPr directly maps the calcPr element. This element defines the
// collection of properties the application uses to record calculation status
// and details. Calculation is the process of computing formulas and then
// displaying the results as values in the cells that contain the formulas.
type xlsxCalcPr struct {
	CalcCompleted         bool    `xml:"calcCompleted,attr,omitempty"`
	CalcID                string  `xml:"calcId,attr,omitempty"`
	CalcMode              string  `xml:"calcMode,attr,omitempty"`
	CalcOnSave            bool    `xml:"calcOnSave,attr,omitempty"`
	ConcurrentCalc        *bool   `xml:"concurrentCalc,attr"`
	ConcurrentManualCount int     `xml:"concurrentManualCount,attr,omitempty"`
	ForceFullCalc         bool    `xml:"forceFullCalc,attr,omitempty"`
	FullCalcOnLoad        bool    `xml:"fullCalcOnLoad,attr,omitempty"`
	FullPrecision         bool    `xml:"fullPrecision,attr,omitempty"`
	Iterate               bool    `xml:"iterate,attr,omitempty"`
	IterateCount          int     `xml:"iterateCount,attr,omitempty"`
	IterateDelta          float64 `xml:"iterateDelta,attr,omitempty"`
	RefMode               string  `xml:"refMode,attr,omitempty"`
}

// xlsxCustomWorkbookViews defines the collection of custom workbook views that
// are defined for this workbook. A customWorkbookView is similar in concept to
// a workbookView in that its attributes contain settings related to the way
// that the workbook should be displayed on a screen by a spreadsheet
// application.
type xlsxCustomWorkbookViews struct {
	CustomWorkbookView []xlsxCustomWorkbookView `xml:"customWorkbookView"`
}

// xlsxCustomWorkbookView directly maps the customWorkbookView element. This
// element specifies a single custom workbook view. A custom workbook view
// consists of a set of display and print settings that you can name and apply
// to a workbook. You can create more than one custom workbook view of the same
// workbook. Custom Workbook Views are not required in order to construct a
// valid SpreadsheetML document, and are not necessary if the document is never
// displayed by a spreadsheet application, or if the spreadsheet application has
// a fixed display for workbooks. However, if a spreadsheet application chooses
// to implement configurable display modes, the customWorkbookView element
// should be used to persist the settings for those display modes.
type xlsxCustomWorkbookView struct {
	ActiveSheetID        *int    `xml:"activeSheetId,attr"`
	AutoUpdate           *bool   `xml:"autoUpdate,attr"`
	ChangesSavedWin      *bool   `xml:"changesSavedWin,attr"`
	GUID                 *string `xml:"guid,attr"`
	IncludeHiddenRowCol  *bool   `xml:"includeHiddenRowCol,attr"`
	IncludePrintSettings *bool   `xml:"includePrintSettings,attr"`
	Maximized            *bool   `xml:"maximized,attr"`
	MergeInterval        int     `xml:"mergeInterval,attr"`
	Minimized            *bool   `xml:"minimized,attr"`
	Name                 *string `xml:"name,attr"`
	OnlySync             *bool   `xml:"onlySync,attr"`
	PersonalView         *bool   `xml:"personalView,attr"`
	ShowComments         *string `xml:"showComments,attr"`
	ShowFormulaBar       *bool   `xml:"showFormulaBar,attr"`
	ShowHorizontalScroll *bool   `xml:"showHorizontalScroll,attr"`
	ShowObjects          *string `xml:"showObjects,attr"`
	ShowSheetTabs        *bool   `xml:"showSheetTabs,attr"`
	ShowStatusbar        *bool   `xml:"showStatusbar,attr"`
	ShowVerticalScroll   *bool   `xml:"showVerticalScroll,attr"`
	TabRatio             *int    `xml:"tabRatio,attr"`
	WindowHeight         *int    `xml:"windowHeight,attr"`
	WindowWidth          *int    `xml:"windowWidth,attr"`
	XWindow              *int    `xml:"xWindow,attr"`
	YWindow              *int    `xml:"yWindow,attr"`
}
<|endoftext|>


<commit_before>package design

import (
	"net/http"

	"goa.design/goa.v2/codegen"
	"goa.design/goa.v2/design"
)

// RequestBody returns an attribute describing the request body of the given
// endpoint. If the DSL defines a body explicitly via the Body function then the
// corresponding attribute is used. Otherwise the attribute is computed by
// removing the attributes of the method payload used to define headers and
// parameters.
func RequestBody(a *EndpointExpr) *design.AttributeExpr {
	if a.Body != nil {
		return a.Body
	}

	var (
		payload = a.MethodExpr.Payload
		headers = a.MappedHeaders()
		params  = a.AllParams()
		suffix  = "RequestBody"
		name    = codegen.Goify(a.Name(), true) + suffix
	)

	bodyOnly := len(*design.AsObject(headers.Type)) == 0 &&
		len(*design.AsObject(params.Type)) == 0

	// 1. If Payload is not an object then check whether there are params or
	// headers defined and if so return empty type (payload encoded in
	// request params or headers) otherwise return payload type (payload
	// encoded in request body).
	if !design.IsObject(payload.Type) {
		if bodyOnly {
			renameType(payload, name, "RequestBody")
			return payload
		}
		return &design.AttributeExpr{Type: design.Empty}
	}

	// 2. Remove header and param attributes
	body := design.NewMappedAttributeExpr(payload)
	removeAttributes(body, headers)
	removeAttributes(body, params)

	// 3. Return empty type if no attribute left
	if len(*design.AsObject(body.Type)) == 0 {
		return &design.AttributeExpr{Type: design.Empty}
	}

	// 4. Build computed user type
	att := body.Attribute()
	ut := &design.UserTypeExpr{
		AttributeExpr: att,
		TypeName:      name,
	}
	appendSuffix(ut.Attribute().Type, "RequestBody")

	return &design.AttributeExpr{
		Type:         ut,
		Validation:   att.Validation,
		UserExamples: att.UserExamples,
	}
}

// ResponseBody returns an attribute representing the response body for the
// given endpoint and response. If the DSL defines a body explicitly via the
// Body function then the corresponding attribute is used. Otherwise the
// attribute is computed by removing the attributes of the method payload used
// to define headers and parameters. Also if the response defines a view then
// the response result type is projected first.
func ResponseBody(a *EndpointExpr, resp *HTTPResponseExpr) *design.AttributeExpr {
	result := a.MethodExpr.Result
	if result == nil || result.Type == design.Empty {
		return &design.AttributeExpr{Type: design.Empty}
	}
	if resp.Body != nil {
		return resp.Body
	}

	var suffix string
	if len(a.Responses) > 1 {
		suffix = http.StatusText(resp.StatusCode)
	}

	var (
		headers = resp.MappedHeaders()
		name    = codegen.Goify(a.Name(), true) + suffix + "ResponseBody"
	)

	// 1. If Result is not an object then check whether there are headers
	// defined and if so return empty type (result encoded in response
	// headers) otherwise return renamed result type (result encoded in
	// response body).
	if !design.IsObject(result.Type) {
		if len(*design.AsObject(resp.Headers().Type)) == 0 {
			result = design.DupAtt(result)
			renameType(result, name, "ResponseBody")
			return result
		}
		return &design.AttributeExpr{Type: design.Empty}
	}

	// 2. Project if response type is result type and attribute has a view.
	rt, isrt := result.Type.(*design.ResultTypeExpr)
	if isrt {
		if v := result.Metadata["view"]; len(v) > 0 {
			dt, err := design.Project(rt, v[0])
			if err != nil {
				panic(err) // bug
			}
			result = design.DupAtt(result)
			result.Type = dt
		}
	}

	// 3. Remove header attributes
	body := design.NewMappedAttributeExpr(result)
	removeAttributes(body, headers)

	// 4. Return empty type if no attribute left
	if len(*design.AsObject(body.Type)) == 0 {
		return &design.AttributeExpr{Type: design.Empty}
	}

	// 5. Build computed user type
	userType := &design.UserTypeExpr{
		AttributeExpr: body.Attribute(),
		TypeName:      name,
	}
	if isrt {
		views := make([]*design.ViewExpr, len(rt.Views))
		for i, v := range rt.Views {
			mv := design.NewMappedAttributeExpr(v.AttributeExpr)
			removeAttributes(mv, headers)
			nv := &design.ViewExpr{
				AttributeExpr: mv.Attribute(),
				Name:          v.Name,
			}
			views[i] = nv
		}
		nmt := &design.ResultTypeExpr{
			UserTypeExpr: userType,
			Identifier:   rt.Identifier,
			ContentType:  rt.ContentType,
			Views:        views,
		}
		for _, v := range views {
			v.Parent = nmt
		}
		return &design.AttributeExpr{Type: nmt, Validation: userType.Validation}
	}
	appendSuffix(userType.Attribute().Type, "ResponseBody")

	return &design.AttributeExpr{Type: userType, Validation: userType.Validation}
}

// ErrorResponseBody returns an attribute describing the response body of a
// given error. If the DSL defines a body explicitly via the Body function then
// the corresponding attribute is returned. Otherwise the attribute is computed
// by removing the attributes of the error used to define headers and
// parameters. Also if the error response defines a view then the result type is
// projected first.
func ErrorResponseBody(a *EndpointExpr, v *ErrorExpr) *design.AttributeExpr {
	result := v.ErrorExpr.AttributeExpr
	if result == nil || result.Type == design.Empty {
		return &design.AttributeExpr{Type: design.Empty}
	}
	resp := v.Response
	if resp.Body != nil {
		return resp.Body
	}

	var (
		headers = resp.MappedHeaders()
		suffix  = codegen.Goify(v.ErrorExpr.Name, true) + "ResponseBody"
		name    = codegen.Goify(a.Name(), true) + suffix
	)

	// 1. If Result is not an object then check whether there are headers
	// defined and if so return empty type (result encoded in response
	// headers) otherwise return renamed result type (result encoded in
	// response body).
	if !design.IsObject(result.Type) {
		if len(*design.AsObject(resp.Headers().Type)) == 0 {
			renameType(result, name, suffix)
			return result
		}
		return &design.AttributeExpr{Type: design.Empty}
	}

	// 2. Project if errorResponse type is result type and attribute has a view.
	rt, isrt := v.ErrorExpr.AttributeExpr.Type.(*design.ResultTypeExpr)
	if isrt {
		if v := result.Metadata["view"]; len(v) > 0 {
			dt, err := design.Project(rt, v[0])
			if err != nil {
				panic(err) // bug
			}
			result = design.DupAtt(result)
			result.Type = dt
		}
	}

	// 3. Remove header attributes
	body := design.NewMappedAttributeExpr(result)
	removeAttributes(body, headers)

	// 4. Return empty type if no attribute left
	if len(*design.AsObject(body.Type)) == 0 {
		return &design.AttributeExpr{Type: design.Empty}
	}

	// 5. Build computed user type
	userType := &design.UserTypeExpr{
		AttributeExpr: body.Attribute(),
		TypeName:      name,
	}
	appendSuffix(userType.Attribute().Type, suffix)

	if !isrt {
		return &design.AttributeExpr{Type: userType, Validation: userType.Validation}
	}

	views := make([]*design.ViewExpr, len(rt.Views))
	for i, v := range rt.Views {
		mv := design.NewMappedAttributeExpr(v.AttributeExpr)
		removeAttributes(mv, headers)
		nv := &design.ViewExpr{
			AttributeExpr: mv.Attribute(),
			Name:          v.Name,
		}
		views[i] = nv
	}
	nmt := &design.ResultTypeExpr{
		UserTypeExpr: userType,
		Identifier:   rt.Identifier,
		ContentType:  rt.ContentType,
		Views:        views,
	}
	for _, v := range views {
		v.Parent = nmt
	}
	return &design.AttributeExpr{Type: nmt, Validation: userType.Validation}
}

func renameType(att *design.AttributeExpr, name, suffix string) {
	rt := att.Type
	switch rt.(type) {
	case design.UserType:
		rt = design.Dup(rt)
		rt.(design.UserType).Rename(name)
		appendSuffix(rt.(design.UserType).Attribute().Type, suffix)
	case *design.Object:
		rt = design.Dup(rt)
		appendSuffix(rt, suffix)
	case *design.Array:
		rt = design.Dup(rt)
		appendSuffix(rt, suffix)
	case *design.Map:
		rt = design.Dup(rt)
		appendSuffix(rt, suffix)
	}
	att.Type = rt
}

func appendSuffix(dt design.DataType, suffix string, seen ...map[string]struct{}) {
	switch actual := dt.(type) {
	case design.UserType:
		var s map[string]struct{}
		if len(seen) > 0 {
			s = seen[0]
		} else {
			s = make(map[string]struct{})
		}
		if _, ok := s[actual.Name()]; ok {
			return
		}
		actual.Rename(actual.Name() + suffix)
		s[actual.Name()] = struct{}{}
		appendSuffix(actual.Attribute().Type, suffix, s)
	case *design.Object:
		for _, nat := range *actual {
			appendSuffix(nat.Attribute.Type, suffix, seen...)
		}
	case *design.Array:
		appendSuffix(actual.ElemType.Type, suffix, seen...)
	case *design.Map:
		appendSuffix(actual.KeyType.Type, suffix, seen...)
		appendSuffix(actual.ElemType.Type, suffix, seen...)
	}
}

func removeAttributes(attr, sub *design.MappedAttributeExpr) {
	codegen.WalkMappedAttr(sub, func(name, _ string, _ bool, _ *design.AttributeExpr) error {
		attr.Delete(name)
		if attr.Validation != nil {
			attr.Validation.RemoveRequired(name)
		}
		for _, ex := range attr.UserExamples {
			if m, ok := ex.Value.(map[string]interface{}); ok {
				delete(m, name)
			}
		}
		return nil
	})
}
<commit_msg>Fix regression.<commit_after>package design

import (
	"net/http"

	"goa.design/goa.v2/codegen"
	"goa.design/goa.v2/design"
)

// RequestBody returns an attribute describing the request body of the given
// endpoint. If the DSL defines a body explicitly via the Body function then the
// corresponding attribute is used. Otherwise the attribute is computed by
// removing the attributes of the method payload used to define headers and
// parameters.
func RequestBody(a *EndpointExpr) *design.AttributeExpr {
	if a.Body != nil {
		return a.Body
	}

	var (
		payload = a.MethodExpr.Payload
		headers = a.MappedHeaders()
		params  = a.AllParams()
		suffix  = "RequestBody"
		name    = codegen.Goify(a.Name(), true) + suffix
	)

	bodyOnly := len(*design.AsObject(headers.Type)) == 0 &&
		len(*design.AsObject(params.Type)) == 0

	// 1. If Payload is not an object then check whether there are params or
	// headers defined and if so return empty type (payload encoded in
	// request params or headers) otherwise return payload type (payload
	// encoded in request body).
	if !design.IsObject(payload.Type) {
		if bodyOnly {
			payload = design.DupAtt(payload)
			renameType(payload, name, "RequestBody")
			return payload
		}
		return &design.AttributeExpr{Type: design.Empty}
	}

	// 2. Remove header and param attributes
	body := design.NewMappedAttributeExpr(payload)
	removeAttributes(body, headers)
	removeAttributes(body, params)

	// 3. Return empty type if no attribute left
	if len(*design.AsObject(body.Type)) == 0 {
		return &design.AttributeExpr{Type: design.Empty}
	}

	// 4. Build computed user type
	att := body.Attribute()
	ut := &design.UserTypeExpr{
		AttributeExpr: att,
		TypeName:      name,
	}
	appendSuffix(ut.Attribute().Type, "RequestBody")

	return &design.AttributeExpr{
		Type:         ut,
		Validation:   att.Validation,
		UserExamples: att.UserExamples,
	}
}

// ResponseBody returns an attribute representing the response body for the
// given endpoint and response. If the DSL defines a body explicitly via the
// Body function then the corresponding attribute is used. Otherwise the
// attribute is computed by removing the attributes of the method payload used
// to define headers and parameters. Also if the response defines a view then
// the response result type is projected first.
func ResponseBody(a *EndpointExpr, resp *HTTPResponseExpr) *design.AttributeExpr {
	result := a.MethodExpr.Result
	if result == nil || result.Type == design.Empty {
		return &design.AttributeExpr{Type: design.Empty}
	}
	if resp.Body != nil {
		return resp.Body
	}

	var suffix string
	if len(a.Responses) > 1 {
		suffix = http.StatusText(resp.StatusCode)
	}

	var (
		headers = resp.MappedHeaders()
		name    = codegen.Goify(a.Name(), true) + suffix + "ResponseBody"
	)

	// 1. If Result is not an object then check whether there are headers
	// defined and if so return empty type (result encoded in response
	// headers) otherwise return renamed result type (result encoded in
	// response body).
	if !design.IsObject(result.Type) {
		if len(*design.AsObject(resp.Headers().Type)) == 0 {
			result = design.DupAtt(result)
			renameType(result, name, "ResponseBody")
			return result
		}
		return &design.AttributeExpr{Type: design.Empty}
	}

	// 2. Project if response type is result type and attribute has a view.
	rt, isrt := result.Type.(*design.ResultTypeExpr)
	if isrt {
		if v := result.Metadata["view"]; len(v) > 0 {
			dt, err := design.Project(rt, v[0])
			if err != nil {
				panic(err) // bug
			}
			result = design.DupAtt(result)
			result.Type = dt
		}
	}

	// 3. Remove header attributes
	body := design.NewMappedAttributeExpr(result)
	removeAttributes(body, headers)

	// 4. Return empty type if no attribute left
	if len(*design.AsObject(body.Type)) == 0 {
		return &design.AttributeExpr{Type: design.Empty}
	}

	// 5. Build computed user type
	userType := &design.UserTypeExpr{
		AttributeExpr: body.Attribute(),
		TypeName:      name,
	}
	if isrt {
		views := make([]*design.ViewExpr, len(rt.Views))
		for i, v := range rt.Views {
			mv := design.NewMappedAttributeExpr(v.AttributeExpr)
			removeAttributes(mv, headers)
			nv := &design.ViewExpr{
				AttributeExpr: mv.Attribute(),
				Name:          v.Name,
			}
			views[i] = nv
		}
		nmt := &design.ResultTypeExpr{
			UserTypeExpr: userType,
			Identifier:   rt.Identifier,
			ContentType:  rt.ContentType,
			Views:        views,
		}
		for _, v := range views {
			v.Parent = nmt
		}
		return &design.AttributeExpr{Type: nmt, Validation: userType.Validation}
	}
	appendSuffix(userType.Attribute().Type, "ResponseBody")

	return &design.AttributeExpr{Type: userType, Validation: userType.Validation}
}

// ErrorResponseBody returns an attribute describing the response body of a
// given error. If the DSL defines a body explicitly via the Body function then
// the corresponding attribute is returned. Otherwise the attribute is computed
// by removing the attributes of the error used to define headers and
// parameters. Also if the error response defines a view then the result type is
// projected first.
func ErrorResponseBody(a *EndpointExpr, v *ErrorExpr) *design.AttributeExpr {
	result := v.ErrorExpr.AttributeExpr
	if result == nil || result.Type == design.Empty {
		return &design.AttributeExpr{Type: design.Empty}
	}
	resp := v.Response
	if resp.Body != nil {
		return resp.Body
	}

	var (
		headers = resp.MappedHeaders()
		suffix  = codegen.Goify(v.ErrorExpr.Name, true) + "ResponseBody"
		name    = codegen.Goify(a.Name(), true) + suffix
	)

	// 1. If Result is not an object then check whether there are headers
	// defined and if so return empty type (result encoded in response
	// headers) otherwise return renamed result type (result encoded in
	// response body).
	if !design.IsObject(result.Type) {
		if len(*design.AsObject(resp.Headers().Type)) == 0 {
			renameType(result, name, suffix)
			return result
		}
		return &design.AttributeExpr{Type: design.Empty}
	}

	// 2. Project if errorResponse type is result type and attribute has a view.
	rt, isrt := v.ErrorExpr.AttributeExpr.Type.(*design.ResultTypeExpr)
	if isrt {
		if v := result.Metadata["view"]; len(v) > 0 {
			dt, err := design.Project(rt, v[0])
			if err != nil {
				panic(err) // bug
			}
			result = design.DupAtt(result)
			result.Type = dt
		}
	}

	// 3. Remove header attributes
	body := design.NewMappedAttributeExpr(result)
	removeAttributes(body, headers)

	// 4. Return empty type if no attribute left
	if len(*design.AsObject(body.Type)) == 0 {
		return &design.AttributeExpr{Type: design.Empty}
	}

	// 5. Build computed user type
	userType := &design.UserTypeExpr{
		AttributeExpr: body.Attribute(),
		TypeName:      name,
	}
	appendSuffix(userType.Attribute().Type, suffix)

	if !isrt {
		return &design.AttributeExpr{Type: userType, Validation: userType.Validation}
	}

	views := make([]*design.ViewExpr, len(rt.Views))
	for i, v := range rt.Views {
		mv := design.NewMappedAttributeExpr(v.AttributeExpr)
		removeAttributes(mv, headers)
		nv := &design.ViewExpr{
			AttributeExpr: mv.Attribute(),
			Name:          v.Name,
		}
		views[i] = nv
	}
	nmt := &design.ResultTypeExpr{
		UserTypeExpr: userType,
		Identifier:   rt.Identifier,
		ContentType:  rt.ContentType,
		Views:        views,
	}
	for _, v := range views {
		v.Parent = nmt
	}
	return &design.AttributeExpr{Type: nmt, Validation: userType.Validation}
}

func renameType(att *design.AttributeExpr, name, suffix string) {
	rt := att.Type
	switch rt.(type) {
	case design.UserType:
		rt = design.Dup(rt)
		rt.(design.UserType).Rename(name)
		appendSuffix(rt.(design.UserType).Attribute().Type, suffix)
	case *design.Object:
		rt = design.Dup(rt)
		appendSuffix(rt, suffix)
	case *design.Array:
		rt = design.Dup(rt)
		appendSuffix(rt, suffix)
	case *design.Map:
		rt = design.Dup(rt)
		appendSuffix(rt, suffix)
	}
	att.Type = rt
}

func appendSuffix(dt design.DataType, suffix string, seen ...map[string]struct{}) {
	switch actual := dt.(type) {
	case design.UserType:
		var s map[string]struct{}
		if len(seen) > 0 {
			s = seen[0]
		} else {
			s = make(map[string]struct{})
		}
		if _, ok := s[actual.Name()]; ok {
			return
		}
		actual.Rename(actual.Name() + suffix)
		s[actual.Name()] = struct{}{}
		appendSuffix(actual.Attribute().Type, suffix, s)
	case *design.Object:
		for _, nat := range *actual {
			appendSuffix(nat.Attribute.Type, suffix, seen...)
		}
	case *design.Array:
		appendSuffix(actual.ElemType.Type, suffix, seen...)
	case *design.Map:
		appendSuffix(actual.KeyType.Type, suffix, seen...)
		appendSuffix(actual.ElemType.Type, suffix, seen...)
	}
}

func removeAttributes(attr, sub *design.MappedAttributeExpr) {
	codegen.WalkMappedAttr(sub, func(name, _ string, _ bool, _ *design.AttributeExpr) error {
		attr.Delete(name)
		if attr.Validation != nil {
			attr.Validation.RemoveRequired(name)
		}
		for _, ex := range attr.UserExamples {
			if m, ok := ex.Value.(map[string]interface{}); ok {
				delete(m, name)
			}
		}
		return nil
	})
}
<|endoftext|>


<commit_before>package gocube

import (
	"strconv"
)

// A CubieCorner represents a physical corner of a cube.
//
// To understand the meaning of a CubieCorner's fields, you must first
// understand the coordinate system. There are there axes, x, y, and z.
// The x axis is 0 at the L face and 1 at the R face.
// The y axis is 0 at the D face and 1 at the U face.
// The z axis is 0 at the B face and 1 at the F face.
//
// A corner piece's index is determined by it's original position on the cube.
// The index is a binary number of the form ZYX, where Z is the most significant
// digit. Thus, the BLD corner is 0, the BRU corner is 3, the FRU corner is 7,
// etc.
//
// The orientation of a corner tells how it is twisted. It is an axis number 0,
// 1, or 2 for x, y, or z respectively. It indicates the direction normal to the
// red or orange sticker (i.e. the sticker that is usually normal to the x
// axis).
type CubieCorner struct {
	Piece       int
	Orientation int
}

// CubieCorners represents the corners of a cube.
type CubieCorners [8]CubieCorner

// SolvedCubieCorners generates the corners of a solved cube.
func SolvedCubieCorners() CubieCorners {
	var res CubieCorners
	for i := 0; i < 8; i++ {
		res[i].Piece = i
	}
	return res
}

// HalfTurn performs a 180 degree turn on a given face.
func (c *CubieCorners) HalfTurn(face int) {
	// A double turn is really just two swaps.
	switch face {
	case 1: // Top face
		c[2], c[7] = c[7], c[2]
		c[3], c[6] = c[6], c[3]
	case 2: // Bottom face
		c[0], c[5] = c[5], c[0]
		c[1], c[4] = c[4], c[1]
	case 3: // Front face
		c[5], c[6] = c[6], c[5]
		c[4], c[7] = c[7], c[4]
	case 4: // Back face
		c[0], c[3] = c[3], c[0]
		c[1], c[2] = c[2], c[1]
	case 5: // Right face
		c[1], c[7] = c[7], c[1]
		c[3], c[5] = c[5], c[3]
	case 6: // Left face
		c[0], c[6] = c[6], c[0]
		c[2], c[4] = c[4], c[2]
	default:
		panic("Unsupported half-turn applied to CubieCorners: " +
			strconv.Itoa(face))
	}
}

// Move applies a face turn to the corners.
func (c *CubieCorners) Move(m Move) {
	// Half turns are a simple case.
	if m.Turns == 2 {
		c.HalfTurn(m.Face)
	} else {
		c.QuarterTurn(m.Face, m.Turns)
	}
}

// QuarterTurn performs a 90 degree turn on a given face.
func (c *CubieCorners) QuarterTurn(face, turns int) {
	// This code is not particularly graceful, but it is rather efficient and
	// quite readable compared to a pure array of transformations.
	switch face {
	case 1: // Top face
		if turns == 1 {
			c[2], c[3], c[7], c[6] = c[6], c[2], c[3], c[7]
		} else {
			c[6], c[2], c[3], c[7] = c[2], c[3], c[7], c[6]
		}
		// Swap orientation 0 with orientation 2.
		for _, i := range []int{2, 3, 6, 7} {
			c[i].Orientation = 2 - c[i].Orientation
		}
	case 2: // Bottom face
		if turns == 1 {
			c[4], c[0], c[1], c[5] = c[0], c[1], c[5], c[4]
		} else {
			c[0], c[1], c[5], c[4] = c[4], c[0], c[1], c[5]
		}
		// Swap orientation 0 with orientation 2.
		for _, i := range []int{0, 1, 4, 5} {
			c[i].Orientation = 2 - c[i].Orientation
		}
	case 3: // Front face
		if turns == 1 {
			c[6], c[7], c[5], c[4] = c[4], c[6], c[7], c[5]
		} else {
			c[4], c[6], c[7], c[5] = c[6], c[7], c[5], c[4]
		}
		// Swap orientation 0 with orientation 1.
		for _, i := range []int{4, 5, 6, 7} {
			if c[i].Orientation == 0 {
				c[i].Orientation = 1
			} else if c[i].Orientation == 1 {
				c[i].Orientation = 0
			}
		}
	case 4: // Back face
		if turns == 1 {
			c[0], c[2], c[3], c[1] = c[2], c[3], c[1], c[0]
		} else {
			c[2], c[3], c[1], c[0] = c[0], c[2], c[3], c[1]
		}
		// Swap orientation 0 with orientation 1.
		for _, i := range []int{0, 1, 2, 3} {
			if c[i].Orientation == 0 {
				c[i].Orientation = 1
			} else if c[i].Orientation == 1 {
				c[i].Orientation = 0
			}
		}
	case 5: // Right face
		if turns == 1 {
			c[7], c[3], c[1], c[5] = c[5], c[7], c[3], c[1]
		} else {
			c[5], c[7], c[3], c[1] = c[7], c[3], c[1], c[5]
		}
		// Swap orientation 2 with orientation 1.
		for _, i := range []int{1, 3, 5, 7} {
			if c[i].Orientation == 1 {
				c[i].Orientation = 2
			} else if c[i].Orientation == 2 {
				c[i].Orientation = 1
			}
		}
	case 6: // Left face
		if turns == 1 {
			c[4], c[6], c[2], c[0] = c[6], c[2], c[0], c[4]
		} else {
			c[6], c[2], c[0], c[4] = c[4], c[6], c[2], c[0]
		}
		// Swap orientation 2 with orientation 1.
		for _, i := range []int{0, 2, 4, 6} {
			if c[i].Orientation == 1 {
				c[i].Orientation = 2
			} else if c[i].Orientation == 2 {
				c[i].Orientation = 1
			}
		}
	default:
		panic("Unsupported quarter-turn applied to CubieCorners: " +
			strconv.Itoa(face))
	}
}

// Solved returns true if all the corners are properly positioned and oriented.
func (c *CubieCorners) Solved() bool {
	for i := 0; i < 8; i++ {
		if c[i].Piece != i || c[i].Orientation != 0 {
			return false
		}
	}
	return true
}
<commit_msg>drafted CubieCorners.Search()<commit_after>package gocube

import (
	"strconv"
	"sync"
)

// A CornersGoal represents an abstract goal state for a depth-first search of
// the cube's corners.
type CornersGoal interface {
	IsGoal(c CubieCorners) bool
}

// A CornersPruner is used as a lower-bound heuristic for a depth-first search
// of the cube's corners.
type CornersPruner interface {
	MinMoves(c CubieCorners) int
}

// A CubieCorner represents a physical corner of a cube.
//
// To understand the meaning of a CubieCorner's fields, you must first
// understand the coordinate system. There are there axes, x, y, and z.
// The x axis is 0 at the L face and 1 at the R face.
// The y axis is 0 at the D face and 1 at the U face.
// The z axis is 0 at the B face and 1 at the F face.
//
// A corner piece's index is determined by it's original position on the cube.
// The index is a binary number of the form ZYX, where Z is the most significant
// digit. Thus, the BLD corner is 0, the BRU corner is 3, the FRU corner is 7,
// etc.
//
// The orientation of a corner tells how it is twisted. It is an axis number 0,
// 1, or 2 for x, y, or z respectively. It indicates the direction normal to the
// red or orange sticker (i.e. the sticker that is usually normal to the x
// axis).
type CubieCorner struct {
	Piece       int
	Orientation int
}

// CubieCorners represents the corners of a cube.
type CubieCorners [8]CubieCorner

// SolvedCubieCorners generates the corners of a solved cube.
func SolvedCubieCorners() CubieCorners {
	var res CubieCorners
	for i := 0; i < 8; i++ {
		res[i].Piece = i
	}
	return res
}

// HalfTurn performs a 180 degree turn on a given face.
func (c *CubieCorners) HalfTurn(face int) {
	// A double turn is really just two swaps.
	switch face {
	case 1: // Top face
		c[2], c[7] = c[7], c[2]
		c[3], c[6] = c[6], c[3]
	case 2: // Bottom face
		c[0], c[5] = c[5], c[0]
		c[1], c[4] = c[4], c[1]
	case 3: // Front face
		c[5], c[6] = c[6], c[5]
		c[4], c[7] = c[7], c[4]
	case 4: // Back face
		c[0], c[3] = c[3], c[0]
		c[1], c[2] = c[2], c[1]
	case 5: // Right face
		c[1], c[7] = c[7], c[1]
		c[3], c[5] = c[5], c[3]
	case 6: // Left face
		c[0], c[6] = c[6], c[0]
		c[2], c[4] = c[4], c[2]
	default:
		panic("Unsupported half-turn applied to CubieCorners: " +
			strconv.Itoa(face))
	}
}

// Move applies a face turn to the corners.
func (c *CubieCorners) Move(m Move) {
	// Half turns are a simple case.
	if m.Turns == 2 {
		c.HalfTurn(m.Face)
	} else {
		c.QuarterTurn(m.Face, m.Turns)
	}
}

// QuarterTurn performs a 90 degree turn on a given face.
func (c *CubieCorners) QuarterTurn(face, turns int) {
	// This code is not particularly graceful, but it is rather efficient and
	// quite readable compared to a pure array of transformations.
	switch face {
	case 1: // Top face
		if turns == 1 {
			c[2], c[3], c[7], c[6] = c[6], c[2], c[3], c[7]
		} else {
			c[6], c[2], c[3], c[7] = c[2], c[3], c[7], c[6]
		}
		// Swap orientation 0 with orientation 2.
		for _, i := range []int{2, 3, 6, 7} {
			c[i].Orientation = 2 - c[i].Orientation
		}
	case 2: // Bottom face
		if turns == 1 {
			c[4], c[0], c[1], c[5] = c[0], c[1], c[5], c[4]
		} else {
			c[0], c[1], c[5], c[4] = c[4], c[0], c[1], c[5]
		}
		// Swap orientation 0 with orientation 2.
		for _, i := range []int{0, 1, 4, 5} {
			c[i].Orientation = 2 - c[i].Orientation
		}
	case 3: // Front face
		if turns == 1 {
			c[6], c[7], c[5], c[4] = c[4], c[6], c[7], c[5]
		} else {
			c[4], c[6], c[7], c[5] = c[6], c[7], c[5], c[4]
		}
		// Swap orientation 0 with orientation 1.
		for _, i := range []int{4, 5, 6, 7} {
			if c[i].Orientation == 0 {
				c[i].Orientation = 1
			} else if c[i].Orientation == 1 {
				c[i].Orientation = 0
			}
		}
	case 4: // Back face
		if turns == 1 {
			c[0], c[2], c[3], c[1] = c[2], c[3], c[1], c[0]
		} else {
			c[2], c[3], c[1], c[0] = c[0], c[2], c[3], c[1]
		}
		// Swap orientation 0 with orientation 1.
		for _, i := range []int{0, 1, 2, 3} {
			if c[i].Orientation == 0 {
				c[i].Orientation = 1
			} else if c[i].Orientation == 1 {
				c[i].Orientation = 0
			}
		}
	case 5: // Right face
		if turns == 1 {
			c[7], c[3], c[1], c[5] = c[5], c[7], c[3], c[1]
		} else {
			c[5], c[7], c[3], c[1] = c[7], c[3], c[1], c[5]
		}
		// Swap orientation 2 with orientation 1.
		for _, i := range []int{1, 3, 5, 7} {
			if c[i].Orientation == 1 {
				c[i].Orientation = 2
			} else if c[i].Orientation == 2 {
				c[i].Orientation = 1
			}
		}
	case 6: // Left face
		if turns == 1 {
			c[4], c[6], c[2], c[0] = c[6], c[2], c[0], c[4]
		} else {
			c[6], c[2], c[0], c[4] = c[4], c[6], c[2], c[0]
		}
		// Swap orientation 2 with orientation 1.
		for _, i := range []int{0, 2, 4, 6} {
			if c[i].Orientation == 1 {
				c[i].Orientation = 2
			} else if c[i].Orientation == 2 {
				c[i].Orientation = 1
			}
		}
	default:
		panic("Unsupported quarter-turn applied to CubieCorners: " +
			strconv.Itoa(face))
	}
}

// Search starts a search using the receiver as the starting state.
// If the specified CornersPruner is nil, no pruning will be performed.
// The depth argument specifies the maximum depth for the search.
// The branch argument specifies how many levels of the search to parallelize.
func (c *CubieCorners) Search(g CornersGoal, p CornersPruner, moves []Move,
	depth, branch int) Search {
	res := &cornersSearch{newSimpleSearch(moves), g, p}
	go func(st CubieCorners) {
		prefix := make([]Move, 0, depth)
		searchCornersBranch(st, res, depth, branch, prefix)
		close(res.channel)
	}(*c)
	return res
}

// Solved returns true if all the corners are properly positioned and oriented.
func (c *CubieCorners) Solved() bool {
	for i := 0; i < 8; i++ {
		if c[i].Piece != i || c[i].Orientation != 0 {
			return false
		}
	}
	return true
}

// A SolveCornersGoal is satisfied when a CubieCorners is completely solved.
type SolveCornersGoal struct{}

// IsGoal returns corners.Solved().
func (_ SolveCornersGoal) IsGoal(corners CubieCorners) bool {
	return corners.Solved()
}

func searchCorners(st CubieCorners, s *cornersSearch, depth int,
	prefix []Move) {
	// If we can't search any further, check if it's the goal.
	if depth == 0 {
		if s.goal.IsGoal(st) {
			// We must make a copy of the prefix before sending it as a
			// solution, since it may be modified after we return.
			solution := make([]Move, len(prefix))
			copy(solution, prefix)
			s.channel <- solution
		}
		return
	}

	// Prune the state
	if s.prune(st) > depth {
		return
	}

	// Apply each move and recurse.
	for _, move := range s.moves {
		if depth > 5 && s.cancelled() {
			return
		}
		newState := st
		newState.Move(move)
		searchCorners(newState, s, depth-1, append(prefix, move))
	}
}

func searchCornersBranch(st CubieCorners, s *cornersSearch, depth, branch int,
	prefix []Move) {
	// If we shouldn't branch, do a regular search.
	if branch == 0 || depth == 0 {
		searchCorners(st, s, depth, prefix)
		return
	}

	// Prune the state
	if s.prune(st) > depth {
		return
	}

	// Run each search on a different goroutine
	wg := sync.WaitGroup{}
	for _, move := range s.moves {
		wg.Add(1)
		go func(m Move, newState CubieCorners) {
			// Apply the move
			newState.Move(m)

			// Create the new prefix by copying the old one.
			pref := make([]Move, len(prefix)+1, len(prefix)+depth)
			copy(pref, prefix)
			pref[len(prefix)] = m

			// Branch out and search.
			searchCornersBranch(newState, s, depth-1, branch-1, pref)

			wg.Done()
		}(move, st)
	}
	wg.Wait()
}

type cornersSearch struct {
	*simpleSearch
	goal   CornersGoal
	pruner CornersPruner
}

func (c *cornersSearch) prune(state CubieCorners) int {
	if c.pruner == nil {
		return 0
	}
	return c.pruner.MinMoves(state)
}
<|endoftext|>


<commit_before>package main

import (
    "flag"
    "fmt"
    "log"
    "os"
    "math/big"
    "time"

    "github.com/tooru/pnbot/prime"
    "github.com/dghubble/go-twitter/twitter"
    "github.com/dghubble/oauth1"
)

const (
    queueSize = 10
    maxRetry = 10
)

func main() {
    log.SetFlags(log.Ldate | log.Ltime | log.Llongfile)

    flags := flag.NewFlagSet("pnbot", flag.ExitOnError)
    consumerKey := flags.String("ck", "", "Twitter Consumer Key")
    consumerSecret := flags.String("cs", "", "Twitter Consumer Secret")
    accessToken := flags.String("at", "", "Twitter Access Token")
    accessSecret := flags.String("as", "", "Twitter Access Secret")
    flags.Parse(os.Args[1:])

    if *consumerKey == "" || *consumerSecret == "" || *accessToken == "" || *accessSecret == "" {
        log.Fatal("Consumer key/secret and Access token/secret required")
    }

    pnbot := NewPNBot(consumerKey, consumerSecret, accessToken, accessSecret)
    pnbot.Start()
}

func NewPNBot(consumerKey *string, consumerSecret *string,
              accessToken *string, accessSecret *string) *PNBot {
    pnbot := &PNBot{
        consumerKey: consumerKey,
        consumerSecret: consumerSecret,
        accessToken: accessToken,
        accessSecret: accessSecret,
        prime: prime.NewPrime(),
        ch: make(chan *big.Int, queueSize),
    }

    return pnbot
}

type PNBot struct {
    consumerKey *string
    consumerSecret *string
    accessToken *string
    accessSecret *string

    prime *prime.Prime
    ch chan *big.Int

    client *twitter.Client

}

func (pnbot *PNBot) Start() error {
    pnbot.client = pnbot.newClient()

    maxPrime, err := pnbot.getMaxPrime()
    if err != nil {
        log.Printf("getMaxPrime: %v\n", err)
        return err
    }

    go pnbot.makePrimes(maxPrime)

    return pnbot.tweetPrimes()
}

func (pnbot *PNBot) newClient() *twitter.Client {
    config := oauth1.NewConfig(*pnbot.consumerKey, *pnbot.consumerSecret)
    token := oauth1.NewToken(*pnbot.accessToken, *pnbot.accessSecret)
    httpClient := config.Client(oauth1.NoContext, token)

    return twitter.NewClient(httpClient)
}

func (pnbot *PNBot) getMaxPrime() (prime *big.Int, err error) {
    tweets, _, err := pnbot.client.Timelines.HomeTimeline(&twitter.HomeTimelineParams{
        SinceID: 0,
    })
    if err != nil {
        return nil, err
    }

    var maxPrime big.Int

    for _, tweet := range tweets {
        if _, ok := maxPrime.SetString(tweet.Text, 10); ok {
            log.Printf("continue from '%s'", tweet.Text)
            return &maxPrime, nil
        }
        log.Printf("skip '%v'", tweet.Text)
    }
    maxPrime.SetInt64(0)
    return &maxPrime, nil
}

func (pnbot *PNBot) makePrimes(maxPrime *big.Int) {
    var prime *big.Int
    var err error

    for {
        prime, err = pnbot.prime.Next()
        if err != nil {
            log.Fatalf("makePrimes: %v\n", err)
            pnbot.ch <- nil
            return
        }

        if prime.Cmp(maxPrime) > 0 {
            break;
        }
        log.Printf("makePrimes: skip %v\n", prime)
    }

    for {
        log.Printf("makePrimes: found %v\n", prime)
        pnbot.ch <- prime

        prime, err = pnbot.prime.Next()
        if err != nil {
            log.Fatalf("makePrimes: %v\n", err)
            pnbot.ch <- nil
            return
        }
    }
}

func (pnbot *PNBot) tweetPrimes() error {
    interval := 1 * time.Second

    for {
        prime := <- pnbot.ch
        if prime == nil {
            log.Fatalf("prime generator was died")
            return fmt.Errorf("prime generator was died")
        }

        text := prime.Text(10)
        retry := 0
        retryInterval := 10 * time.Second
        for {
            _, _, err := pnbot.client.Statuses.Update(text, nil)
            if err == nil {
                break
            }
            if retry >= maxRetry {
                return fmt.Errorf("Too many tweet error: %v\n", err)
            }
            log.Printf("Tweet error[%d/%d]:sleep=%s: %v\n", retry+1, maxRetry, retryInterval, err)

            time.Sleep(retryInterval)
            pnbot.client = pnbot.newClient()
            retry++
            retryInterval *= 2
            continue
        }
        if retry > 0 {
            interval += 1 * time.Second
        }

        log.Printf("tweet %s :sleep=%s\n", text, interval)
        time.Sleep(interval)
    }
}
<commit_msg>Modified pnbot.go.<commit_after>package main

import (
    "flag"
    "fmt"
    "log"
    "os"
    "math/big"
    "time"

    "github.com/tooru/pnbot/prime"
    "github.com/dghubble/go-twitter/twitter"
    "github.com/dghubble/oauth1"
)

const (
    queueSize = 10
    maxRetry = 10
)

func main() {
    log.SetFlags(log.Ldate | log.Ltime | log.Llongfile)

    flags := flag.NewFlagSet("pnbot", flag.ExitOnError)
    consumerKey := flags.String("ck", "", "Twitter Consumer Key")
    consumerSecret := flags.String("cs", "", "Twitter Consumer Secret")
    accessToken := flags.String("at", "", "Twitter Access Token")
    accessSecret := flags.String("as", "", "Twitter Access Secret")
    flags.Parse(os.Args[1:])

    if *consumerKey == "" || *consumerSecret == "" || *accessToken == "" || *accessSecret == "" {
        log.Fatal("Consumer key/secret and Access token/secret required")
    }

    pnbot := NewPNBot(consumerKey, consumerSecret, accessToken, accessSecret)
    pnbot.Start()
}

func NewPNBot(consumerKey *string, consumerSecret *string,
              accessToken *string, accessSecret *string) *PNBot {
    pnbot := &PNBot{
        consumerKey: consumerKey,
        consumerSecret: consumerSecret,
        accessToken: accessToken,
        accessSecret: accessSecret,
        prime: prime.NewPrime(),
        ch: make(chan *big.Int, queueSize),
    }

    return pnbot
}

type PNBot struct {
    consumerKey *string
    consumerSecret *string
    accessToken *string
    accessSecret *string

    prime *prime.Prime
    ch chan *big.Int

    client *twitter.Client

}

func (pnbot *PNBot) Start() error {
    pnbot.client = pnbot.newClient()

    maxPrime, err := pnbot.getMaxPrime()
    if err != nil {
        log.Printf("getMaxPrime: %v\n", err)
        return err
    }

    go pnbot.makePrimes(maxPrime)

    return pnbot.tweetPrimes()
}

func (pnbot *PNBot) newClient() *twitter.Client {
    config := oauth1.NewConfig(*pnbot.consumerKey, *pnbot.consumerSecret)
    token := oauth1.NewToken(*pnbot.accessToken, *pnbot.accessSecret)
    httpClient := config.Client(oauth1.NoContext, token)

    return twitter.NewClient(httpClient)
}

func (pnbot *PNBot) getMaxPrime() (prime *big.Int, err error) {
    tweets, _, err := pnbot.client.Timelines.HomeTimeline(&twitter.HomeTimelineParams{
        SinceID: 0,
    })
    if err != nil {
        return nil, err
    }

    var maxPrime big.Int

    for _, tweet := range tweets {
        if _, ok := maxPrime.SetString(tweet.Text, 10); ok {
            log.Printf("continue from '%s'", tweet.Text)
            return &maxPrime, nil
        }
        log.Printf("skip '%v'", tweet.Text)
    }
    maxPrime.SetInt64(0)
    return &maxPrime, nil
}

func (pnbot *PNBot) makePrimes(maxPrime *big.Int) {
    var prime *big.Int
    var err error

    for {
        prime, err = pnbot.prime.Next()
        if err != nil {
            log.Fatalf("makePrimes: %v\n", err)
            pnbot.ch <- nil
            return
        }

        if prime.Cmp(maxPrime) > 0 {
            break;
        }
        log.Printf("makePrimes: skip %v\n", prime)
    }

    for {
        log.Printf("makePrimes: found %v\n", prime)
        pnbot.ch <- prime

        prime, err = pnbot.prime.Next()
        if err != nil {
            log.Fatalf("makePrimes: %v\n", err)
            pnbot.ch <- nil
            return
        }
    }
}

func (pnbot *PNBot) tweetPrimes() error {
    interval := 1 * time.Second

    for {
        prime := <- pnbot.ch
        if prime == nil {
            log.Fatalf("prime generator was died")
            return fmt.Errorf("prime generator was died")
        }

        text := prime.Text(10)
        retry := 0
        retryInterval := 10 * time.Second
        for {
            _, _, err := pnbot.client.Statuses.Update(text, nil)
            if err == nil {
                break
            }
            if retry >= maxRetry {
                return fmt.Errorf("Too many tweet error: %v\n", err)
            }
            log.Printf("Tweet error[%d/%d]:sleep=%s: %v\n", retry+1, maxRetry, retryInterval, err)

            time.Sleep(retryInterval)
            pnbot.client = pnbot.newClient()
            retry++
            retryInterval = max(retryInterval * 2, 30 * time.Minute)
            continue
        }
        if retry > 0 {
            interval += 1 * time.Second
        }

        log.Printf("tweet %s :sleep=%s\n", text, interval)
        time.Sleep(interval)
    }
}

func max(a time.Duration, b time.Duration) time.Duration {
    if a > b {
        return b
    } else {
        return a
    }
}
<|endoftext|>