<commit_before>package hkit

import (
	"errors"
	"net/http"
)

type HTTPCache struct {
	cacheService CacheService
}

var (
	errHeaderAlreadyLocked = errors.New("Header has already been locked")
	ErrAlreadyWritten      = errors.New("Response has already been written")
)

// HTTPResponseInterceptor will capture actions on an underlaying
// ResponseWriter. IMPORTANT NOTE: will only capture header state until
// the first call to WriteHeader or Write. Therefore no support for
// trailers etc.
// Behavior may diverge from the implementors in the "net/http", so no
// crazily protective behaviors should be expected from this wrapper.
// TL;DR: you probably have your own cache wrapper if you are writing
// e.g. trailer headers all over your handlers.
type HTTPResponseInterceptor struct {
	tempHeader   http.Header
	lockedHeader http.Header
	writtenBytes []byte
	writtenCode  int
}

func NewHTTPResponseInterceptor(
	w http.ResponseWriter) *HTTPResponseInterceptor {

	interceptor := &HTTPResponseInterceptor{
		tempHeader:   cloneHeader(w.Header()),
		lockedHeader: nil,
		writtenBytes: nil,
		writtenCode:  -1,
	}
	return interceptor
}

func (w *HTTPResponseInterceptor) Write(bytes []byte) (int, error) {
	w.lockHeader()
	if w.writtenBytes != nil {
		return 0, ErrAlreadyWritten
	}
	w.writtenBytes = bytes
	return w.Write(bytes)
}

func (w *HTTPResponseInterceptor) WriteHeader(code int) {
	if err := w.lockHeader(); err != nil {
		return
	}
	w.writtenCode = code
	w.WriteHeader(code)
}

func (w *HTTPResponseInterceptor) Header() http.Header {
	return w.tempHeader
}

func (w *HTTPResponseInterceptor) lockHeader() error {
	if w.lockedHeader != nil {
		return errHeaderAlreadyLocked
	}
	w.lockedHeader = cloneHeader(w.tempHeader)
	overwriteHeader(w.Header(), w.lockedHeader)
	return nil
}

func (w *HTTPResponseInterceptor) Replay(writer http.ResponseWriter) {
	if w.lockedHeader != nil {
		overwriteHeader(writer.Header(), w.lockedHeader)
	}
	if w.writtenCode != -1 {
		writer.WriteHeader(w.writtenCode)
	}
	if w.writtenBytes != nil {
		writer.Write(w.writtenBytes)
	}
}

func cloneHeader(h http.Header) http.Header {
	h2 := make(http.Header, len(h))
	for k, vv := range h {
		vv2 := make([]string, len(vv))
		copy(vv2, vv)
		h2[k] = vv2
	}
	return h2
}

func overwriteHeader(lhs, rhs http.Header) {
	keys := make([]string, 0)
	for k, _ := range lhs {
		keys = append(keys, k)
	}
	for _, k := range keys {
		lhs.Del(k)
	}

	for k, v := range rhs {
		vv := make([]string, len(v))
		copy(vv, v)
		lhs[k] = vv
	}
}
<commit_msg>Fix multiple write bug in http response interceptor<commit_after>package hkit

import (
	"errors"
	"net/http"
)

type HTTPCache struct {
	cacheService CacheService
}

var (
	errHeaderAlreadyLocked = errors.New("Header has already been locked")
	ErrAlreadyWritten      = errors.New("Response has already been written")
)

// HTTPResponseInterceptor will capture actions on an underlaying
// ResponseWriter. IMPORTANT NOTE: will only capture header state until
// the first call to WriteHeader or Write. Therefore no support for
// trailers etc.
// Should be used once. Instantiate a new one for each time you're proxying
// the write process. E.g. instantiate inside your middleware function per req.
// Behavior may diverge from the implementors in the "net/http", so no
// crazily protective behaviors should be expected from this wrapper.
// TL;DR: you probably have your own cache wrapper if you are writing
// e.g. trailer headers all over your handlers.
type HTTPResponseInterceptor struct {
	tempHeader   http.Header
	lockedHeader http.Header
	writtenBytes []byte
	writtenCode  int
}

func NewHTTPResponseInterceptor(
	w http.ResponseWriter) *HTTPResponseInterceptor {

	interceptor := &HTTPResponseInterceptor{
		tempHeader:   cloneHeader(w.Header()),
		lockedHeader: nil,
		writtenBytes: make([]byte, 0),
		writtenCode:  -1,
	}
	return interceptor
}

func (w *HTTPResponseInterceptor) Write(bytes []byte) (int, error) {
	w.lockHeader()
	w.writtenBytes = append(w.writtenBytes, bytes...)
	return w.Write(bytes)
}

func (w *HTTPResponseInterceptor) WriteHeader(code int) {
	if err := w.lockHeader(); err != nil {
		return
	}
	w.writtenCode = code
	w.WriteHeader(code)
}

func (w *HTTPResponseInterceptor) Header() http.Header {
	return w.tempHeader
}

func (w *HTTPResponseInterceptor) lockHeader() error {
	if w.lockedHeader != nil {
		return errHeaderAlreadyLocked
	}
	w.lockedHeader = cloneHeader(w.tempHeader)
	overwriteHeader(w.Header(), w.lockedHeader)
	return nil
}

func (w *HTTPResponseInterceptor) Replay(writer http.ResponseWriter) {
	if w.lockedHeader != nil {
		overwriteHeader(writer.Header(), w.lockedHeader)
	}
	if w.writtenCode != -1 {
		writer.WriteHeader(w.writtenCode)
	}
	writer.Write(w.writtenBytes)
}

func (w *HTTPResponseInterceptor) IsHTTPStatusOK() bool {
	return w.lockedHeader != nil && (w.writtenCode == -1 ||
		w.writtenCode == http.StatusOK)
}

func cloneHeader(h http.Header) http.Header {
	h2 := make(http.Header, len(h))
	for k, vv := range h {
		vv2 := make([]string, len(vv))
		copy(vv2, vv)
		h2[k] = vv2
	}
	return h2
}

func overwriteHeader(lhs, rhs http.Header) {
	keys := make([]string, 0)
	for k, _ := range lhs {
		keys = append(keys, k)
	}
	for _, k := range keys {
		lhs.Del(k)
	}

	for k, v := range rhs {
		vv := make([]string, len(v))
		copy(vv, v)
		lhs[k] = vv
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/labstack/echo"
	mid "github.com/labstack/echo/middleware"
	"net/http"
)

// ManagerWeb основная стркутура объекта Менеджер веб-интерфейса
type ManagerWeb struct {
	e *echo.Echo
	Sign chan Action	// канал по которому передается команда добавления/удаления вебсокета (нового подключения веб-клиента)
	Listch []chan Alarm // массив активных веб-клиентов
	//	TODO структура менеджера веб-интерфейса
}

// Action структура действий с массивом активных вебсокетов
type Action struct {
	Command string		// команда
	Channel chan Alarm	// канал, который слушает активный вебсокет. При получении сигнала обновляет информацию на веб-интерфейсе
}

// Alarm сигнал, который вызывает обновление информации в списке ботов и отправки ее по вебсокету
type Alarm struct {

}

func newManagerWeb() (*ManagerWeb, error) {
	mw := &ManagerWeb{}
	return mw, nil
}

//Start метод запускает веб-интерфейс
func (mw *ManagerWeb) Start() error {
	go func() {
		if mw.e == nil {
			mw.e = echo.New()
		}

		mw.e.Use(mid.Logger()) // выводить лог
		//mw.e.Use(mid.Recover())	// игнорировать ошибки при работе сервера

		mw.e.Get("/", hello) // будущая основная страница

		//api
		mw.e.Get("/api/bots", listbot)                      // вывести json-список текущих ботов
		mw.e.Post("/api/bots", createbot)                   // создать нового бота
		mw.e.Patch("/api/bot/:id/:action", sendactiontobot) // отправить основные команды боту (старт, стоп...)
		mw.e.Delete("/api/bot/:id", deletebot)              // удалить бота

		//websocket
		mw.e.WebSocket("/bots/ws", websockdatabots) // вебсокет для динамического обновления информация по списку ботов

		// служебные вызовы на время разработки
		mw.e.Get("/api/bots/upd", updateinfbots) // иницировать обновление информации в списке ботов
		mw.e.Post("/api/bot/test", testbot)

		//		TODO инициализация настроек сервера (ip с которых можно принимать запросы, порт и т.д.)
		mw.e.Run(":8080")
	}()

	return nil
}

//Stop метод останавливает веб-интерфейс
func (mw *ManagerWeb) Stop() error {
	//	TODO остановка веб-интерфейса
	return nil
}

//Restart метод останавливает и запускает (перезапускает) веб-интерфейс
func (mw *ManagerWeb) Restart() error {
	var err error

	// остановить веб-интерфейс
	err = mw.Stop()
	if err != nil {
		return err
	}

	// запустить веб-интерфейс
	err = mw.Start()
	if err != nil {
		return err
	}
	return nil
}

func hello(c *echo.Context) error {
	//	TODO когда-нибудь будет выводить основную страницу
	return c.String(http.StatusOK, "ok\n")
}
<commit_msg>Добавлена функция-горутина, поддерживающая актуальный массив текущих подключений веб-клиентов по вебсокету.<commit_after>package main

import (
	"github.com/labstack/echo"
	mid "github.com/labstack/echo/middleware"
	"net/http"
	"fmt"
)

// ManagerWeb основная стркутура объекта Менеджер веб-интерфейса
type ManagerWeb struct {
	e *echo.Echo
	Sign chan Action	// канал по которому передается команда добавления/удаления вебсокета (нового подключения веб-клиента)
	Listch []chan Alarm // массив активных веб-клиентов
	//	TODO структура менеджера веб-интерфейса
}

// Action структура действий с массивом активных вебсокетов
type Action struct {
	Command string		// команда
	Channel chan Alarm	// канал, который слушает активный вебсокет. При получении сигнала обновляет информацию на веб-интерфейсе
}

// Alarm сигнал, который вызывает обновление информации в списке ботов и отправки ее по вебсокету
type Alarm struct {

}

func newManagerWeb() (*ManagerWeb, error) {
	mw := &ManagerWeb{}
	return mw, nil
}

//Start метод запускает веб-интерфейс
func (mw *ManagerWeb) Start() error {
	go func() {
		if mw.e == nil {
			mw.e = echo.New()
		}

		mw.e.Use(mid.Logger()) // выводить лог
		//mw.e.Use(mid.Recover())	// игнорировать ошибки при работе сервера

		mw.e.Get("/", hello) // будущая основная страница

		//api
		mw.e.Get("/api/bots", listbot)                      // вывести json-список текущих ботов
		mw.e.Post("/api/bots", createbot)                   // создать нового бота
		mw.e.Patch("/api/bot/:id/:action", sendactiontobot) // отправить основные команды боту (старт, стоп...)
		mw.e.Delete("/api/bot/:id", deletebot)              // удалить бота

		//websocket
		mw.e.WebSocket("/bots/ws", websockdatabots) // вебсокет для динамического обновления информация по списку ботов

		// служебные вызовы на время разработки
		mw.e.Get("/api/bots/upd", updateinfbots) // иницировать обновление информации в списке ботов
		mw.e.Post("/api/bot/test", testbot)

		//		TODO инициализация настроек сервера (ip с которых можно принимать запросы, порт и т.д.)
		mw.e.Run(":8080")
	}()

	return nil
}

// manager фоновая горутина, отслеживает актуальный список подключившихся веб-клиентов
func (mw *ManagerWeb) manager(ch <-chan Action) {
	for {
		act := <-ch // получить команду
		switch act.Command {
		case "add":		// добавить в масиив канал, который слушает новый вебсокет
			mw.Listch = append(mw.Listch, act.Channel)
		case "del":		// удалить из массива канал
			for i:= 0; i < len(mw.Listch); i++ {
				if mw.Listch[i] == act.Channel {
					mw.Listch = append(mw.Listch[:i], mw.Listch[i+1:]...)
					break
				}
			}
		}
		fmt.Println(mw.Listch)
	}
}

//Stop метод останавливает веб-интерфейс
func (mw *ManagerWeb) Stop() error {
	//	TODO остановка веб-интерфейса
	return nil
}

//Restart метод останавливает и запускает (перезапускает) веб-интерфейс
func (mw *ManagerWeb) Restart() error {
	var err error

	// остановить веб-интерфейс
	err = mw.Stop()
	if err != nil {
		return err
	}

	// запустить веб-интерфейс
	err = mw.Start()
	if err != nil {
		return err
	}
	return nil
}

func hello(c *echo.Context) error {
	//	TODO когда-нибудь будет выводить основную страницу
	return c.String(http.StatusOK, "ok\n")
}
<|endoftext|>


<commit_before>package calcium

import (
	"context"
	"fmt"
	"sync"

	"github.com/projecteru2/core/cluster"
	enginetypes "github.com/projecteru2/core/engine/types"
	"github.com/projecteru2/core/metrics"
	"github.com/projecteru2/core/store"
	"github.com/projecteru2/core/types"
	"github.com/projecteru2/core/utils"
	"github.com/sanity-io/litter"
	log "github.com/sirupsen/logrus"
)

// CreateContainer use options to create containers
func (c *Calcium) CreateContainer(ctx context.Context, opts *types.DeployOptions) (chan *types.CreateContainerMessage, error) {
	opts.ProcessIdent = utils.RandomString(16)
	pod, err := c.store.GetPod(ctx, opts.Podname)
	if err != nil {
		log.Errorf("[CreateContainer %s] Error during GetPod for %s: %v", opts.ProcessIdent, opts.Podname, err)
		return nil, err
	}
	log.Infof("[CreateContainer %s] Creating container with options:", opts.ProcessIdent)
	litter.Dump(opts)

	// 4194304 Byte = 4 MB, 创建容器的内存最低标准
	if opts.Memory < minMemory {
		return nil, types.NewDetailedErr(types.ErrBadMemory,
			fmt.Sprintf("Minimum memory limit allowed is 4MB, got %d", opts.Memory))
	}
	// Count 要大于0
	if opts.Count <= 0 {
		return nil, types.NewDetailedErr(types.ErrBadCount, opts.Count)
	}
	// CPUQuota 也需要大于 0
	if opts.CPUQuota <= 0 {
		return nil, types.NewDetailedErr(types.ErrBadCPU, opts.CPUQuota)
	}

	return c.doCreateContainer(ctx, opts, pod)
}

func (c *Calcium) doCreateContainer(ctx context.Context, opts *types.DeployOptions, pod *types.Pod) (chan *types.CreateContainerMessage, error) {
	ch := make(chan *types.CreateContainerMessage)
	// RFC 计算当前 app 部署情况的时候需要保证同一时间只有这个 app 的这个 entrypoint 在跑
	// 因此需要在这里加个全局锁，直到部署完毕才释放
	// 通过 Processing 状态跟踪达成 18 Oct, 2018
	nodesInfo, err := c.doAllocResource(ctx, opts)
	if err != nil {
		log.Errorf("[doCreateContainer] Error during alloc resource: %v", err)
		return ch, err
	}

	go func() {
		defer close(ch)
		wg := sync.WaitGroup{}
		wg.Add(len(nodesInfo))
		index := 0

		// do deployment by each node
		for _, nodeInfo := range nodesInfo {
			go metrics.Client.SendDeployCount(nodeInfo.Deploy)
			go func(nodeInfo types.NodeInfo, index int) {
				defer wg.Done()
				defer c.store.DeleteProcessing(ctx, opts, nodeInfo)
				messages := c.doCreateContainerOnNode(ctx, nodeInfo, opts, index)
				for i, m := range messages {
					ch <- m
					if m.Error != nil && m.ContainerID == "" {
						if err := c.withNodeLocked(ctx, opts.Podname, nodeInfo.Name, func(node *types.Node) error {
							return c.store.UpdateNodeResource(ctx, node, m.CPU, opts.CPUQuota, opts.Memory, opts.Storage, store.ActionIncr)
						}); err != nil {
							log.Errorf("[doCreateContainer] Reset node %s failed %v", nodeInfo.Name, err)
						}
					} else if m.Error != nil && m.ContainerID != "" {
						log.Warnf("[doCreateContainer] Create container failed %v, and container %s not removed", m.Error, m.ContainerID)
					}
					// decr processing count
					if err := c.store.UpdateProcessing(ctx, opts, nodeInfo.Name, nodeInfo.Deploy-i-1); err != nil {
						log.Warnf("[doCreateContainer] Update processing count failed %v", err)
					}
				}
			}(nodeInfo, index)
			index += nodeInfo.Deploy
		}
		wg.Wait()
	}()

	return ch, nil
}

func (c *Calcium) doCreateContainerOnNode(ctx context.Context, nodeInfo types.NodeInfo, opts *types.DeployOptions, index int) []*types.CreateContainerMessage {
	ms := make([]*types.CreateContainerMessage, nodeInfo.Deploy)

	node, err := c.doGetAndPrepareNode(ctx, opts.Podname, nodeInfo.Name, opts.Image)
	if err != nil {
		log.Errorf("[doCreateContainerOnNode] Get and prepare node error %v", err)
		for i := 0; i < nodeInfo.Deploy; i++ {
			cpu := types.CPUMap{}
			if len(nodeInfo.CPUPlan) > 0 {
				cpu = nodeInfo.CPUPlan[i]
			}
			ms[i] = &types.CreateContainerMessage{Error: err, CPU: cpu}
		}
		return ms
	}

	for i := 0; i < nodeInfo.Deploy; i++ {
		// createAndStartContainer will auto cleanup
		cpu := types.CPUMap{}
		if len(nodeInfo.CPUPlan) > 0 {
			cpu = nodeInfo.CPUPlan[i]
		}
		ms[i] = c.doCreateAndStartContainer(ctx, i+index, node, opts, cpu)
		if !ms[i].Success {
			log.Errorf("[doCreateContainerOnNode] Error when create and start a container, %v", ms[i].Error)
			continue
		}
		log.Infof("[doCreateContainerOnNode] create container success %s", ms[i].ContainerID)
	}

	return ms
}

func (c *Calcium) doGetAndPrepareNode(ctx context.Context, podname, nodename, image string) (*types.Node, error) {
	node, err := c.GetNode(ctx, podname, nodename)
	if err != nil {
		return nil, err
	}

	return node, pullImage(ctx, node, image)
}

func (c *Calcium) doCreateAndStartContainer(
	ctx context.Context,
	no int, node *types.Node,
	opts *types.DeployOptions,
	cpu types.CPUMap,
) *types.CreateContainerMessage {
	container := &types.Container{
		Podname:    opts.Podname,
		Nodename:   node.Name,
		CPU:        cpu,
		Quota:      opts.CPUQuota,
		Memory:     opts.Memory,
		Storage:    opts.Storage,
		Hook:       opts.Entrypoint.Hook,
		Privileged: opts.Entrypoint.Privileged,
		Engine:     node.Engine,
		SoftLimit:  opts.SoftLimit,
		Image:      opts.Image,
	}
	createContainerMessage := &types.CreateContainerMessage{
		Podname:  container.Podname,
		Nodename: container.Nodename,
		Success:  false,
		CPU:      cpu,
		Quota:    opts.CPUQuota,
		Memory:   opts.Memory,
		Storage:  opts.Storage,
		Publish:  map[string][]string{},
	}

	defer func() {
		if !createContainerMessage.Success && container.ID != "" {
			if err := c.doRemoveContainer(ctx, container, true); err != nil {
				log.Errorf("[doCreateAndStartContainer] create and start container failed, and remove it failed also %v", err)
				return
			}
			createContainerMessage.ContainerID = ""
		}
	}()

	// get config
	config := c.doMakeContainerOptions(no, cpu, opts, node)
	container.Name = config.Name
	createContainerMessage.ContainerName = container.Name

	// create container
	containerCreated, err := node.Engine.VirtualizationCreate(ctx, config)
	if err != nil {
		createContainerMessage.Error = err
		return createContainerMessage
	}
	container.ID = containerCreated.ID
	createContainerMessage.ContainerID = containerCreated.ID

	// Copy data to container
	if len(opts.Data) > 0 {
		for dst, src := range opts.Data {
			if err := c.doSendFileToContainer(ctx, node.Engine, containerCreated.ID, dst, src, true, true); err != nil {
				createContainerMessage.Error = err
				return createContainerMessage
			}
		}
	}

	// deal with hook
	hook := container.Hook
	if len(opts.AfterCreate) > 0 {
		cmds := opts.AfterCreate
		if hook != nil {
			cmds = append(cmds, hook.AfterStart...)
		}
		container.Hook = &types.Hook{
			AfterStart: cmds,
			Force:      hook.Force,
		}
	}

	// store eru container
	if err = c.store.AddContainer(ctx, container); err != nil {
		createContainerMessage.Error = err
		return createContainerMessage
	}

	// start first
	createContainerMessage.Hook, err = c.doStartContainer(ctx, container, opts.IgnoreHook)
	if err != nil {
		createContainerMessage.Error = err
		return createContainerMessage
	}

	// inspect real meta
	containerInfo, err := container.Inspect(ctx) // 补充静态元数据
	if err != nil {
		createContainerMessage.Error = err
		return createContainerMessage
	}

	// update meta
	if containerInfo.Networks != nil {
		createContainerMessage.Publish = utils.MakePublishInfo(containerInfo.Networks, opts.Entrypoint.Publish)
	}
	container.User = containerInfo.User
	container.Labels = containerInfo.Labels
	container.Env = containerInfo.Env
	container.Hook = hook
	container.Running = containerInfo.Running
	container.Networks = containerInfo.Networks

	// update store meta
	if err = c.store.UpdateContainer(ctx, container); err != nil {
		createContainerMessage.Error = err
		return createContainerMessage
	}

	// mark success
	createContainerMessage.Success = true
	return createContainerMessage
}

func (c *Calcium) doMakeContainerOptions(index int, cpumap types.CPUMap, opts *types.DeployOptions, node *types.Node) *enginetypes.VirtualizationCreateOptions {
	config := &enginetypes.VirtualizationCreateOptions{}
	config.Seq = index
	config.CPU = cpumap.Map()
	config.Quota = opts.CPUQuota
	config.Memory = opts.Memory
	config.Storage = opts.Storage
	config.NUMANode = node.GetNUMANode(cpumap)
	config.SoftLimit = opts.SoftLimit
	entry := opts.Entrypoint

	// 如果有指定用户，用指定用户
	// 没有指定用户，用镜像自己的
	// CapAdd and Privileged
	user := opts.User
	config.CapAdd = []string{}
	if entry.Privileged {
		user = root
		config.CapAdd = append(config.CapAdd, "SYS_ADMIN")
	}

	// command and user
	// extra args is dynamically
	slices := utils.MakeCommandLineArgs(fmt.Sprintf("%s %s", entry.Command, opts.ExtraArgs))
	config.Cmd = slices

	// env
	env := append(opts.Env, fmt.Sprintf("APP_NAME=%s", opts.Name))
	env = append(env, fmt.Sprintf("ERU_POD=%s", opts.Podname))
	env = append(env, fmt.Sprintf("ERU_NODE_NAME=%s", node.Name))
	env = append(env, fmt.Sprintf("ERU_CONTAINER_NO=%d", index))
	env = append(env, fmt.Sprintf("ERU_MEMORY=%d", opts.Memory))
	env = append(env, fmt.Sprintf("ERU_STORAGE=%d", opts.Storage))

	// log config
	// 默认是配置里的driver, 如果entrypoint有指定就用指定的.
	// 如果用 debug 模式就用默认配置的
	logType := c.config.Docker.Log.Type
	logConfig := c.config.Docker.Log.Config
	if logConfig == nil {
		logConfig = map[string]string{}
	}
	logConfig["tag"] = fmt.Sprintf("%s {{.ID}}", opts.Name)
	if entry.Log != nil && !opts.Debug {
		logType = entry.Log.Type
		logConfig = entry.Log.Config
	}
	config.LogType = logType
	config.LogConfig = logConfig

	// basic labels, bind to EruMeta
	config.Labels = map[string]string{
		cluster.ERUMark: "1",
		cluster.ERUMeta: utils.EncodeMetaInLabel(&types.EruMeta{
			Publish:     opts.Entrypoint.Publish,
			HealthCheck: entry.HealthCheck,
		}),
	}

	// 接下来是meta
	for key, value := range opts.Labels {
		config.Labels[key] = value
	}

	// ulimit
	config.Ulimits = map[string]*enginetypes.VirtualizationUlimits{
		"nofile": &enginetypes.VirtualizationUlimits{Soft: 65535, Hard: 65535},
	}

	// name
	suffix := utils.RandomString(6)
	config.Name = utils.MakeContainerName(opts.Name, opts.Entrypoint.Name, suffix)

	// network mode 和 networks 互斥
	// 没有 networks 的时候用 networkmode 的值
	// 有 networks 的时候一律用用 networks 的值作为 mode
	config.Network = opts.NetworkMode
	config.Networks = opts.Networks
	for name := range opts.Networks {
		config.Network = name
		break
	}
	// 如果没有 network 用默认值替换
	if config.Network == "" {
		config.Network = c.config.Docker.NetworkMode
	}

	// dns
	config.DNS = opts.DNS
	// sysctls
	config.Sysctl = entry.Sysctls

	// restart
	config.RestartPolicy = entry.RestartPolicy
	config.RestartRetryCount = 3
	if config.RestartPolicy == restartAlways {
		config.RestartRetryCount = 0
	}

	// general
	config.User = user
	config.Image = opts.Image
	config.WorkingDir = entry.Dir
	config.Stdin = opts.OpenStdin
	config.Privileged = entry.Privileged
	config.Env = env
	config.Hosts = opts.ExtraHosts
	config.Publish = entry.Publish
	config.NetworkDisabled = false
	config.Volumes = opts.Volumes
	return config
}
<commit_msg>minor fix: compose create container opts properly<commit_after>package calcium

import (
	"context"
	"fmt"
	"sync"

	"github.com/projecteru2/core/cluster"
	enginetypes "github.com/projecteru2/core/engine/types"
	"github.com/projecteru2/core/metrics"
	"github.com/projecteru2/core/store"
	"github.com/projecteru2/core/types"
	"github.com/projecteru2/core/utils"
	"github.com/sanity-io/litter"
	log "github.com/sirupsen/logrus"
)

// CreateContainer use options to create containers
func (c *Calcium) CreateContainer(ctx context.Context, opts *types.DeployOptions) (chan *types.CreateContainerMessage, error) {
	opts.ProcessIdent = utils.RandomString(16)
	pod, err := c.store.GetPod(ctx, opts.Podname)
	if err != nil {
		log.Errorf("[CreateContainer %s] Error during GetPod for %s: %v", opts.ProcessIdent, opts.Podname, err)
		return nil, err
	}
	log.Infof("[CreateContainer %s] Creating container with options:", opts.ProcessIdent)
	litter.Dump(opts)

	// 4194304 Byte = 4 MB, 创建容器的内存最低标准
	if opts.Memory < minMemory {
		return nil, types.NewDetailedErr(types.ErrBadMemory,
			fmt.Sprintf("Minimum memory limit allowed is 4MB, got %d", opts.Memory))
	}
	// Count 要大于0
	if opts.Count <= 0 {
		return nil, types.NewDetailedErr(types.ErrBadCount, opts.Count)
	}
	// CPUQuota 也需要大于 0
	if opts.CPUQuota <= 0 {
		return nil, types.NewDetailedErr(types.ErrBadCPU, opts.CPUQuota)
	}

	return c.doCreateContainer(ctx, opts, pod)
}

func (c *Calcium) doCreateContainer(ctx context.Context, opts *types.DeployOptions, pod *types.Pod) (chan *types.CreateContainerMessage, error) {
	ch := make(chan *types.CreateContainerMessage)
	// RFC 计算当前 app 部署情况的时候需要保证同一时间只有这个 app 的这个 entrypoint 在跑
	// 因此需要在这里加个全局锁，直到部署完毕才释放
	// 通过 Processing 状态跟踪达成 18 Oct, 2018
	nodesInfo, err := c.doAllocResource(ctx, opts)
	if err != nil {
		log.Errorf("[doCreateContainer] Error during alloc resource: %v", err)
		return ch, err
	}

	go func() {
		defer close(ch)
		wg := sync.WaitGroup{}
		wg.Add(len(nodesInfo))
		index := 0

		// do deployment by each node
		for _, nodeInfo := range nodesInfo {
			go metrics.Client.SendDeployCount(nodeInfo.Deploy)
			go func(nodeInfo types.NodeInfo, index int) {
				defer wg.Done()
				defer c.store.DeleteProcessing(ctx, opts, nodeInfo)
				messages := c.doCreateContainerOnNode(ctx, nodeInfo, opts, index)
				for i, m := range messages {
					ch <- m
					if m.Error != nil && m.ContainerID == "" {
						if err := c.withNodeLocked(ctx, opts.Podname, nodeInfo.Name, func(node *types.Node) error {
							return c.store.UpdateNodeResource(ctx, node, m.CPU, opts.CPUQuota, opts.Memory, opts.Storage, store.ActionIncr)
						}); err != nil {
							log.Errorf("[doCreateContainer] Reset node %s failed %v", nodeInfo.Name, err)
						}
					} else if m.Error != nil && m.ContainerID != "" {
						log.Warnf("[doCreateContainer] Create container failed %v, and container %s not removed", m.Error, m.ContainerID)
					}
					// decr processing count
					if err := c.store.UpdateProcessing(ctx, opts, nodeInfo.Name, nodeInfo.Deploy-i-1); err != nil {
						log.Warnf("[doCreateContainer] Update processing count failed %v", err)
					}
				}
			}(nodeInfo, index)
			index += nodeInfo.Deploy
		}
		wg.Wait()
	}()

	return ch, nil
}

func (c *Calcium) doCreateContainerOnNode(ctx context.Context, nodeInfo types.NodeInfo, opts *types.DeployOptions, index int) []*types.CreateContainerMessage {
	ms := make([]*types.CreateContainerMessage, nodeInfo.Deploy)

	node, err := c.doGetAndPrepareNode(ctx, opts.Podname, nodeInfo.Name, opts.Image)
	if err != nil {
		log.Errorf("[doCreateContainerOnNode] Get and prepare node error %v", err)
		for i := 0; i < nodeInfo.Deploy; i++ {
			cpu := types.CPUMap{}
			if len(nodeInfo.CPUPlan) > 0 {
				cpu = nodeInfo.CPUPlan[i]
			}
			ms[i] = &types.CreateContainerMessage{Error: err, CPU: cpu}
		}
		return ms
	}

	for i := 0; i < nodeInfo.Deploy; i++ {
		// createAndStartContainer will auto cleanup
		cpu := types.CPUMap{}
		if len(nodeInfo.CPUPlan) > 0 {
			cpu = nodeInfo.CPUPlan[i]
		}
		ms[i] = c.doCreateAndStartContainer(ctx, i+index, node, opts, cpu)
		if !ms[i].Success {
			log.Errorf("[doCreateContainerOnNode] Error when create and start a container, %v", ms[i].Error)
			continue
		}
		log.Infof("[doCreateContainerOnNode] create container success %s", ms[i].ContainerID)
	}

	return ms
}

func (c *Calcium) doGetAndPrepareNode(ctx context.Context, podname, nodename, image string) (*types.Node, error) {
	node, err := c.GetNode(ctx, podname, nodename)
	if err != nil {
		return nil, err
	}

	return node, pullImage(ctx, node, image)
}

func (c *Calcium) doCreateAndStartContainer(
	ctx context.Context,
	no int, node *types.Node,
	opts *types.DeployOptions,
	cpu types.CPUMap,
) *types.CreateContainerMessage {
	container := &types.Container{
		Podname:    opts.Podname,
		Nodename:   node.Name,
		CPU:        cpu,
		Quota:      opts.CPUQuota,
		Memory:     opts.Memory,
		Storage:    opts.Storage,
		Hook:       opts.Entrypoint.Hook,
		Privileged: opts.Entrypoint.Privileged,
		Engine:     node.Engine,
		SoftLimit:  opts.SoftLimit,
		Image:      opts.Image,
	}
	createContainerMessage := &types.CreateContainerMessage{
		Podname:  container.Podname,
		Nodename: container.Nodename,
		Success:  false,
		CPU:      cpu,
		Quota:    opts.CPUQuota,
		Memory:   opts.Memory,
		Storage:  opts.Storage,
		Publish:  map[string][]string{},
	}

	defer func() {
		if !createContainerMessage.Success && container.ID != "" {
			if err := c.doRemoveContainer(ctx, container, true); err != nil {
				log.Errorf("[doCreateAndStartContainer] create and start container failed, and remove it failed also %v", err)
				return
			}
			createContainerMessage.ContainerID = ""
		}
	}()

	// get config
	config := c.doMakeContainerOptions(no, cpu, opts, node)
	container.Name = config.Name
	createContainerMessage.ContainerName = container.Name

	// create container
	containerCreated, err := node.Engine.VirtualizationCreate(ctx, config)
	if err != nil {
		createContainerMessage.Error = err
		return createContainerMessage
	}
	container.ID = containerCreated.ID
	createContainerMessage.ContainerID = containerCreated.ID

	// Copy data to container
	if len(opts.Data) > 0 {
		for dst, src := range opts.Data {
			if err := c.doSendFileToContainer(ctx, node.Engine, containerCreated.ID, dst, src, true, true); err != nil {
				createContainerMessage.Error = err
				return createContainerMessage
			}
		}
	}

	// deal with hook
	hook := container.Hook
	if len(opts.AfterCreate) > 0 {
		cmds := opts.AfterCreate
		if hook != nil {
			cmds = append(cmds, hook.AfterStart...)
		}
		container.Hook = &types.Hook{
			AfterStart: cmds,
			Force:      hook.Force,
		}
	}

	// store eru container
	if err = c.store.AddContainer(ctx, container); err != nil {
		createContainerMessage.Error = err
		return createContainerMessage
	}

	// start first
	createContainerMessage.Hook, err = c.doStartContainer(ctx, container, opts.IgnoreHook)
	if err != nil {
		createContainerMessage.Error = err
		return createContainerMessage
	}

	// inspect real meta
	containerInfo, err := container.Inspect(ctx) // 补充静态元数据
	if err != nil {
		createContainerMessage.Error = err
		return createContainerMessage
	}

	// update meta
	if containerInfo.Networks != nil {
		createContainerMessage.Publish = utils.MakePublishInfo(containerInfo.Networks, opts.Entrypoint.Publish)
	}
	container.User = containerInfo.User
	container.Labels = containerInfo.Labels
	container.Env = containerInfo.Env
	container.Hook = hook
	container.Running = containerInfo.Running
	container.Networks = containerInfo.Networks

	// update store meta
	if err = c.store.UpdateContainer(ctx, container); err != nil {
		createContainerMessage.Error = err
		return createContainerMessage
	}

	// mark success
	createContainerMessage.Success = true
	return createContainerMessage
}

func (c *Calcium) doMakeContainerOptions(index int, cpumap types.CPUMap, opts *types.DeployOptions, node *types.Node) *enginetypes.VirtualizationCreateOptions {
	config := &enginetypes.VirtualizationCreateOptions{}
	config.Seq = index
	config.CPU = cpumap.Map()
	config.Quota = opts.CPUQuota
	config.Memory = opts.Memory
	config.Storage = opts.Storage
	config.NUMANode = node.GetNUMANode(cpumap)
	config.SoftLimit = opts.SoftLimit
	config.RawArgs = opts.RawArgs
	entry := opts.Entrypoint

	// 如果有指定用户，用指定用户
	// 没有指定用户，用镜像自己的
	// CapAdd and Privileged
	user := opts.User
	config.CapAdd = []string{}
	if entry.Privileged {
		user = root
		config.CapAdd = append(config.CapAdd, "SYS_ADMIN")
	}

	// command and user
	// extra args is dynamically
	slices := utils.MakeCommandLineArgs(fmt.Sprintf("%s %s", entry.Command, opts.ExtraArgs))
	config.Cmd = slices

	// env
	env := append(opts.Env, fmt.Sprintf("APP_NAME=%s", opts.Name))
	env = append(env, fmt.Sprintf("ERU_POD=%s", opts.Podname))
	env = append(env, fmt.Sprintf("ERU_NODE_NAME=%s", node.Name))
	env = append(env, fmt.Sprintf("ERU_CONTAINER_NO=%d", index))
	env = append(env, fmt.Sprintf("ERU_MEMORY=%d", opts.Memory))
	env = append(env, fmt.Sprintf("ERU_STORAGE=%d", opts.Storage))

	// log config
	// 默认是配置里的driver, 如果entrypoint有指定就用指定的.
	// 如果用 debug 模式就用默认配置的
	logType := c.config.Docker.Log.Type
	logConfig := c.config.Docker.Log.Config
	if logConfig == nil {
		logConfig = map[string]string{}
	}
	logConfig["tag"] = fmt.Sprintf("%s {{.ID}}", opts.Name)
	if entry.Log != nil && !opts.Debug {
		logType = entry.Log.Type
		logConfig = entry.Log.Config
	}
	config.LogType = logType
	config.LogConfig = logConfig

	// basic labels, bind to EruMeta
	config.Labels = map[string]string{
		cluster.ERUMark: "1",
		cluster.ERUMeta: utils.EncodeMetaInLabel(&types.EruMeta{
			Publish:     opts.Entrypoint.Publish,
			HealthCheck: entry.HealthCheck,
		}),
	}

	// 接下来是meta
	for key, value := range opts.Labels {
		config.Labels[key] = value
	}

	// ulimit
	config.Ulimits = map[string]*enginetypes.VirtualizationUlimits{
		"nofile": &enginetypes.VirtualizationUlimits{Soft: 65535, Hard: 65535},
	}

	// name
	suffix := utils.RandomString(6)
	config.Name = utils.MakeContainerName(opts.Name, opts.Entrypoint.Name, suffix)

	// network mode 和 networks 互斥
	// 没有 networks 的时候用 networkmode 的值
	// 有 networks 的时候一律用用 networks 的值作为 mode
	config.Network = opts.NetworkMode
	config.Networks = opts.Networks
	for name := range opts.Networks {
		config.Network = name
		break
	}
	// 如果没有 network 用默认值替换
	if config.Network == "" {
		config.Network = c.config.Docker.NetworkMode
	}

	// dns
	config.DNS = opts.DNS
	// sysctls
	config.Sysctl = entry.Sysctls

	// restart
	config.RestartPolicy = entry.RestartPolicy
	config.RestartRetryCount = 3
	if config.RestartPolicy == restartAlways {
		config.RestartRetryCount = 0
	}

	// general
	config.User = user
	config.Image = opts.Image
	config.WorkingDir = entry.Dir
	config.Stdin = opts.OpenStdin
	config.Privileged = entry.Privileged
	config.Env = env
	config.Hosts = opts.ExtraHosts
	config.Publish = entry.Publish
	config.NetworkDisabled = false
	config.Volumes = opts.Volumes
	return config
}
<|endoftext|>


<commit_before>package containercommands

import (
	"fmt"

	"github.com/jrperritt/rack/handler"
	"github.com/jrperritt/rack/internal/github.com/codegangsta/cli"
	"github.com/jrperritt/rack/internal/github.com/rackspace/gophercloud/rackspace/objectstorage/v1/containers"
	"github.com/jrperritt/rack/util"
)

var update = cli.Command{
	Name:        "update",
	Usage:       util.Usage(commandPrefix, "update", "--name <containerName>"),
	Description: "Updates a container",
	Action:      actionUpdate,
	Flags:       util.CommandFlags(flagsUpdate, keysUpdate),
	BashComplete: func(c *cli.Context) {
		util.CompleteFlags(util.CommandFlags(flagsUpdate, keysUpdate))
	},
}

func flagsUpdate() []cli.Flag {
	return []cli.Flag{
		cli.StringFlag{
			Name:  "name",
			Usage: "[required] The name of the container",
		},
		cli.StringFlag{
			Name:  "metadata",
			Usage: "[optional] Comma-separated key-value pairs for the container. Example: key1=val1,key2=val2",
		},
		cli.StringFlag{
			Name:  "container-read",
			Usage: "[optional] Comma-separated list of users for whom to grant read access to the container",
		},
		cli.StringFlag{
			Name:  "container-write",
			Usage: "[optional] Comma-separated list of users for whom to grant write access to the container",
		},
	}
}

var keysUpdate = []string{}

type paramsUpdate struct {
	container string
	opts      containers.UpdateOpts
}

type commandUpdate handler.Command

func actionUpdate(c *cli.Context) {
	command := &commandUpdate{
		Ctx: &handler.Context{
			CLIContext: c,
		},
	}
	handler.Handle(command)
}

func (command *commandUpdate) Context() *handler.Context {
	return command.Ctx
}

func (command *commandUpdate) Keys() []string {
	return keysUpdate
}

func (command *commandUpdate) ServiceClientType() string {
	return serviceClientType
}

func (command *commandUpdate) HandleFlags(resource *handler.Resource) error {
	c := command.Ctx.CLIContext
	opts := containers.UpdateOpts{
		ContainerRead:  c.String("container-read"),
		ContainerWrite: c.String("container-write"),
	}
	if c.IsSet("metadata") {
		metadata, err := command.Ctx.CheckKVFlag("metadata")
		if err != nil {
			return err
		}
		opts.Metadata = metadata
	}
	resource.Params = &paramsUpdate{
		opts: opts,
	}
	return nil
}

func (command *commandUpdate) HandleSingle(resource *handler.Resource) error {
	err := command.Ctx.CheckFlagsSet([]string{"name"})
	if err != nil {
		return err
	}
	resource.Params.(*paramsCreate).container = command.Ctx.CLIContext.String("name")
	return nil
}

func (command *commandUpdate) Execute(resource *handler.Resource) {
	params := resource.Params.(*paramsUpdate)
	containerName := params.container
	opts := params.opts
	rawResponse := containers.Update(command.Ctx.ServiceClient, containerName, opts)
	if rawResponse.Err != nil {
		resource.Err = rawResponse.Err
		return
	}
	resource.Result = fmt.Sprintf("Successfully updated container [%s]\n", containerName)
}
<commit_msg>remove HandleSingle method<commit_after>package containercommands

import (
	"fmt"

	"github.com/jrperritt/rack/handler"
	"github.com/jrperritt/rack/internal/github.com/codegangsta/cli"
	"github.com/jrperritt/rack/internal/github.com/rackspace/gophercloud/rackspace/objectstorage/v1/containers"
	"github.com/jrperritt/rack/util"
)

var update = cli.Command{
	Name:        "update",
	Usage:       util.Usage(commandPrefix, "update", "--name <containerName>"),
	Description: "Updates a container",
	Action:      actionUpdate,
	Flags:       util.CommandFlags(flagsUpdate, keysUpdate),
	BashComplete: func(c *cli.Context) {
		util.CompleteFlags(util.CommandFlags(flagsUpdate, keysUpdate))
	},
}

func flagsUpdate() []cli.Flag {
	return []cli.Flag{
		cli.StringFlag{
			Name:  "name",
			Usage: "[required] The name of the container",
		},
		cli.StringFlag{
			Name:  "metadata",
			Usage: "[optional] Comma-separated key-value pairs for the container. Example: key1=val1,key2=val2",
		},
		cli.StringFlag{
			Name:  "container-read",
			Usage: "[optional] Comma-separated list of users for whom to grant read access to the container",
		},
		cli.StringFlag{
			Name:  "container-write",
			Usage: "[optional] Comma-separated list of users for whom to grant write access to the container",
		},
	}
}

var keysUpdate = []string{}

type paramsUpdate struct {
	container string
	opts      containers.UpdateOpts
}

type commandUpdate handler.Command

func actionUpdate(c *cli.Context) {
	command := &commandUpdate{
		Ctx: &handler.Context{
			CLIContext: c,
		},
	}
	handler.Handle(command)
}

func (command *commandUpdate) Context() *handler.Context {
	return command.Ctx
}

func (command *commandUpdate) Keys() []string {
	return keysUpdate
}

func (command *commandUpdate) ServiceClientType() string {
	return serviceClientType
}

func (command *commandUpdate) HandleFlags(resource *handler.Resource) error {
	c := command.Ctx.CLIContext

	err := command.Ctx.CheckFlagsSet([]string{"name"})
	if err != nil {
		return err
	}

	opts := containers.UpdateOpts{
		ContainerRead:  c.String("container-read"),
		ContainerWrite: c.String("container-write"),
	}
	if c.IsSet("metadata") {
		metadata, err := command.Ctx.CheckKVFlag("metadata")
		if err != nil {
			return err
		}
		opts.Metadata = metadata
	}
	resource.Params = &paramsUpdate{
		container: c.String("name"),
		opts:      opts,
	}
	return nil
}

func (command *commandUpdate) Execute(resource *handler.Resource) {
	params := resource.Params.(*paramsUpdate)
	containerName := params.container
	opts := params.opts
	rawResponse := containers.Update(command.Ctx.ServiceClient, containerName, opts)
	if rawResponse.Err != nil {
		resource.Err = rawResponse.Err
		return
	}
	resource.Result = fmt.Sprintf("Successfully updated container [%s]\n", containerName)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors 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 cmd

import (
	goflag "flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"path/filepath"

	"encoding/json"

	"github.com/docker/machine/libmachine/log"
	"github.com/golang/glog"
	"github.com/minishift/minishift/cmd/minishift/cmd/addon"
	configCmd "github.com/minishift/minishift/cmd/minishift/cmd/config"
	hostfolderCmd "github.com/minishift/minishift/cmd/minishift/cmd/hostfolder"
	"github.com/minishift/minishift/cmd/minishift/cmd/image"
	cmdOpenshift "github.com/minishift/minishift/cmd/minishift/cmd/openshift"
	"github.com/minishift/minishift/cmd/minishift/cmd/util"
	"github.com/minishift/minishift/pkg/minikube/constants"
	minishiftConfig "github.com/minishift/minishift/pkg/minishift/config"
	"github.com/minishift/minishift/pkg/util/filehelper"
	"github.com/minishift/minishift/pkg/util/os/atexit"
	"github.com/minishift/minishift/pkg/version"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

var dirs = [...]string{
	constants.Minipath,
	constants.MakeMiniPath("certs"),
	constants.MakeMiniPath("machines"),
	constants.MakeMiniPath("cache"),
	constants.MakeMiniPath("cache", "iso"),
	constants.MakeMiniPath("cache", "oc"),
	constants.MakeMiniPath("cache", "images"),
	constants.MakeMiniPath("config"),
	constants.MakeMiniPath("addons"),
	constants.MakeMiniPath("logs"),
	constants.MakeMiniPath("tmp"),
}

const (
	showLibmachineLogs    = "show-libmachine-logs"
	enableExperimentalEnv = "MINISHIFT_ENABLE_EXPERIMENTAL"
)

var viperWhiteList = []string{
	"v",
	"alsologtostderr",
	"log_dir",
}

var hasEnabledExperimental bool = false

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "minishift",
	Short: "Minishift is a tool for application development in local OpenShift clusters.",
	Long:  `Minishift is a command-line tool that provisions and manages single-node OpenShift clusters optimized for development workflows.`,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		var (
			err          error
			isFreshStart bool
		)

		if !filehelper.Exists(constants.Minipath) || filehelper.IsEmptyDir(constants.Minipath) {
			isFreshStart = true
		}

		for _, path := range dirs {
			if err := os.MkdirAll(path, 0777); err != nil {
				glog.Exitf("Error creating minishift directory: %s", err)
			}
		}

		ensureConfigFileExists(constants.ConfigFile)

		// Create all instances config
		allInstanceConfigPath := filepath.Join(constants.Minipath, "config", "allinstances.json")
		minishiftConfig.AllInstancesConfig, err = minishiftConfig.NewAllInstancesConfig(allInstanceConfigPath)
		if err != nil {
			atexit.ExitWithMessage(1, fmt.Sprintf("Error creating config for all instances: %s", err.Error()))
		}

		// Create MACHINE_NAME.json
		instanceConfigPath := filepath.Join(constants.Minipath, "machines", constants.MachineName+".json")
		minishiftConfig.InstanceConfig, err = minishiftConfig.NewInstanceConfig(instanceConfigPath)
		if err != nil {
			atexit.ExitWithMessage(1, fmt.Sprintf("Error creating config for VM: %s", err.Error()))
		}

		// Run the default addons on fresh minishift start
		if isFreshStart {
			fmt.Print("-- Installing default add-ons ... ")
			if err := util.UnpackAddons(constants.MakeMiniPath("addons")); err != nil {
				atexit.ExitWithMessage(1, fmt.Sprintf("Error installing default add-ons : %s", err))
			}

			fmt.Println("OK")
		}

		// Check marker file created by update command and perform post update execution steps
		if filehelper.Exists(filepath.Join(constants.Minipath, constants.UpdateMarkerFileName)) {
			if err := performPostUpdateExecution(filepath.Join(constants.Minipath, constants.UpdateMarkerFileName)); err != nil {
				atexit.ExitWithMessage(1, fmt.Sprintf("Error in performing post update exeuction: %s", err))
			}
		}

		if hasEnabledExperimental {
			glog.Info("Experimental features are enabled")
		}

		shouldShowLibmachineLogs := viper.GetBool(showLibmachineLogs)
		if glog.V(3) {
			log.SetDebug(true)
		}
		if !shouldShowLibmachineLogs {
			log.SetOutWriter(ioutil.Discard)
			log.SetErrWriter(ioutil.Discard)
		}
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		atexit.ExitWithMessage(1, err.Error())
	}
}

// Handle config values for flags used in external packages (e.g. glog)
// by setting them directly, using values from viper when not passed in as args
func setFlagsUsingViper() {
	for _, config := range viperWhiteList {
		var a = pflag.Lookup(config)
		viper.SetDefault(a.Name, a.DefValue)
		// If the flag is set, override viper value
		if a.Changed {
			viper.Set(a.Name, a.Value.String())
		}
		// Viper will give precedence first to calls to the Set command,
		// then to values from the config.yml
		a.Value.Set(viper.GetString(a.Name))
		a.Changed = true
	}
}

func init() {
	enableExperimental, err := util.GetBoolEnv(enableExperimentalEnv)
	if err == util.BooleanFormatError {
		atexit.ExitWithMessage(1, fmt.Sprintf("Error enabling experimental features: %s", err))
	}
	hasEnabledExperimental = enableExperimental

	RootCmd.PersistentFlags().Bool(showLibmachineLogs, false, "Show logs from libmachine.")
	RootCmd.AddCommand(configCmd.ConfigCmd)
	RootCmd.AddCommand(cmdOpenshift.OpenShiftCmd)
	RootCmd.AddCommand(hostfolderCmd.HostfolderCmd)
	RootCmd.AddCommand(addon.AddonsCmd)
	RootCmd.AddCommand(image.ImageCmd)
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
	logDir := pflag.Lookup("log_dir")
	if !logDir.Changed {
		logDir.Value.Set(constants.MakeMiniPath("logs"))
	}
	viper.BindPFlags(RootCmd.PersistentFlags())
	cobra.OnInitialize(initConfig)
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	configPath := constants.ConfigFile
	viper.SetConfigFile(configPath)
	viper.SetConfigType("json")
	err := viper.ReadInConfig()
	if err != nil {
		glog.Warningf("Error reading config file at %s: %s", configPath, err)
	}
	setupViper()
}

func setupViper() {
	viper.SetEnvPrefix(constants.MiniShiftEnvPrefix)
	// Replaces '-' in flags with '_' in env variables
	// e.g. show-libmachine-logs => $ENVPREFIX_SHOW_LIBMACHINE_LOGS
	viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
	viper.AutomaticEnv()
	setFlagsUsingViper()
}

func ensureConfigFileExists(configPath string) {
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		jsonRoot := []byte("{}")
		f, err := os.Create(configPath)
		if err != nil {
			glog.Exitf("Cannot create file %s: %s", configPath, err)
		}
		defer f.Close()
		_, err = f.Write(jsonRoot)
		if err != nil {
			glog.Exitf("Cannot encode config %s: %s", configPath, err)
		}
	}
}

// performPostUpdateExecution executes the post update actions like unpacking the default addons
// if user chose to update addons during `minishift update` command.
// It also remove the marker file created by update command to avoid repeating the post update execution process
func performPostUpdateExecution(markerPath string) error {
	var markerData UpdateMarker

	file, err := ioutil.ReadFile(markerPath)
	if err != nil {
		return err
	}

	json.Unmarshal(file, &markerData)
	if markerData.InstallAddon {
		fmt.Println(fmt.Sprintf("Minishift was upgraded from v%s to v%s. Running post update actions.", markerData.PreviousVersion, version.GetMinishiftVersion()))
		fmt.Print("--- Updating default add-ons ... ")
		util.UnpackAddons(constants.MakeMiniPath("add-ons"))
		fmt.Println("OK")
		fmt.Println(fmt.Sprintf("Default add-ons %s installed", strings.Join(util.DefaultAssets, ", ")))
	}

	// Delete the marker file once post update execution is done
	if err := os.Remove(markerPath); err != nil {
		return err
	}

	return nil
}
<commit_msg>Issue #1203 Install default add-ons if addons directory is not present<commit_after>/*
Copyright 2016 The Kubernetes Authors 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 cmd

import (
	goflag "flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"path/filepath"

	"encoding/json"

	"github.com/docker/machine/libmachine/log"
	"github.com/golang/glog"
	"github.com/minishift/minishift/cmd/minishift/cmd/addon"
	configCmd "github.com/minishift/minishift/cmd/minishift/cmd/config"
	hostfolderCmd "github.com/minishift/minishift/cmd/minishift/cmd/hostfolder"
	"github.com/minishift/minishift/cmd/minishift/cmd/image"
	cmdOpenshift "github.com/minishift/minishift/cmd/minishift/cmd/openshift"
	"github.com/minishift/minishift/cmd/minishift/cmd/util"
	"github.com/minishift/minishift/pkg/minikube/constants"
	minishiftConfig "github.com/minishift/minishift/pkg/minishift/config"
	"github.com/minishift/minishift/pkg/util/filehelper"
	"github.com/minishift/minishift/pkg/util/os/atexit"
	"github.com/minishift/minishift/pkg/version"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

var dirs = [...]string{
	constants.Minipath,
	constants.MakeMiniPath("certs"),
	constants.MakeMiniPath("machines"),
	constants.MakeMiniPath("cache"),
	constants.MakeMiniPath("cache", "iso"),
	constants.MakeMiniPath("cache", "oc"),
	constants.MakeMiniPath("cache", "images"),
	constants.MakeMiniPath("config"),
	constants.MakeMiniPath("addons"),
	constants.MakeMiniPath("logs"),
	constants.MakeMiniPath("tmp"),
}

const (
	showLibmachineLogs    = "show-libmachine-logs"
	enableExperimentalEnv = "MINISHIFT_ENABLE_EXPERIMENTAL"
)

var viperWhiteList = []string{
	"v",
	"alsologtostderr",
	"log_dir",
}

var hasEnabledExperimental bool = false

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "minishift",
	Short: "Minishift is a tool for application development in local OpenShift clusters.",
	Long:  `Minishift is a command-line tool that provisions and manages single-node OpenShift clusters optimized for development workflows.`,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		var (
			err                    error
			isAddonInstallRequired bool
		)

		if !filehelper.Exists(constants.MakeMiniPath("addons")) {
			isAddonInstallRequired = true
		}

		for _, path := range dirs {
			if err := os.MkdirAll(path, 0777); err != nil {
				glog.Exitf("Error creating minishift directory: %s", err)
			}
		}

		ensureConfigFileExists(constants.ConfigFile)

		// Create all instances config
		allInstanceConfigPath := filepath.Join(constants.Minipath, "config", "allinstances.json")
		minishiftConfig.AllInstancesConfig, err = minishiftConfig.NewAllInstancesConfig(allInstanceConfigPath)
		if err != nil {
			atexit.ExitWithMessage(1, fmt.Sprintf("Error creating config for all instances: %s", err.Error()))
		}

		// Create MACHINE_NAME.json
		instanceConfigPath := filepath.Join(constants.Minipath, "machines", constants.MachineName+".json")
		minishiftConfig.InstanceConfig, err = minishiftConfig.NewInstanceConfig(instanceConfigPath)
		if err != nil {
			atexit.ExitWithMessage(1, fmt.Sprintf("Error creating config for VM: %s", err.Error()))
		}

		// Run the default addons on fresh minishift start
		if isAddonInstallRequired {
			fmt.Print("-- Installing default add-ons ... ")
			if err := util.UnpackAddons(constants.MakeMiniPath("addons")); err != nil {
				atexit.ExitWithMessage(1, fmt.Sprintf("Error installing default add-ons : %s", err))
			}

			fmt.Println("OK")
		}

		// Check marker file created by update command and perform post update execution steps
		if filehelper.Exists(filepath.Join(constants.Minipath, constants.UpdateMarkerFileName)) {
			if err := performPostUpdateExecution(filepath.Join(constants.Minipath, constants.UpdateMarkerFileName)); err != nil {
				atexit.ExitWithMessage(1, fmt.Sprintf("Error in performing post update exeuction: %s", err))
			}
		}

		if hasEnabledExperimental {
			glog.Info("Experimental features are enabled")
		}

		shouldShowLibmachineLogs := viper.GetBool(showLibmachineLogs)
		if glog.V(3) {
			log.SetDebug(true)
		}
		if !shouldShowLibmachineLogs {
			log.SetOutWriter(ioutil.Discard)
			log.SetErrWriter(ioutil.Discard)
		}
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		atexit.ExitWithMessage(1, err.Error())
	}
}

// Handle config values for flags used in external packages (e.g. glog)
// by setting them directly, using values from viper when not passed in as args
func setFlagsUsingViper() {
	for _, config := range viperWhiteList {
		var a = pflag.Lookup(config)
		viper.SetDefault(a.Name, a.DefValue)
		// If the flag is set, override viper value
		if a.Changed {
			viper.Set(a.Name, a.Value.String())
		}
		// Viper will give precedence first to calls to the Set command,
		// then to values from the config.yml
		a.Value.Set(viper.GetString(a.Name))
		a.Changed = true
	}
}

func init() {
	enableExperimental, err := util.GetBoolEnv(enableExperimentalEnv)
	if err == util.BooleanFormatError {
		atexit.ExitWithMessage(1, fmt.Sprintf("Error enabling experimental features: %s", err))
	}
	hasEnabledExperimental = enableExperimental

	RootCmd.PersistentFlags().Bool(showLibmachineLogs, false, "Show logs from libmachine.")
	RootCmd.AddCommand(configCmd.ConfigCmd)
	RootCmd.AddCommand(cmdOpenshift.OpenShiftCmd)
	RootCmd.AddCommand(hostfolderCmd.HostfolderCmd)
	RootCmd.AddCommand(addon.AddonsCmd)
	RootCmd.AddCommand(image.ImageCmd)
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
	logDir := pflag.Lookup("log_dir")
	if !logDir.Changed {
		logDir.Value.Set(constants.MakeMiniPath("logs"))
	}
	viper.BindPFlags(RootCmd.PersistentFlags())
	cobra.OnInitialize(initConfig)
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	configPath := constants.ConfigFile
	viper.SetConfigFile(configPath)
	viper.SetConfigType("json")
	err := viper.ReadInConfig()
	if err != nil {
		glog.Warningf("Error reading config file at %s: %s", configPath, err)
	}
	setupViper()
}

func setupViper() {
	viper.SetEnvPrefix(constants.MiniShiftEnvPrefix)
	// Replaces '-' in flags with '_' in env variables
	// e.g. show-libmachine-logs => $ENVPREFIX_SHOW_LIBMACHINE_LOGS
	viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
	viper.AutomaticEnv()
	setFlagsUsingViper()
}

func ensureConfigFileExists(configPath string) {
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		jsonRoot := []byte("{}")
		f, err := os.Create(configPath)
		if err != nil {
			glog.Exitf("Cannot create file %s: %s", configPath, err)
		}
		defer f.Close()
		_, err = f.Write(jsonRoot)
		if err != nil {
			glog.Exitf("Cannot encode config %s: %s", configPath, err)
		}
	}
}

// performPostUpdateExecution executes the post update actions like unpacking the default addons
// if user chose to update addons during `minishift update` command.
// It also remove the marker file created by update command to avoid repeating the post update execution process
func performPostUpdateExecution(markerPath string) error {
	var markerData UpdateMarker

	file, err := ioutil.ReadFile(markerPath)
	if err != nil {
		return err
	}

	json.Unmarshal(file, &markerData)
	if markerData.InstallAddon {
		fmt.Println(fmt.Sprintf("Minishift was upgraded from v%s to v%s. Running post update actions.", markerData.PreviousVersion, version.GetMinishiftVersion()))
		fmt.Print("--- Updating default add-ons ... ")
		util.UnpackAddons(constants.MakeMiniPath("add-ons"))
		fmt.Println("OK")
		fmt.Println(fmt.Sprintf("Default add-ons %s installed", strings.Join(util.DefaultAssets, ", ")))
	}

	// Delete the marker file once post update execution is done
	if err := os.Remove(markerPath); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"log"
	"net"
	"os"
	"path/filepath"

	"github.com/buaazp/fasthttprouter"
	"github.com/henkman/steamquery"
	"github.com/valyala/fasthttp"
	"github.com/zserge/webview"
)

func Players(ctx *fasthttp.RequestCtx) {
	server := ctx.UserValue("server").(string)
	ps, err := steamquery.QueryPlayersString(server)
	if err != nil {
		ctx.Response.SetBodyString("{'error':" + err.Error() + "}")
		return
	}
	json.NewEncoder(ctx.Response.BodyWriter()).Encode(&ps)
}

func Info(ctx *fasthttp.RequestCtx) {
	server := ctx.UserValue("server").(string)
	res, err := steamquery.QueryString(server)
	if err != nil {
		ctx.Response.SetBodyString("{'error':" + err.Error() + "}")
		return
	}
	json.NewEncoder(ctx.Response.BodyWriter()).Encode(&res)
}

func Index(server string) fasthttp.RequestHandler {
	return func(ctx *fasthttp.RequestCtx) {
		ctx.Response.Header.Set("X-UA-Compatible", "IE=edge")
		ctx.Response.Header.SetContentType("text/html")
		ctx.Response.SetBodyString(`

	<title>test</title>

	<input type="button" id="refresh" value="refresh">

	Name:

	Map:

	Players:

				Name
				Score
				Online
				Score/Second

`)
	}
}

var (
	_server string
)

func init() {
	flag.StringVar(&_server, "s", "", "server")
	flag.Parse()
}

func main() {
	if _server == "" {
		flag.Usage()
		return
	}
	exe, err := os.Executable()
	if err != nil {
		log.Fatal(err)
	}
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}
	defer ln.Close()
	go func() {
		router := fasthttprouter.New()
		router.GET("/", Index(_server))
		router.GET("/players/:server", Players)
		router.GET("/info/:server", Info)
		router.ServeFiles("/r/*filepath", filepath.Join(filepath.Dir(exe), "r"))
		log.Fatal(fasthttp.Serve(ln, router.Handler))
	}()
	log.Fatal(webview.Open("server state", "http://"+ln.Addr().String(),
		600, 800, false))
}
<commit_msg>fix cache problems in serverstate ui<commit_after>package main

import (
	"encoding/json"
	"flag"
	"log"
	"net"
	"os"
	"path/filepath"

	"github.com/buaazp/fasthttprouter"
	"github.com/henkman/steamquery"
	"github.com/valyala/fasthttp"
	"github.com/zserge/webview"
)

func Players(ctx *fasthttp.RequestCtx) {
	ctx.Response.Header.Set("Content-Type", "application/json")
	ctx.Response.Header.Set("Cache-Control", "No-Cache")
	server := ctx.UserValue("server").(string)
	ps, err := steamquery.QueryPlayersString(server)
	if err != nil {
		ctx.Response.SetBodyString("{'error':" + err.Error() + "}")
		return
	}
	json.NewEncoder(ctx.Response.BodyWriter()).Encode(&ps)
}

func Info(ctx *fasthttp.RequestCtx) {
	ctx.Response.Header.Set("Content-Type", "application/json")
	ctx.Response.Header.Set("Cache-Control", "No-Cache")
	server := ctx.UserValue("server").(string)
	res, err := steamquery.QueryString(server)
	if err != nil {
		ctx.Response.SetBodyString("{'error':" + err.Error() + "}")
		return
	}
	json.NewEncoder(ctx.Response.BodyWriter()).Encode(&res)
}

func Index(server string) fasthttp.RequestHandler {
	return func(ctx *fasthttp.RequestCtx) {
		ctx.Response.Header.Set("X-UA-Compatible", "IE=edge")
		ctx.Response.Header.SetContentType("text/html")
		ctx.Response.SetBodyString(`

	<title>test</title>

	<input type="button" id="refresh" value="refresh">

	Name:

	Map:

	Players:

				Name
				Score
				Online
				Score/Second

`)
	}
}

var (
	_server string
)

func init() {
	flag.StringVar(&_server, "s", "", "server")
	flag.Parse()
}

func main() {
	if _server == "" {
		flag.Usage()
		return
	}
	exe, err := os.Executable()
	if err != nil {
		log.Fatal(err)
	}
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}
	defer ln.Close()
	go func() {
		router := fasthttprouter.New()
		router.GET("/", Index(_server))
		router.GET("/players/:server", Players)
		router.GET("/info/:server", Info)
		router.ServeFiles("/r/*filepath", filepath.Join(filepath.Dir(exe), "r"))
		log.Fatal(fasthttp.Serve(ln, router.Handler))
	}()
	log.Fatal(webview.Open("server state", "http://"+ln.Addr().String(),
		600, 800, false))
}
<|endoftext|>


<commit_before>//CCMU Automatic Connector
//
//This tool provides automatic login feature for
//connect to the network authenticated by dr.com
//
//Author: ms2008vip@gmail.com at 2016/11/8 16:54:29
//

//go:generate goversioninfo -icon=cover.ico

package main

import (
    "fmt"
    "net/http"
    "net/url"
    "os"
    "bufio"
    "io/ioutil"
    "strings"
    "strconv"
    "regexp"
    "math/rand"
    "time"
    l4g "github.com/alecthomas/log4go"
    "github.com/toqueteos/webbrowser"
)

const (
    URL = "http://192.168.161.2/"
    FILE = "ids.txt"
)

var log = make(l4g.Logger)
var buildstamp string = ""
var githash string = ""

func init() {
    log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter())
    log.AddFilter("file", l4g.INFO, l4g.NewFileLogWriter("connect.log", false))
}

func main() {
    defer log.Close()

    args := os.Args
    if len(args)==2 && (args[1]=="--version" || args[1] =="-v") {
        fmt.Printf("Git Commit Hash: %s\n", githash)
        fmt.Printf("UTC Build Time: %s\n", buildstamp)
        return
    }
    fmt.Println("os args:", os.Args)

    userList := fileTolines(FILE)
    rand.Seed(time.Now().Unix())
    //fmt.Println(userList)
    count := 1
    isCheckIn := false

    for {

        if count == 1 && len(userList) == 1 && strings.TrimSpace(userList[0]) == "\xef\xbb\xbf" {
            fmt.Println("密码库是空的！")
            fmt.Println("按下「回车键」终止本程序")
            fmt.Scanln()
            return
        } else if len(userList) == 0 {
            fmt.Println("你的学弟学妹们都太抠了，连费都舍不得充！")
            fmt.Println("按下「回车键」终止本程序")
            fmt.Scanln()
            return
        }

        // 速率限制
        // if count%1000 == 0 {
        //     fmt.Printf("processed at least %d iterms\n", count)
        //     <- time.After(5 * time.Second)
        //     fmt.Println("go on....")
        // }

        // 注销上次的用户
        if isCheckIn == true {
            checkOut()
            isCheckIn = false
        }

        n := rand.Intn(len(userList))
        line := userList[n]

        // 将测试过的用户移除出当前密码库
        userList = append(userList[:n], userList[n+1:]...)
        count++

        //userInfo := strings.Split(line, "\t")
        userInfo := strings.Fields(line)
        if len(userInfo) < 2 {
            log.Critical("shit happens: unrecognized user info format: %v", userInfo)
            continue
        }
        account := userInfo[0]
        password := userInfo[len(userInfo)-1]
        //username := userInfo[1]

        //fmt.Println(account, password, username)
        //log.Info(line)
        if succ := checkPasswd(account, password); succ {
            isCheckIn = true
            balanceInfo := getBalance()
            fmt.Println(account, password, "Used Time:", balanceInfo[0], "Balance:", balanceInfo[1])

            if balanceInfo[0] >= 2400 && balanceInfo[1] == 0 {
                //fmt.Println(account, "time exceed!")
            } else {
                log.Info(account, password, "Used Time:", balanceInfo[0], "Balance:", balanceInfo[1])
                webbrowser.Open(URL)
                break
            }
        }
    }

    fmt.Println("看到这个，就是想证明下我不是个恶意程序，5s 之后就看不到我啦 :-)")
    <- time.After(5 * time.Second)
}

func fileTolines(filePath string) []string {
    f, err := os.Open(filePath)
    if err != nil {
        //panic(err)
        fmt.Println("没有发现密码库文件！")
        fmt.Println("按下「CTRL + C」终止本程序")
        fmt.Scanln()
    }
    defer f.Close()

    var lines []string
    scanner := bufio.NewScanner(f)
    for scanner.Scan() {
        lines = append(lines, scanner.Text())
    }
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    return lines
}

func chunkTolines(chunk string) []string {
    var lines []string
    lines = strings.Split(chunk, "\n")

    return lines
}

func checkPasswd(user, passwd string) bool {
    defer func() {
        if p := recover(); p != nil {
            err := p.(error)
            log.Critical("shit happens: %v [user: %s pass: %s]", err, user, passwd)
        }
    }()

    client := &http.Client{
        Timeout: time.Duration(10 * time.Second),
    }

    // 构造认证请求
    form := url.Values{}
    form.Add("DDDDD", user)
    form.Add("upass", passwd)
    form.Add("0MKKey", "")

    req, err := http.NewRequest("POST", URL, strings.NewReader(form.Encode()))
    // 防止被 dr.com banned
    req.Header.Set(`User-Agent`, `Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36`)
    req.Header.Set(`Referer`, URL)

    resp, err := client.Do(req)
    if err != nil {
        log.Error("failed to sent post request due to %s [user: %s pass: %s]", err, user, passwd)
        return false
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Error("failed to read login response body due to %s [user: %s pass: %s]", err, user, passwd)
        return false
    }
    //fmt.Println(string(body))

    // 判断认证是否通过
    line := chunkTolines(string(body))[5]
    if string(line[1]) == "s" {
        // 认证通过
        return true
    } else if string(line[1]) == "S" {
        return false
    } else {
        log.Warn("unexpected response! [user: %s pass: %s]", user, passwd)
        return false
    }
}

func getBalance() [2]int {
    defer func() {
        if p := recover(); p != nil {
            err := p.(error)
            log.Critical("shit happens: balance calc fauled %v", err)
        }
    }()

    balanceInfo := [2]int{0, 0}
    res, err := http.Get(URL)
    if err != nil {
        log.Error("failed to get balance info due to %s", err)
        return balanceInfo
    }

    result, err := ioutil.ReadAll(res.Body)
    defer res.Body.Close()
    if err != nil {
        log.Error("failed to read balance response body info due to %s", err)
        return balanceInfo
    }

    // 抓取已用时间
    line := chunkTolines(string(result))[6]
    re := regexp.MustCompile(`;time='(\d+) *';`)
    usedTime, _ := strconv.Atoi(re.FindStringSubmatch(line)[1])
    balanceInfo[0] = usedTime

    // 抓取余额
    re = regexp.MustCompile(`;fee='(\d+) *';`)
    fee, _ := strconv.Atoi(re.FindStringSubmatch(line)[1])
    amount := (fee - fee%100) / 10000
    balanceInfo[1] = amount

    return balanceInfo
}

func checkOut() {
    client := &http.Client{
        Timeout: time.Duration(5 * time.Second),
    }

    _, err := client.Get(URL+"F.htm")
    if err != nil {
        log.Error("failed to check the latest session out due to %s", err)
    }
}
<commit_msg>change the int balance number to float64<commit_after>//CCMU Automatic Connector
//
//This tool provides automatic login feature for
//connect to the network authenticated by dr.com
//
//Author: ms2008vip@gmail.com at 2016/11/8 16:54:29
//

//go:generate goversioninfo -icon=cover.ico

package main

import (
    "fmt"
    "net/http"
    "net/url"
    "os"
    "bufio"
    "io/ioutil"
    "strings"
    "strconv"
    "regexp"
    "math/rand"
    "time"
    l4g "github.com/alecthomas/log4go"
    "github.com/toqueteos/webbrowser"
)

const (
    URL = "http://192.168.161.2/"
    FILE = "ids.txt"
)

var log = make(l4g.Logger)
var buildstamp string = ""
var githash string = ""

func init() {
    log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter())
    log.AddFilter("file", l4g.INFO, l4g.NewFileLogWriter("connect.log", false))
}

func main() {
    defer log.Close()

    args := os.Args
    if len(args)==2 && (args[1]=="--version" || args[1] =="-v") {
        fmt.Printf("Git Commit Hash: %s\n", githash)
        fmt.Printf("UTC Build Time: %s\n", buildstamp)
        return
    }
    fmt.Println("os args:", os.Args)

    userList := fileTolines(FILE)
    rand.Seed(time.Now().Unix())
    //fmt.Println(userList)
    count := 1
    isCheckIn := false

    for {

        if count == 1 && len(userList) == 1 && strings.TrimSpace(userList[0]) == "\xef\xbb\xbf" {
            fmt.Println("密码库是空的！")
            fmt.Println("按下「回车键」终止本程序")
            fmt.Scanln()
            return
        } else if len(userList) == 0 {
            fmt.Println("你的学弟学妹们都太抠了，连费都舍不得充！")
            fmt.Println("按下「回车键」终止本程序")
            fmt.Scanln()
            return
        }

        // 速率限制
        // if count%1000 == 0 {
        //     fmt.Printf("processed at least %d iterms\n", count)
        //     <- time.After(5 * time.Second)
        //     fmt.Println("go on....")
        // }

        // 注销上次的用户
        if isCheckIn == true {
            checkOut()
            isCheckIn = false
        }

        n := rand.Intn(len(userList))
        line := userList[n]

        // 将测试过的用户移除出当前密码库
        userList = append(userList[:n], userList[n+1:]...)
        count++

        //userInfo := strings.Split(line, "\t")
        userInfo := strings.Fields(line)
        if len(userInfo) < 2 {
            log.Critical("shit happens: unrecognized user info format: %v", userInfo)
            continue
        }
        account := userInfo[0]
        password := userInfo[len(userInfo)-1]
        //username := userInfo[1]

        //fmt.Println(account, password, username)
        //log.Info(line)
        if succ := checkPasswd(account, password); succ {
            isCheckIn = true
            balanceInfo := getBalance()
            fmt.Println(account, password, "Used Time:", balanceInfo[0], "Balance:", balanceInfo[1])

            if balanceInfo[0] >= 2400 && balanceInfo[1] == 0 {
                //fmt.Println(account, "time exceed!")
            } else {
                log.Info(account, password, "Used Time:", balanceInfo[0], "Balance:", balanceInfo[1])
                webbrowser.Open(URL)
                break
            }
        }
    }

    fmt.Println("看到这个，就是想证明下我不是个恶意程序，5s 之后就看不到我啦 :-)")
    <- time.After(5 * time.Second)
}

func fileTolines(filePath string) []string {
    f, err := os.Open(filePath)
    if err != nil {
        //panic(err)
        fmt.Println("没有发现密码库文件！")
        fmt.Println("按下「CTRL + C」终止本程序")
        fmt.Scanln()
    }
    defer f.Close()

    var lines []string
    scanner := bufio.NewScanner(f)
    for scanner.Scan() {
        lines = append(lines, scanner.Text())
    }
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    return lines
}

func chunkTolines(chunk string) []string {
    var lines []string
    lines = strings.Split(chunk, "\n")

    return lines
}

func checkPasswd(user, passwd string) bool {
    defer func() {
        if p := recover(); p != nil {
            err := p.(error)
            log.Critical("shit happens: %v [user: %s pass: %s]", err, user, passwd)
        }
    }()

    client := &http.Client{
        Timeout: time.Duration(10 * time.Second),
    }

    // 构造认证请求
    form := url.Values{}
    form.Add("DDDDD", user)
    form.Add("upass", passwd)
    form.Add("0MKKey", "")

    req, err := http.NewRequest("POST", URL, strings.NewReader(form.Encode()))
    // 防止被 dr.com banned
    req.Header.Set(`User-Agent`, `Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36`)
    req.Header.Set(`Referer`, URL)

    resp, err := client.Do(req)
    if err != nil {
        log.Error("failed to sent post request due to %s [user: %s pass: %s]", err, user, passwd)
        return false
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Error("failed to read login response body due to %s [user: %s pass: %s]", err, user, passwd)
        return false
    }
    //fmt.Println(string(body))

    // 判断认证是否通过
    line := chunkTolines(string(body))[5]
    if string(line[1]) == "s" {
        // 认证通过
        return true
    } else if string(line[1]) == "S" {
        return false
    } else {
        log.Warn("unexpected response! [user: %s pass: %s]", user, passwd)
        return false
    }
}

func getBalance() [2]float64 {
    defer func() {
        if p := recover(); p != nil {
            err := p.(error)
            log.Critical("shit happens: balance calc fauled %v", err)
        }
    }()

    balanceInfo := [2]float64{0, 0}
    res, err := http.Get(URL)
    if err != nil {
        log.Error("failed to get balance info due to %s", err)
        return balanceInfo
    }

    result, err := ioutil.ReadAll(res.Body)
    defer res.Body.Close()
    if err != nil {
        log.Error("failed to read balance response body info due to %s", err)
        return balanceInfo
    }

    // 抓取已用时间
    line := chunkTolines(string(result))[6]
    re := regexp.MustCompile(`;time='(\d+) *';`)
    usedTime, _ := strconv.ParseFloat(re.FindStringSubmatch(line)[1], 64)
    balanceInfo[0] = usedTime

    // 抓取余额
    re = regexp.MustCompile(`;fee='(\d+) *';`)
    fee, _ := strconv.Atoi(re.FindStringSubmatch(line)[1])
    amount := (fee - fee%100) / 10000
    balanceInfo[1] = float64(amount)

    return balanceInfo
}

func checkOut() {
    client := &http.Client{
        Timeout: time.Duration(5 * time.Second),
    }

    _, err := client.Get(URL+"F.htm")
    if err != nil {
        log.Error("failed to check the latest session out due to %s", err)
    }
}
<|endoftext|>


<commit_before>package cairo

//#cgo pkg-config: cairo
//#include <cairo/cairo.h>
import "C"

import (
	"runtime"
)

//FontOptions specify how fonts should be rendered.
//Most of the time the font options implied by a surface are just right
//and do not need changes, but, for pixel-based targets, tweaking font options
//may result in superior output on a particular display.
//
//Originally cairo_font_options_t.
type FontOptions struct {
	fo *C.cairo_font_options_t
}

//NewFontOptions creates a new FontOptions with the default values.
//
//Originally cairo_font_options_create.
func NewFontOptions() *FontOptions {
	fo := &FontOptions{C.cairo_font_options_create()}
	runtime.SetFinalizer(fo, (*FontOptions).Close)
	return fo
}

func initFontOptions(opt *C.cairo_font_options_t) *FontOptions {
	fo := &FontOptions{opt}
	runtime.SetFinalizer(fo, (*FontOptions).Close)
	return fo
}

//Close destroys the FontOptions. Close is idempotent.
//
//Originally cairo_font_options_destroy.
func (f *FontOptions) Close() error {
	if f.fo == nil {
		return nil
	}
	C.cairo_font_options_destroy(f.fo)
	f.fo = nil
	runtime.SetFinalizer(f, nil)
	return nil
}

//Error queries f to see if there is an error.
//
//Originally cairo_font_options_status.
func (f *FontOptions) Err() error {
	return toerr(C.cairo_font_options_status(f.fo))
}

//Merge merges non-default options from o into f and return f.
//
//Originally cairo_font_options_merge.
func (f *FontOptions) Merge(o *FontOptions) *FontOptions {
	C.cairo_font_options_merge(f.fo, o.fo)
	return f
}

//Clone creates a new FontOptions with the same values as f.
//
//Originally cairo_font_options_copy.
func (f *FontOptions) Clone() *FontOptions {
	return &FontOptions{C.cairo_font_options_copy(f.fo)}
}

//Equal compares f with o.
//
//Originally cairo_font_options_equal.
func (f *FontOptions) Equal(o *FontOptions) bool {
	return C.cairo_font_options_equal(f.fo, o.fo) == 1
}

//SetAntialiasMode sets the antialiasing mode of f and returns f.
//
//Originally cairo_font_options_set_antialias.
func (f *FontOptions) SetAntialiasMode(a antialias) *FontOptions {
	C.cairo_font_options_set_antialias(f.fo, C.cairo_antialias_t(a))
	return f
}

//AntialiasMode reports the antialiasing mode of f.
//
//Originally cairo_font_topns_get_antialias.
func (f *FontOptions) AntialiasMode() antialias {
	return antialias(C.cairo_font_options_get_antialias(f.fo))
}

//SetSubpixelOrder sets the subpixel ordering of f and returns f.
//
//Originally cairo_font_options_set_subpixel_order.
func (f *FontOptions) SetSubpixelOrder(s subpixelOrder) *FontOptions {
	C.cairo_font_options_set_subpixel_order(f.fo, C.cairo_subpixel_order_t(s))
	return f
}

//SubpixelOrder reports the subpixel ordering of f.
//
//Originally cairo_font_options_get_subpixel_order.
func (f *FontOptions) SubpixelOrder() subpixelOrder {
	return subpixelOrder(C.cairo_font_options_get_subpixel_order(f.fo))
}

//SetHintStyle sets the hint style of f and returns f.
//
//Originally cairo_font_options_set_hint_style.
func (f *FontOptions) SetHintStyle(h hintStyle) *FontOptions {
	C.cairo_font_options_set_hint_style(f.fo, C.cairo_hint_style_t(h))
	return f
}

//HintStyle reports the hint style of f.
//
//Originally cairo_font_options_get_hint_style.
func (f *FontOptions) HintStyle() hintStyle {
	return hintStyle(C.cairo_font_options_get_hint_style(f.fo))
}

//SetHintMetrics sets the hint metrics of f and returns f.
//
//Originally cairo_font_options_set_hint_metrics.
func (f *FontOptions) SetHintMetrics(h hintMetrics) *FontOptions {
	C.cairo_font_options_set_hint_metrics(f.fo, C.cairo_hint_metrics_t(h))
	return f
}

//HintMetrics reports the hint metrics of f.
//
//Originally cairo_font_options_get_hint_metrics.
func (f *FontOptions) HintMetrics() hintMetrics {
	return hintMetrics(C.cairo_font_options_get_hint_metrics(f.fo))
}

//FontExtents stores metric information for a font.
//Values are given in the current user-space coordinate system.
//
//Because font metrics are in user-space coordinates, they are mostly,
//but not entirely, independent of the current transformation matrix.
//They will, however, change slightly due to hinting but otherwise remain
//unchanged.
//
//Originally cairo_font_extents_t.
type FontExtents struct {
	//Ascent is the distance the font extends above the baseline.
	Ascent float64
	//Descent is the distance the font extends below the baseline.
	Descent float64
	//Height is the recommended vertical distance between baselines
	//when setting consecutive lines of text with the font.
	Height float64
	//MaxAdvanceX is the maximum distance in the X direction that
	//the origin is advanced for any glyph in the font.
	//
	//Originally max_y_advance.
	MaxAdvanceX float64
	//MaxAdvanceY is the maximum distance in the Y direction that
	//the origin is advanced for any glyph in the font.
	//
	//This will be zero for most fonts used for horizontal writing.
	//
	//Originally max_x_advance.
	MaxAdvanceY float64
}

func newFontExtents(fe C.cairo_font_extents_t) FontExtents {
	return FontExtents{
		float64(fe.ascent),
		float64(fe.descent),
		float64(fe.height),
		float64(fe.max_x_advance),
		float64(fe.max_y_advance),
	}
}

//ExternalLeading reports the difference between the Height and the sum
//of the Ascent and Descent. Also known as "line spacing".
func (f FontExtents) ExternalLeading() float64 {
	return f.Height - (f.Ascent + f.Descent)
}

//TextExtents stores the extents of a single glyph or string of glyphs
//in user-space coordinates.
//Because text extents are in user-space coordinates, they are mostly,
//but not entirely, independent of the current transformation matrix.
//They will, however, change slightly due to hinting.
//
//Originally cairo_text_extents_t.
type TextExtents struct {
	//The horizontal distance from the origin to the leftmost part of the glyhps
	//as drawn.
	//Positive if the glyphs lie entirely to the right of the origin.
	//
	//Originally x_bearing.
	BearingX float64
	//The vertical distance from the origin to the topmost part of the glyhps
	//as drawn.
	//Positive if the glyphs lie entirely below the origin.
	//
	//Originally y_bearing
	BearingY float64
	//Width of the glyphs as drawn.
	Width float64
	//Height of the glyphs as drawn.
	Height float64
	//AdvanceX is the distance in the X direction to advance after drawing
	//these glyphs.
	//
	//Originally x_advance.
	AdvanceX float64
	//AdvanceY is the distance in the Y direction to advance after drawing
	//these glyphs.
	//
	//This will be zero for most fonts used for horizontal writing.
	//
	//Originally y_advance.
	AdvanceY float64
}

func newTextExtents(te C.cairo_text_extents_t) TextExtents {
	return TextExtents{
		BearingX: float64(te.x_bearing),
		BearingY: float64(te.y_bearing),
		Width:    float64(te.width),
		Height:   float64(te.height),
		AdvanceX: float64(te.x_advance),
		AdvanceY: float64(te.y_advance),
	}
}

type Font interface {
	Type() fontType
	Close() error
	Err() error
}
<commit_msg>added glyph type<commit_after>package cairo

//#cgo pkg-config: cairo
//#include <stdlib.h>
//#include <cairo/cairo.h>
import "C"

import (
	"runtime"
	"unsafe"
)

//FontOptions specify how fonts should be rendered.
//Most of the time the font options implied by a surface are just right
//and do not need changes, but, for pixel-based targets, tweaking font options
//may result in superior output on a particular display.
//
//Originally cairo_font_options_t.
type FontOptions struct {
	fo *C.cairo_font_options_t
}

//NewFontOptions creates a new FontOptions with the default values.
//
//Originally cairo_font_options_create.
func NewFontOptions() *FontOptions {
	fo := &FontOptions{C.cairo_font_options_create()}
	runtime.SetFinalizer(fo, (*FontOptions).Close)
	return fo
}

func initFontOptions(opt *C.cairo_font_options_t) *FontOptions {
	fo := &FontOptions{opt}
	runtime.SetFinalizer(fo, (*FontOptions).Close)
	return fo
}

//Close destroys the FontOptions. Close is idempotent.
//
//Originally cairo_font_options_destroy.
func (f *FontOptions) Close() error {
	if f.fo == nil {
		return nil
	}
	C.cairo_font_options_destroy(f.fo)
	f.fo = nil
	runtime.SetFinalizer(f, nil)
	return nil
}

//Error queries f to see if there is an error.
//
//Originally cairo_font_options_status.
func (f *FontOptions) Err() error {
	return toerr(C.cairo_font_options_status(f.fo))
}

//Merge merges non-default options from o into f and return f.
//
//Originally cairo_font_options_merge.
func (f *FontOptions) Merge(o *FontOptions) *FontOptions {
	C.cairo_font_options_merge(f.fo, o.fo)
	return f
}

//Clone creates a new FontOptions with the same values as f.
//
//Originally cairo_font_options_copy.
func (f *FontOptions) Clone() *FontOptions {
	return &FontOptions{C.cairo_font_options_copy(f.fo)}
}

//Equal compares f with o.
//
//Originally cairo_font_options_equal.
func (f *FontOptions) Equal(o *FontOptions) bool {
	return C.cairo_font_options_equal(f.fo, o.fo) == 1
}

//SetAntialiasMode sets the antialiasing mode of f and returns f.
//
//Originally cairo_font_options_set_antialias.
func (f *FontOptions) SetAntialiasMode(a antialias) *FontOptions {
	C.cairo_font_options_set_antialias(f.fo, C.cairo_antialias_t(a))
	return f
}

//AntialiasMode reports the antialiasing mode of f.
//
//Originally cairo_font_topns_get_antialias.
func (f *FontOptions) AntialiasMode() antialias {
	return antialias(C.cairo_font_options_get_antialias(f.fo))
}

//SetSubpixelOrder sets the subpixel ordering of f and returns f.
//
//Originally cairo_font_options_set_subpixel_order.
func (f *FontOptions) SetSubpixelOrder(s subpixelOrder) *FontOptions {
	C.cairo_font_options_set_subpixel_order(f.fo, C.cairo_subpixel_order_t(s))
	return f
}

//SubpixelOrder reports the subpixel ordering of f.
//
//Originally cairo_font_options_get_subpixel_order.
func (f *FontOptions) SubpixelOrder() subpixelOrder {
	return subpixelOrder(C.cairo_font_options_get_subpixel_order(f.fo))
}

//SetHintStyle sets the hint style of f and returns f.
//
//Originally cairo_font_options_set_hint_style.
func (f *FontOptions) SetHintStyle(h hintStyle) *FontOptions {
	C.cairo_font_options_set_hint_style(f.fo, C.cairo_hint_style_t(h))
	return f
}

//HintStyle reports the hint style of f.
//
//Originally cairo_font_options_get_hint_style.
func (f *FontOptions) HintStyle() hintStyle {
	return hintStyle(C.cairo_font_options_get_hint_style(f.fo))
}

//SetHintMetrics sets the hint metrics of f and returns f.
//
//Originally cairo_font_options_set_hint_metrics.
func (f *FontOptions) SetHintMetrics(h hintMetrics) *FontOptions {
	C.cairo_font_options_set_hint_metrics(f.fo, C.cairo_hint_metrics_t(h))
	return f
}

//HintMetrics reports the hint metrics of f.
//
//Originally cairo_font_options_get_hint_metrics.
func (f *FontOptions) HintMetrics() hintMetrics {
	return hintMetrics(C.cairo_font_options_get_hint_metrics(f.fo))
}

//FontExtents stores metric information for a font.
//Values are given in the current user-space coordinate system.
//
//Because font metrics are in user-space coordinates, they are mostly,
//but not entirely, independent of the current transformation matrix.
//They will, however, change slightly due to hinting but otherwise remain
//unchanged.
//
//Originally cairo_font_extents_t.
type FontExtents struct {
	//Ascent is the distance the font extends above the baseline.
	Ascent float64
	//Descent is the distance the font extends below the baseline.
	Descent float64
	//Height is the recommended vertical distance between baselines
	//when setting consecutive lines of text with the font.
	Height float64
	//MaxAdvanceX is the maximum distance in the X direction that
	//the origin is advanced for any glyph in the font.
	//
	//Originally max_y_advance.
	MaxAdvanceX float64
	//MaxAdvanceY is the maximum distance in the Y direction that
	//the origin is advanced for any glyph in the font.
	//
	//This will be zero for most fonts used for horizontal writing.
	//
	//Originally max_x_advance.
	MaxAdvanceY float64
}

func newFontExtents(fe C.cairo_font_extents_t) FontExtents {
	return FontExtents{
		float64(fe.ascent),
		float64(fe.descent),
		float64(fe.height),
		float64(fe.max_x_advance),
		float64(fe.max_y_advance),
	}
}

//ExternalLeading reports the difference between the Height and the sum
//of the Ascent and Descent. Also known as "line spacing".
func (f FontExtents) ExternalLeading() float64 {
	return f.Height - (f.Ascent + f.Descent)
}

//TextExtents stores the extents of a single glyph or string of glyphs
//in user-space coordinates.
//Because text extents are in user-space coordinates, they are mostly,
//but not entirely, independent of the current transformation matrix.
//They will, however, change slightly due to hinting.
//
//Originally cairo_text_extents_t.
type TextExtents struct {
	//The horizontal distance from the origin to the leftmost part of the glyhps
	//as drawn.
	//Positive if the glyphs lie entirely to the right of the origin.
	//
	//Originally x_bearing.
	BearingX float64
	//The vertical distance from the origin to the topmost part of the glyhps
	//as drawn.
	//Positive if the glyphs lie entirely below the origin.
	//
	//Originally y_bearing
	BearingY float64
	//Width of the glyphs as drawn.
	Width float64
	//Height of the glyphs as drawn.
	Height float64
	//AdvanceX is the distance in the X direction to advance after drawing
	//these glyphs.
	//
	//Originally x_advance.
	AdvanceX float64
	//AdvanceY is the distance in the Y direction to advance after drawing
	//these glyphs.
	//
	//This will be zero for most fonts used for horizontal writing.
	//
	//Originally y_advance.
	AdvanceY float64
}

func newTextExtents(te C.cairo_text_extents_t) TextExtents {
	return TextExtents{
		BearingX: float64(te.x_bearing),
		BearingY: float64(te.y_bearing),
		Width:    float64(te.width),
		Height:   float64(te.height),
		AdvanceX: float64(te.x_advance),
		AdvanceY: float64(te.y_advance),
	}
}

type Font interface {
	Type() fontType
	Close() error
	Err() error
}

//Glyph holds information about a single glyph when drawing or measuring text.
//A font is (in simple terms) a collection of shapes used to draw text.
//A glyph is one of these shapes.
//There can be multiple glyphs for a single character (alternates to be used
//in different contexts, for example), or a glyph can be a ligature of multiple
//characters.
//Cairo doesn't expose any way of converting input text into glyphs,
//so in order to use the Cairo interfaces that take arrays of glyphs, you must
//directly access the appropriate underlying font system.
//
//Note that the offsets given by Point.X and Point.Y are not cumulative.
//When drawing or measuring text, each glyph is individually positioned with respect to the overall origin
//
//Originally cairo_glyph_t.
type Glyph struct {
	Index uint64
	Point Point
}

func cGlyph(g C.cairo_glyph_t) Glyph {
	return Glyph{
		Index: uint64(g.index),
		Point: cPt(g.x, g.y),
	}
}

func cGlyphs(glyphs *C.cairo_glyph_t, N C.int) []Glyph {
	n := int(N)
	gs := (*[1 << 30]C.cairo_glyph_t)(unsafe.Pointer(glyphs))[:n:n]
	out := make([]Glyph, n)
	for i, v := range gs {
		out[i] = cGlyph(v)
	}
	return out
}

func glyphsC(gs []Glyph) (glyphs *C.cairo_glyph_t, N C.int) {
	n := len(gs)
	N = C.int(n)
	var t C.cairo_glyph_t
	glyphs = (*C.cairo_glyph_t)(C.malloc(C.size_t(uintptr(n) * unsafe.Sizeof(t))))
	iter := (*[1 << 30]C.cairo_glyph_t)(unsafe.Pointer(glyphs))[:n:n]
	for i, g := range gs {
		iter[i] = g.c()
	}
	return
}

func (g Glyph) c() C.cairo_glyph_t {
	out := C.cairo_glyph_t{}
	out.index = C.ulong(g.Index)
	out.x, out.y = g.Point.c()
	return out
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"text/tabwriter"

	"github.com/codegangsta/cli"
)

func main() {
	app := cli.NewApp()
	create := cli.Command{
		Name:  "create",
		Usage: "Create a new Project on Bazooka",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "bazooka-uri",
				Value:  "http://localhost:3000",
				Usage:  "URI for the bazooka server",
				EnvVar: "BZK_URI",
			},
			cli.StringFlag{
				Name:  "build",
				Value: "scm",
				Usage: "Type of build for Bazooka, eg. scm for `SCM based build`",
			},
			cli.StringFlag{
				Name:   "name",
				Usage:  "Name of the project",
				EnvVar: "BZK_SCM_NAME",
			},
			cli.StringFlag{
				Name:   "scm",
				Value:  "git",
				Usage:  "Type of SCM for the SCM based-build",
				EnvVar: "BZK_SCM",
			},
			cli.StringFlag{
				Name:   "scm-uri",
				Usage:  "URI of your SCM Project",
				EnvVar: "BZK_SCM_URI",
			},
		},
		Action: func(c *cli.Context) {
			client, err := NewClient(c.String("bazooka-uri"))
			if err != nil {
				log.Fatal(err)
			}
			res, err := client.CreateProject(c.String("name"), c.String("scm"), c.String("scm-uri"))
			if err != nil {
				log.Fatal(err)
			}
			w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)
			fmt.Fprint(w, "JOB ID\tPROJECT ID\tORCHESTRATION ID\n")
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n", res.ID, res.Name, res.ScmType, res.ScmURI)
			w.Flush()
		},
	}

	list := cli.Command{
		Name:  "list",
		Usage: "List bazooka projects",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "bazooka-uri",
				Value:  "http://localhost:3000",
				Usage:  "URI for the bazooka server",
				EnvVar: "BZK_URI",
			},
		},
		Action: func(c *cli.Context) {
			client, err := NewClient(c.String("bazooka-uri"))
			if err != nil {
				log.Fatal(err)
			}
			res, err := client.ListProjects()
			if err != nil {
				log.Fatal(err)
			}
			w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)
			fmt.Fprint(w, "PROJECT ID\tNAME\tSCM TYPE\tSCM URI\n")
			for _, item := range res {
				fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n", item.ID, item.Name, item.ScmType, item.ScmURI)
			}
			w.Flush()
		},
	}

	startJob := cli.Command{
		Name:  "start-job",
		Usage: "Start Job for the bazooka project",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "bazooka-uri",
				Value:  "http://localhost:3000",
				Usage:  "URI for the bazooka server",
				EnvVar: "BZK_URI",
			},
			cli.StringFlag{
				Name:  "project-id",
				Usage: "ID of the project to buiod",
			},
			cli.StringFlag{
				Name:  "scm-ref",
				Value: "master",
				Usage: "SCM Reference to build",
			},
		},
		Action: func(c *cli.Context) {
			client, err := NewClient(c.String("bazooka-uri"))
			if err != nil {
				log.Fatal(err)
			}
			res, err := client.StartJob(c.String("project-id"), c.String("scm-ref"))
			if err != nil {
				log.Fatal(err)
			}
			w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)
			fmt.Fprint(w, "JOB ID\tPROJECT ID\tORCHESTRATION ID\n")
			fmt.Fprintf(w, "%s\t%s\t%s\t\n", res.ID, res.ProjectID, res.OrchestrationID)
			w.Flush()
		},
	}

	app.Commands = []cli.Command{
		{
			Name:        "project",
			Usage:       "Actions on projects",
			Subcommands: []cli.Command{create, list, startJob},
		},
	}
	app.Run(os.Args)
}
<commit_msg>add env var for project ID<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"text/tabwriter"

	"github.com/codegangsta/cli"
)

func main() {
	app := cli.NewApp()
	create := cli.Command{
		Name:  "create",
		Usage: "Create a new Project on Bazooka",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "bazooka-uri",
				Value:  "http://localhost:3000",
				Usage:  "URI for the bazooka server",
				EnvVar: "BZK_URI",
			},
			cli.StringFlag{
				Name:  "build",
				Value: "scm",
				Usage: "Type of build for Bazooka, eg. scm for `SCM based build`",
			},
			cli.StringFlag{
				Name:   "name",
				Usage:  "Name of the project",
				EnvVar: "BZK_SCM_NAME",
			},
			cli.StringFlag{
				Name:   "scm",
				Value:  "git",
				Usage:  "Type of SCM for the SCM based-build",
				EnvVar: "BZK_SCM",
			},
			cli.StringFlag{
				Name:   "scm-uri",
				Usage:  "URI of your SCM Project",
				EnvVar: "BZK_SCM_URI",
			},
		},
		Action: func(c *cli.Context) {
			client, err := NewClient(c.String("bazooka-uri"))
			if err != nil {
				log.Fatal(err)
			}
			res, err := client.CreateProject(c.String("name"), c.String("scm"), c.String("scm-uri"))
			if err != nil {
				log.Fatal(err)
			}
			w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)
			fmt.Fprint(w, "JOB ID\tPROJECT ID\tORCHESTRATION ID\n")
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n", res.ID, res.Name, res.ScmType, res.ScmURI)
			w.Flush()
		},
	}

	list := cli.Command{
		Name:  "list",
		Usage: "List bazooka projects",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "bazooka-uri",
				Value:  "http://localhost:3000",
				Usage:  "URI for the bazooka server",
				EnvVar: "BZK_URI",
			},
		},
		Action: func(c *cli.Context) {
			client, err := NewClient(c.String("bazooka-uri"))
			if err != nil {
				log.Fatal(err)
			}
			res, err := client.ListProjects()
			if err != nil {
				log.Fatal(err)
			}
			w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)
			fmt.Fprint(w, "PROJECT ID\tNAME\tSCM TYPE\tSCM URI\n")
			for _, item := range res {
				fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n", item.ID, item.Name, item.ScmType, item.ScmURI)
			}
			w.Flush()
		},
	}

	startJob := cli.Command{
		Name:  "start-job",
		Usage: "Start Job for the bazooka project",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "bazooka-uri",
				Value:  "http://localhost:3000",
				Usage:  "URI for the bazooka server",
				EnvVar: "BZK_URI",
			},
			cli.StringFlag{
				Name:   "project-id",
				Usage:  "ID of the project to build",
				EnvVar: "BZK_PROJECT_ID",
			},
			cli.StringFlag{
				Name:  "scm-ref",
				Value: "master",
				Usage: "SCM Reference to build",
			},
		},
		Action: func(c *cli.Context) {
			client, err := NewClient(c.String("bazooka-uri"))
			if err != nil {
				log.Fatal(err)
			}
			res, err := client.StartJob(c.String("project-id"), c.String("scm-ref"))
			if err != nil {
				log.Fatal(err)
			}
			w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)
			fmt.Fprint(w, "JOB ID\tPROJECT ID\tORCHESTRATION ID\n")
			fmt.Fprintf(w, "%s\t%s\t%s\t\n", res.ID, res.ProjectID, res.OrchestrationID)
			w.Flush()
		},
	}

	app.Commands = []cli.Command{
		{
			Name:        "project",
			Usage:       "Actions on projects",
			Subcommands: []cli.Command{create, list, startJob},
		},
	}
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package app

import (
	"fmt"
	"net/http"
	"reflect"
	"syscall"
)

// Error represents an error which, besides a message,
// has also an assocciated status code to be sent to
// the client.
type Error interface {
	error
	StatusCode() int
}

// Indicates that something wasn't found. Its status code
// is 404.
type NotFoundError struct {
	Kind string
}

func (n *NotFoundError) StatusCode() int {
	return http.StatusNotFound
}

func (n *NotFoundError) Error() string {
	if n.Kind != "" {
		fmt.Sprintf("%s not found", n.Kind)
	}
	return "Not found"
}

// Indicates that a required parameter is mising. Its
// status code is 400.
type MissingParameterError struct {
	Name string
}

func (m *MissingParameterError) StatusCode() int {
	return http.StatusBadRequest
}

func (m *MissingParameterError) Error() string {
	return fmt.Sprintf("Missing required parameter \"%s\"", m.Name)
}

// Indicates that a parameter does not have the required type
// (e.g. an int was requested but a string was provided). Its
// status code is 400.
type InvalidParameterTypeError struct {
	*MissingParameterError
	Type reflect.Type
}

func (i *InvalidParameterTypeError) Error() string {
	return fmt.Sprintf("Required parameter %q must be of type %v", i.Name, i.Type)
}

// NotFound panics with NotFoundError.
func NotFound() {
	KindNotFound("")
}

// KindNotFound panics with a NotFoundError indicating
// what was not found (.e.g "Article not found).
func KindNotFound(kind string) {
	panic(&NotFoundError{kind})
}

// MissingParameter panics with a MissingParameterError, using
// the given parameter name.
func MissingParameter(name string) {
	panic(&MissingParameterError{name})
}

// InvalidParameterType panics with an InvalidParameterTypeError
// using the given parameter name and type.
func InvalidParameterType(name string, typ reflect.Type) {
	panic(&InvalidParameterTypeError{&MissingParameterError{name}, typ})
}

func isIgnorable(err interface{}) bool {
	if e, ok := err.(error); ok && e == syscall.EPIPE {
		// Client closed the connection
		return true
	}
	return false
}
<commit_msg>Detect EPIPE better<commit_after>package app

import (
	"fmt"
	"net"
	"net/http"
	"reflect"
	"syscall"
)

// Error represents an error which, besides a message,
// has also an assocciated status code to be sent to
// the client.
type Error interface {
	error
	StatusCode() int
}

// Indicates that something wasn't found. Its status code
// is 404.
type NotFoundError struct {
	Kind string
}

func (n *NotFoundError) StatusCode() int {
	return http.StatusNotFound
}

func (n *NotFoundError) Error() string {
	if n.Kind != "" {
		fmt.Sprintf("%s not found", n.Kind)
	}
	return "Not found"
}

// Indicates that a required parameter is mising. Its
// status code is 400.
type MissingParameterError struct {
	Name string
}

func (m *MissingParameterError) StatusCode() int {
	return http.StatusBadRequest
}

func (m *MissingParameterError) Error() string {
	return fmt.Sprintf("Missing required parameter \"%s\"", m.Name)
}

// Indicates that a parameter does not have the required type
// (e.g. an int was requested but a string was provided). Its
// status code is 400.
type InvalidParameterTypeError struct {
	*MissingParameterError
	Type reflect.Type
}

func (i *InvalidParameterTypeError) Error() string {
	return fmt.Sprintf("Required parameter %q must be of type %v", i.Name, i.Type)
}

// NotFound panics with NotFoundError.
func NotFound() {
	KindNotFound("")
}

// KindNotFound panics with a NotFoundError indicating
// what was not found (.e.g "Article not found).
func KindNotFound(kind string) {
	panic(&NotFoundError{kind})
}

// MissingParameter panics with a MissingParameterError, using
// the given parameter name.
func MissingParameter(name string) {
	panic(&MissingParameterError{name})
}

// InvalidParameterType panics with an InvalidParameterTypeError
// using the given parameter name and type.
func InvalidParameterType(name string, typ reflect.Type) {
	panic(&InvalidParameterTypeError{&MissingParameterError{name}, typ})
}

func isIgnorable(err interface{}) bool {
	if e, ok := err.(error); ok {
		if ne, ok := e.(*net.OpError); ok {
			e = ne.Err
		}
		if e == syscall.EPIPE {
			// Client closed the connection
			return true
		}
	}
	return false
}
<|endoftext|>