<commit_before>// Copyright 2016 Mirantis
//
// 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 externalip

import (
	"github.com/Mirantis/k8s-externalipcontroller/pkg/ipmanager"
	"github.com/Mirantis/k8s-externalipcontroller/pkg/netutils"
	"github.com/Mirantis/k8s-externalipcontroller/pkg/workqueue"
	"github.com/golang/glog"

	"k8s.io/client-go/1.5/kubernetes"
	"k8s.io/client-go/1.5/pkg/api"
	"k8s.io/client-go/1.5/pkg/api/v1"
	"k8s.io/client-go/1.5/pkg/runtime"
	"k8s.io/client-go/1.5/pkg/watch"
	"k8s.io/client-go/1.5/rest"
	"k8s.io/client-go/1.5/tools/cache"
)

type ExternalIpController struct {
	Uid   string
	Iface string
	Mask  string

	source    cache.ListerWatcher
	ipHandler netutils.IPHandler
	Queue     workqueue.QueueType
	manager   ipmanager.Manager
}

func NewExternalIpController(config *rest.Config, uid, iface, mask string, ipmanagerInst ipmanager.Manager) (*ExternalIpController, error) {
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	if ipmanagerInst == nil {
		ipmanagerInst = &ipmanager.Noop{}
	}

	lw := &cache.ListWatch{
		ListFunc: func(options api.ListOptions) (runtime.Object, error) {
			return clientset.Core().Services(api.NamespaceAll).List(api.ListOptions{})
		},
		WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
			return clientset.Core().Services(api.NamespaceAll).Watch(api.ListOptions{})
		},
	}
	return &ExternalIpController{
		Uid:       uid,
		Iface:     iface,
		Mask:      mask,
		source:    lw,
		ipHandler: netutils.LinuxIPHandler{},
		Queue:     workqueue.NewQueue(),
		manager:   ipmanagerInst,
	}, nil
}

func NewExternalIpControllerWithSource(uid, iface, mask string, source cache.ListerWatcher, ipmanagerInst ipmanager.Manager) *ExternalIpController {
	if ipmanagerInst == nil {
		ipmanagerInst = &ipmanager.Noop{}
	}
	return &ExternalIpController{
		Uid:       uid,
		Iface:     iface,
		Mask:      mask,
		source:    source,
		ipHandler: netutils.LinuxIPHandler{},
		Queue:     workqueue.NewQueue(),
		manager:   ipmanagerInst,
	}
}

func (c *ExternalIpController) Run(stopCh chan struct{}) {
	glog.Infof("Starting externalipcontroller")
	var store cache.Store
	store, controller := cache.NewInformer(
		c.source,
		&v1.Service{},
		0,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				c.processServiceExternalIPs(obj.(*v1.Service))
			},
			UpdateFunc: func(old, cur interface{}) {
				c.processServiceExternalIPs(cur.(*v1.Service))
			},
			DeleteFunc: func(obj interface{}) {
				c.deleteServiceExternalIPs(obj.(*v1.Service), store)
			},
		},
	)

	// we can spawn worker for each interface, but i doubt that we will ever need such
	// optimization
	go c.worker()
	go controller.Run(stopCh)
	<-stopCh
	c.Queue.Close()
}

func (c *ExternalIpController) worker() {
	for {
		item, quit := c.Queue.Get()
		if quit {
			return
		}
		c.processItem(item)
	}
}

func (c *ExternalIpController) processItem(item interface{}) {
	defer c.Queue.Done(item)
	var err error
	var cidr string
	var action string
	switch t := item.(type) {
	case *netutils.AddCIDR:
		fit, err := c.manager.Fit(c.Uid, t.Cidr)
		if !fit && err == nil {
			return
		}
		if err == nil {
			err = c.ipHandler.Add(c.Iface, t.Cidr)
			cidr = t.Cidr
		}
		action = "assignment"

	case *netutils.DelCIDR:
		err = c.ipHandler.Del(c.Iface, t.Cidr)
		cidr = t.Cidr
		action = "removal"
	}
	if err != nil {
		glog.Errorf("Error during IP: %s %v on %s - %v", cidr, action, c.Iface, err)
		c.Queue.Add(item)
	} else {
		glog.V(2).Infof("IP: %v %v was successfull assigned", action, cidr)
	}
}

func (c *ExternalIpController) processServiceExternalIPs(service *v1.Service) {
	for i := range service.Spec.ExternalIPs {
		cidr := service.Spec.ExternalIPs[i] + "/" + c.Mask
		c.Queue.Add(&netutils.AddCIDR{cidr})
	}
}

func (c *ExternalIpController) deleteServiceExternalIPs(service *v1.Service, store cache.Store) {
	if len(service.Spec.ExternalIPs) == 0 {
		return
	}
	ips := make(map[string]bool)
	key, _ := cache.MetaNamespaceKeyFunc(service)
	// collect external IPs of existing services
	svcList := store.List()
	for s := range svcList {
		svc := svcList[s].(*v1.Service)
		svcKey, _ := cache.MetaNamespaceKeyFunc(svc)
		if svcKey != key {
			for _, ip := range svc.Spec.ExternalIPs {
				ips[ip] = true
			}
		}
	}
	for _, ip := range service.Spec.ExternalIPs {
		if _, present := ips[ip]; !present {
			cidr := ip + "/" + c.Mask
			c.Queue.Add(&netutils.DelCIDR{cidr})
		}
	}
}
<commit_msg>Add support of IPs update<commit_after>// Copyright 2016 Mirantis
//
// 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 externalip

import (
	"reflect"

	"github.com/Mirantis/k8s-externalipcontroller/pkg/ipmanager"
	"github.com/Mirantis/k8s-externalipcontroller/pkg/netutils"
	"github.com/Mirantis/k8s-externalipcontroller/pkg/workqueue"
	"github.com/golang/glog"

	"k8s.io/client-go/1.5/kubernetes"
	"k8s.io/client-go/1.5/pkg/api"
	"k8s.io/client-go/1.5/pkg/api/v1"
	"k8s.io/client-go/1.5/pkg/runtime"
	"k8s.io/client-go/1.5/pkg/watch"
	"k8s.io/client-go/1.5/rest"
	"k8s.io/client-go/1.5/tools/cache"
)

type ExternalIpController struct {
	Uid   string
	Iface string
	Mask  string

	source    cache.ListerWatcher
	ipHandler netutils.IPHandler
	Queue     workqueue.QueueType
	manager   ipmanager.Manager
}

func NewExternalIpController(config *rest.Config, uid, iface, mask string, ipmanagerInst ipmanager.Manager) (*ExternalIpController, error) {
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	if ipmanagerInst == nil {
		ipmanagerInst = &ipmanager.Noop{}
	}

	lw := &cache.ListWatch{
		ListFunc: func(options api.ListOptions) (runtime.Object, error) {
			return clientset.Core().Services(api.NamespaceAll).List(api.ListOptions{})
		},
		WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
			return clientset.Core().Services(api.NamespaceAll).Watch(api.ListOptions{})
		},
	}
	return &ExternalIpController{
		Uid:       uid,
		Iface:     iface,
		Mask:      mask,
		source:    lw,
		ipHandler: netutils.LinuxIPHandler{},
		Queue:     workqueue.NewQueue(),
		manager:   ipmanagerInst,
	}, nil
}

func NewExternalIpControllerWithSource(uid, iface, mask string, source cache.ListerWatcher, ipmanagerInst ipmanager.Manager) *ExternalIpController {
	if ipmanagerInst == nil {
		ipmanagerInst = &ipmanager.Noop{}
	}
	return &ExternalIpController{
		Uid:       uid,
		Iface:     iface,
		Mask:      mask,
		source:    source,
		ipHandler: netutils.LinuxIPHandler{},
		Queue:     workqueue.NewQueue(),
		manager:   ipmanagerInst,
	}
}

func (c *ExternalIpController) Run(stopCh chan struct{}) {
	glog.Infof("Starting externalipcontroller")
	var store cache.Store
	store, controller := cache.NewInformer(
		c.source,
		&v1.Service{},
		0,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				c.processServiceExternalIPs(nil, obj.(*v1.Service), store)
			},
			UpdateFunc: func(old, cur interface{}) {
				c.processServiceExternalIPs(old.(*v1.Service), cur.(*v1.Service), store)
			},
			DeleteFunc: func(obj interface{}) {
				c.processServiceExternalIPs(cur.(*v1.Service), nil, store)
			},
		},
	)

	// we can spawn worker for each interface, but i doubt that we will ever need such
	// optimization
	go c.worker()
	go controller.Run(stopCh)
	<-stopCh
	c.Queue.Close()
}

func (c *ExternalIpController) worker() {
	for {
		item, quit := c.Queue.Get()
		if quit {
			return
		}
		c.processItem(item)
	}
}

func (c *ExternalIpController) processItem(item interface{}) {
	defer c.Queue.Done(item)
	var err error
	var cidr string
	var action string
	switch t := item.(type) {
	case *netutils.AddCIDR:
		fit, err := c.manager.Fit(c.Uid, t.Cidr)
		if !fit && err == nil {
			return
		}
		if err == nil {
			err = c.ipHandler.Add(c.Iface, t.Cidr)
			cidr = t.Cidr
		}
		action = "assignment"

	case *netutils.DelCIDR:
		err = c.ipHandler.Del(c.Iface, t.Cidr)
		cidr = t.Cidr
		action = "removal"
	}
	if err != nil {
		glog.Errorf("Error during %s of IP %v on %s - %v", action, cidr, c.Iface, err)
		c.Queue.Add(item)
	} else {
		glog.V(2).Infof("%s of IP %v was done successfully", action, cidr)
	}
}

func boolMapDifference(minuend, subtrahend map) map {
	difference = make(map[string]bool)

	for key, _ := range minuend.set {
		if !subtrahend[key] {
			difference[key] = true
		}
	}

	return difference
}

func neglectIPsInUse(ips *map, serviceKey string, store cache.Store) {
	svcList := store.List()
	for s := range svcList {
		svc := svcList[s].(*v1.Service)
		svcKey, _ := cache.MetaNamespaceKeyFunc(svc)
		if svcKey != key {
			for _, ip := range svc.Spec.ExternalIPs {
				delete(ips, ip)
			}
		}
	}
}

func (c *ExternalIpController) processServiceExternalIPs(old, cur *v1.Service, store cache.Store) {
	old_ips = make(map[string]bool)
	cur_ips = make(map[string]bool)
	key := ""

	if old != nil:
		for i := range old.Spec.ExternalIPs {
			old_ips[old.Spec.ExternalIPs[i]] = true
		}
		key, _ := cache.MetaNamespaceKeyFunc(old)
	if cur != nil:
		for i := range cur.Spec.ExternalIPs {
			cur_ips[cur.Spec.ExternalIPs[i]] = true
		}
		key, _ := cache.MetaNamespaceKeyFunc(new)

	if reflect.DeepEqual(cur_ips, old_ips) {
		return
	}

	ips_to_add = boolMapDifference(cur_ips, old_ips)
	ips_to_remove = boolMapDifference(old_ips, cur_ips)

	neglectIPsInUse(ips_to_add, key, store)
	neglectIPsInUse(ips_to_remove, key, store)

	for ip := range ips_to_add {
		cidr := ip + "/" + c.Mask
		c.Queue.Add(&netutils.AddCIDR{cidr})
	}
	for ip := range ips_to_remove {
		cidr := ip + "/" + c.Mask
		c.Queue.Add(&netutils.DelCIDR{cidr})
	}
}
<|endoftext|>


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

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
)

const (
	/*
	 * IOCTL commands --- we will commandeer 0x4C ('L')
	 */
	LOOP_SET_CAPACITY = 0x4C07
	LOOP_CHANGE_FD    = 0x4C06
	LOOP_GET_STATUS64 = 0x4C05
	LOOP_SET_STATUS64 = 0x4C04
	LOOP_GET_STATUS   = 0x4C03
	LOOP_SET_STATUS   = 0x4C02
	LOOP_CLR_FD       = 0x4C01
	LOOP_SET_FD       = 0x4C00
	LO_NAME_SIZE      = 64
	LO_KEY_SIZE       = 32
	/* /dev/loop-control interface */
	LOOP_CTL_ADD      = 0x4C80
	LOOP_CTL_REMOVE   = 0x4C81
	LOOP_CTL_GET_FREE = 0x4C82
	SYS_ioctl         = 16
)

const tcz = "/tinycorelinux.net/5.x/x86_64/tcz"

//http://distro.ibiblio.org/tinycorelinux/5.x/x86_64/tcz/
//The .dep is the name + .dep

var l = log.New(os.Stdout, "tcz: ", 0)

// consider making this a goroutine which pushes the string down the channel.
func findloop() (name string, err error) {
	cfd, err := syscall.Open("/dev/loop-control", syscall.O_RDWR, 0)
	if err != nil {
		log.Fatalf("/dev/loop-control: %v", err)
	}
	defer syscall.Close(cfd)
	a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(cfd), LOOP_CTL_GET_FREE, 0)
	if errno != 0 {
		log.Fatalf("ioctl: %v\n", err)
	}
	log.Printf("a %v b %v err %v\n", a, b, err)
	name = fmt.Sprintf("/dev/loop%d", a)
	return name, nil
}
func linkone(p string, i os.FileInfo, err error) error {
	l.Printf("symtree: p %v\n", p)
	if err != nil {
		return err
	}

	// the tree of symlinks starts at /tmp/tcloop
	packagel := filepath.SplitList(p)
	// surely there's a better way.
	n := append([]string{"/"}, packagel[2:]...)
	to := path.Join(n...)

	l.Printf("symtree: remove %v\n", to)
	os.Remove(to)
	l.Printf("symtree: symlink %v to %v\n", p, to)
	return os.Symlink(p, to)
}
func clonetree(tree string) error {
	lt := len(tree)
	err := filepath.Walk(tree, func(path string, fi os.FileInfo, err error) error {
		if fi.IsDir() {
			l.Printf("walking, dir %v\n", path)
			os.MkdirAll(path[lt:], 0600)
			return nil
		}
		// all else gets a symlink.
		l.Printf("Need to symlnk %v to %v\n", path, path)
		os.Symlink(path, path[lt:])
		return nil
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	return err
}

func fetch(p string) error {
	// path.Join doesn't quite work here.
	fullpath := path.Join(tcz, p)
	if _, err := os.Stat(fullpath); err != nil {
		cmd := "http:/" + fullpath
		l.Printf("Fetch %v\n", cmd)

		resp, err := http.Get(cmd)
		if err != nil {
			l.Fatalf("Get of %v failed: %v\n", cmd, err)
		}
		defer resp.Body.Close()

		if resp.Status != "200 OK" {
			l.Printf("%v Not OK! %v\n", cmd, resp.Status)
			return syscall.ENOENT
		}

		l.Printf("resp %v err %v\n", resp, err)
		// we have the whole tcz in resp.Body.
		// First, save it to /tcz/name
		f, err := os.Create(fullpath)
		if err != nil {
			l.Fatal("Create of :%v: failed: %v\n", fullpath, err)
		} else {
			l.Printf("created %v f %v\n", fullpath, f)
		}

		if c, err := io.Copy(f, resp.Body); err != nil {
			l.Fatal(err)
		} else {
			/* OK, these are compressed tars ... */
			l.Printf("c %v err %v\n", c, err)
		}
		f.Close()
	}
	return nil
}

// deps is ALL the packages we need fetched or not
// this may even let us work with parallel tcz, ALMOST
func installPackage(tczName string, deps map[string]bool) error {
	l.Printf("installPackage: %v %v\n", tczName, deps)
	depName := tczName + ".dep"
	// path.Join doesn't quite work here.
	if err := fetch(tczName); err != nil {
		l.Fatal(err)
	}
	deps[tczName] = true
	l.Printf("Fetched %v\n", tczName)
	// now fetch dependencies if any.
	if err := fetch(depName); err == nil {
		l.Printf("Fetched dep ok!\n")
	} else {
		l.Printf("No dep file found\n")
		return nil
	}
	// read deps file
	deplist, err := ioutil.ReadFile(path.Join(tcz, depName))
	if err != nil {
		l.Fatalf("Fetched dep file %v but can't read it? %v", depName, err)
	}
	l.Printf("deplist for %v is :%v:\n", depName, deplist)
	for _, v := range strings.Split(string(deplist), "\n") {
		if deps[v] {
			continue
		}
		if err := installPackage(v, deps); err != nil {
			return err
		}
	}
	return nil

}

func setupPackages(tczName string, deps map[string]bool) error {
	for v := range deps {
		cmdName := strings.Split(v, ".")[0]
		packagePath := path.Join("/tmp/tcloop", cmdName)
		if err := os.MkdirAll(packagePath, 0600); err != nil {
			l.Fatal(err)
		}

		loopname, err := findloop()
		if err != nil {
			l.Fatal(err)
		}
		l.Printf("findloop gets %v err %v\n", loopname, err)
		pkgpath := path.Join(tcz, v)
		ffd, err := syscall.Open(pkgpath, syscall.O_RDONLY, 0)
		lfd, err := syscall.Open(loopname, syscall.O_RDONLY, 0)
		l.Printf("ffd %v lfd %v\n", ffd, lfd)
		a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(lfd), LOOP_SET_FD, uintptr(ffd))
		if errno != 0 {
			l.Fatalf("loop set fd ioctl: %v, %v, %v\n", a, b, errno)
		}
		/* now mount it. The convention is the mount is in /tmp/tcloop/packagename */
		if err := syscall.Mount(loopname, packagePath, "squashfs", syscall.MS_MGC_VAL|syscall.MS_RDONLY, ""); err != nil {
			l.Fatalf("Mount %s on %s: %v\n", loopname, packagePath, err)
		}
		err = clonetree(packagePath)
		if err != nil {
			l.Fatalf("clonetree:  %v\n", err)
		}
	}
	return nil

}

func main() {
	needPackages := make(map[string]bool)

	if len(os.Args) < 2 {
		os.Exit(1)
	}
	cmdName := os.Args[1]
	tczName := cmdName + ".tcz"

	if err := os.MkdirAll(tcz, 0600); err != nil {
		l.Fatal(err)
	}

	if err := os.MkdirAll("/tmp/tcloop", 0600); err != nil {
		l.Fatal(err)
	}

	// path.Join doesn't quite work here.
	if err := installPackage(tczName, needPackages); err != nil {
		l.Fatal(err)
	}

	if err := setupPackages(tczName, needPackages); err != nil {
		l.Fatal(err)
	}
}
<commit_msg>We're closer, this is bug fixes for tcz + some extra debug prints that will be removed.<commit_after>// Copyright 2012 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
)

const (
	/*
	 * IOCTL commands --- we will commandeer 0x4C ('L')
	 */
	LOOP_SET_CAPACITY = 0x4C07
	LOOP_CHANGE_FD    = 0x4C06
	LOOP_GET_STATUS64 = 0x4C05
	LOOP_SET_STATUS64 = 0x4C04
	LOOP_GET_STATUS   = 0x4C03
	LOOP_SET_STATUS   = 0x4C02
	LOOP_CLR_FD       = 0x4C01
	LOOP_SET_FD       = 0x4C00
	LO_NAME_SIZE      = 64
	LO_KEY_SIZE       = 32
	/* /dev/loop-control interface */
	LOOP_CTL_ADD      = 0x4C80
	LOOP_CTL_REMOVE   = 0x4C81
	LOOP_CTL_GET_FREE = 0x4C82
	SYS_ioctl         = 16
)

const tcz = "/tinycorelinux.net/5.x/x86_64/tcz"

//http://distro.ibiblio.org/tinycorelinux/5.x/x86_64/tcz/
//The .dep is the name + .dep

var l = log.New(os.Stdout, "tcz: ", 0)

// consider making this a goroutine which pushes the string down the channel.
func findloop() (name string, err error) {
	cfd, err := syscall.Open("/dev/loop-control", syscall.O_RDWR, 0)
	if err != nil {
		log.Fatalf("/dev/loop-control: %v", err)
	}
	defer syscall.Close(cfd)
	a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(cfd), LOOP_CTL_GET_FREE, 0)
	if errno != 0 {
		log.Fatalf("ioctl: %v\n", err)
	}
	log.Printf("a %v b %v err %v\n", a, b, err)
	name = fmt.Sprintf("/dev/loop%d", a)
	return name, nil
}
func linkone(p string, i os.FileInfo, err error) error {
	l.Printf("symtree: p %v\n", p)
	if err != nil {
		return err
	}

	// the tree of symlinks starts at /tmp/tcloop
	packagel := filepath.SplitList(p)
	// surely there's a better way.
	n := append([]string{"/"}, packagel[2:]...)
	to := path.Join(n...)

	l.Printf("symtree: remove %v\n", to)
	os.Remove(to)
	l.Printf("symtree: symlink %v to %v\n", p, to)
	return os.Symlink(p, to)
}
func clonetree(tree string) error {
	lt := len(tree)
	err := filepath.Walk(tree, func(path string, fi os.FileInfo, err error) error {
		if fi.IsDir() {
			l.Printf("walking, dir %v\n", path)
			os.MkdirAll(path[lt:], 0600)
			return nil
		}
		// all else gets a symlink.
		l.Printf("Need to symlnk %v to %v\n", path, path)
		os.Symlink(path, path[lt:])
		return nil
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	return err
}

func fetch(p string) error {
	// path.Join doesn't quite work here.
	fullpath := path.Join(tcz, p)
	if _, err := os.Stat(fullpath); err != nil {
		cmd := "http:/" + fullpath
		l.Printf("Fetch %v\n", cmd)

		resp, err := http.Get(cmd)
		if err != nil {
			l.Fatalf("Get of %v failed: %v\n", cmd, err)
		}
		defer resp.Body.Close()

		if resp.Status != "200 OK" {
			l.Printf("%v Not OK! %v\n", cmd, resp.Status)
			return syscall.ENOENT
		}

		l.Printf("resp %v err %v\n", resp, err)
		// we have the whole tcz in resp.Body.
		// First, save it to /tcz/name
		f, err := os.Create(fullpath)
		if err != nil {
			l.Fatal("Create of :%v: failed: %v\n", fullpath, err)
		} else {
			l.Printf("created %v f %v\n", fullpath, f)
		}

		if c, err := io.Copy(f, resp.Body); err != nil {
			l.Fatal(err)
		} else {
			/* OK, these are compressed tars ... */
			l.Printf("c %v err %v\n", c, err)
		}
		f.Close()
	}
	return nil
}

// deps is ALL the packages we need fetched or not
// this may even let us work with parallel tcz, ALMOST
func installPackage(tczName string, deps map[string]bool) error {
	l.Printf("installPackage: %v %v\n", tczName, deps)
	depName := tczName + ".dep"
	// path.Join doesn't quite work here.
	if err := fetch(tczName); err != nil {
		l.Fatal(err)
	}
	deps[tczName] = true
	l.Printf("Fetched %v\n", tczName)
	// now fetch dependencies if any.
	if err := fetch(depName); err == nil {
		l.Printf("Fetched dep ok!\n")
	} else {
		l.Printf("No dep file found\n")
		return nil
	}
	// read deps file
	deplist, err := ioutil.ReadFile(path.Join(tcz, depName))
	if err != nil {
		l.Fatalf("Fetched dep file %v but can't read it? %v", depName, err)
	}
	l.Printf("deplist for %v is :%v:\n", depName, deplist)
	for _, v := range strings.Split(string(deplist), "\n") {
		// split("name\n") gets you a 2-element array with second
		// element the empty string
		if len(v) == 0 {
			break
		}
	l.Printf("FOR %v get package %v\n", tczName, v)
		if deps[v] {
			continue
		}
		if err := installPackage(v, deps); err != nil {
			return err
		}
	}
	return nil

}

func setupPackages(tczName string, deps map[string]bool) error {
	l.Printf("setupPackages: @ %v deps %v\n", tczName, deps)
	for v := range deps {
		cmdName := strings.Split(v, ".")[0]
		packagePath := path.Join("/tmp/tcloop", cmdName)
		if err := os.MkdirAll(packagePath, 0600); err != nil {
			l.Fatal(err)
		}

		loopname, err := findloop()
		if err != nil {
			l.Fatal(err)
		}
		l.Printf("findloop gets %v err %v\n", loopname, err)
		pkgpath := path.Join(tcz, v)
		ffd, err := syscall.Open(pkgpath, syscall.O_RDONLY, 0)
		if err != nil {
			l.Fatal("%v: %v\n", pkgpath, err)
		}
		lfd, err := syscall.Open(loopname, syscall.O_RDONLY, 0)
		if err != nil {
			l.Fatal("%v: %v\n", loopname, err)
		}
		l.Printf("ffd %v lfd %v\n", ffd, lfd)
		a, b, errno := syscall.Syscall(SYS_ioctl, uintptr(lfd), LOOP_SET_FD, uintptr(ffd))
		if errno != 0 {
			l.Fatalf("loop set fd ioctl: pkgpath :%v:, loop :%v:, %v, %v, %v\n", pkgpath, loopname, a, b, errno)
		}
		/* now mount it. The convention is the mount is in /tmp/tcloop/packagename */
		if err := syscall.Mount(loopname, packagePath, "squashfs", syscall.MS_MGC_VAL|syscall.MS_RDONLY, ""); err != nil {
			l.Fatalf("Mount :%s: on :%s: %v\n", loopname, packagePath, err)
		}
		err = clonetree(packagePath)
		if err != nil {
			l.Fatalf("clonetree:  %v\n", err)
		}
	}
	return nil

}

func main() {
	needPackages := make(map[string]bool)

	if len(os.Args) < 2 {
		os.Exit(1)
	}
	cmdName := os.Args[1]
	tczName := cmdName + ".tcz"

	if err := os.MkdirAll(tcz, 0600); err != nil {
		l.Fatal(err)
	}

	if err := os.MkdirAll("/tmp/tcloop", 0600); err != nil {
		l.Fatal(err)
	}

	// path.Join doesn't quite work here.
	if err := installPackage(tczName, needPackages); err != nil {
		l.Fatal(err)
	}
	l.Printf("After installpackages: needPackages %v\n", needPackages)

	if err := setupPackages(tczName, needPackages); err != nil {
		l.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package expr

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/grafana/grafana-plugin-sdk-go/backend"
	"github.com/grafana/grafana-plugin-sdk-go/data"
	"github.com/grafana/grafana/pkg/expr/classic"
	"github.com/grafana/grafana/pkg/expr/mathexp"

	"gonum.org/v1/gonum/graph/simple"
)

// baseNode includes commmon properties used across DPNodes.
type baseNode struct {
	id    int64
	refID string
}

type rawNode struct {
	RefID         string `json:"refId"`
	Query         map[string]interface{}
	QueryType     string
	TimeRange     TimeRange
	DatasourceUID string
}

func (rn *rawNode) GetDatasourceName() (string, error) {
	rawDs, ok := rn.Query["datasource"]
	if !ok {
		return "", nil
	}
	dsName, ok := rawDs.(string)
	if !ok {
		return "", fmt.Errorf("expted datasource identifier to be a string, got %T", rawDs)
	}
	return dsName, nil
}

func (rn *rawNode) GetCommandType() (c CommandType, err error) {
	rawType, ok := rn.Query["type"]
	if !ok {
		return c, fmt.Errorf("no expression command type in query for refId %v", rn.RefID)
	}
	typeString, ok := rawType.(string)
	if !ok {
		return c, fmt.Errorf("expected expression command type to be a string, got type %T", rawType)
	}
	return ParseCommandType(typeString)
}

// String returns a string representation of the node. In particular for
// %v formating in error messages.
func (b *baseNode) String() string {
	return b.refID
}

// CMDNode is a DPNode that holds an expression command.
type CMDNode struct {
	baseNode
	CMDType CommandType
	Command Command
}

// ID returns the id of the node so it can fulfill the gonum's graph Node interface.
func (b *baseNode) ID() int64 {
	return b.id
}

// RefID returns the refId of the node.
func (b *baseNode) RefID() string {
	return b.refID
}

// NodeType returns the data pipeline node type.
func (gn *CMDNode) NodeType() NodeType {
	return TypeCMDNode
}

// Execute runs the node and adds the results to vars. If the node requires
// other nodes they must have already been executed and their results must
// already by in vars.
func (gn *CMDNode) Execute(ctx context.Context, vars mathexp.Vars, s *Service) (mathexp.Results, error) {
	return gn.Command.Execute(ctx, vars)
}

func buildCMDNode(dp *simple.DirectedGraph, rn *rawNode) (*CMDNode, error) {
	commandType, err := rn.GetCommandType()
	if err != nil {
		return nil, fmt.Errorf("invalid expression command type in '%v'", rn.RefID)
	}

	node := &CMDNode{
		baseNode: baseNode{
			id:    dp.NewNode().ID(),
			refID: rn.RefID,
		},
		CMDType: commandType,
	}

	switch commandType {
	case TypeMath:
		node.Command, err = UnmarshalMathCommand(rn)
	case TypeReduce:
		node.Command, err = UnmarshalReduceCommand(rn)
	case TypeResample:
		node.Command, err = UnmarshalResampleCommand(rn)
	case TypeClassicConditions:
		node.Command, err = classic.UnmarshalConditionsCmd(rn.Query, rn.RefID)
	default:
		return nil, fmt.Errorf("expression command type '%v' in '%v' not implemented", commandType, rn.RefID)
	}
	if err != nil {
		return nil, err
	}

	return node, nil
}

const (
	defaultIntervalMS = int64(64)
	defaultMaxDP      = int64(5000)
)

// DSNode is a DPNode that holds a datasource request.
type DSNode struct {
	baseNode
	query         json.RawMessage
	datasourceID  int64
	datasourceUID string

	orgID      int64
	queryType  string
	timeRange  TimeRange
	intervalMS int64
	maxDP      int64
}

// NodeType returns the data pipeline node type.
func (dn *DSNode) NodeType() NodeType {
	return TypeDatasourceNode
}

func (s *Service) buildDSNode(dp *simple.DirectedGraph, rn *rawNode, orgID int64) (*DSNode, error) {
	encodedQuery, err := json.Marshal(rn.Query)
	if err != nil {
		return nil, err
	}

	dsNode := &DSNode{
		baseNode: baseNode{
			id:    dp.NewNode().ID(),
			refID: rn.RefID,
		},
		orgID:      orgID,
		query:      json.RawMessage(encodedQuery),
		queryType:  rn.QueryType,
		intervalMS: defaultIntervalMS,
		maxDP:      defaultMaxDP,
		timeRange:  rn.TimeRange,
	}

	rawDsID, ok := rn.Query["datasourceId"]
	switch ok {
	case true:
		floatDsID, ok := rawDsID.(float64)
		if !ok {
			return nil, fmt.Errorf("expected datasourceId to be a float64, got type %T for refId %v", rawDsID, rn.RefID)
		}
		dsNode.datasourceID = int64(floatDsID)
	default:
		if rn.DatasourceUID == "" {
			return nil, fmt.Errorf("neither datasourceId or datasourceUid in expression data source request for refId %v", rn.RefID)
		}
		dsNode.datasourceUID = rn.DatasourceUID
	}

	var floatIntervalMS float64
	if rawIntervalMS := rn.Query["intervalMs"]; ok {
		if floatIntervalMS, ok = rawIntervalMS.(float64); !ok {
			return nil, fmt.Errorf("expected intervalMs to be an float64, got type %T for refId %v", rawIntervalMS, rn.RefID)
		}
		dsNode.intervalMS = int64(floatIntervalMS)
	}

	var floatMaxDP float64
	if rawMaxDP := rn.Query["maxDataPoints"]; ok {
		if floatMaxDP, ok = rawMaxDP.(float64); !ok {
			return nil, fmt.Errorf("expected maxDataPoints to be an float64, got type %T for refId %v", rawMaxDP, rn.RefID)
		}
		dsNode.maxDP = int64(floatMaxDP)
	}

	return dsNode, nil
}

// Execute runs the node and adds the results to vars. If the node requires
// other nodes they must have already been executed and their results must
// already by in vars.
func (dn *DSNode) Execute(ctx context.Context, vars mathexp.Vars, s *Service) (mathexp.Results, error) {
	pc := backend.PluginContext{
		OrgID: dn.orgID,
		DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{
			ID:  dn.datasourceID,
			UID: dn.datasourceUID,
		},
	}

	q := []backend.DataQuery{
		{
			RefID:         dn.refID,
			MaxDataPoints: dn.maxDP,
			Interval:      time.Duration(int64(time.Millisecond) * dn.intervalMS),
			JSON:          dn.query,
			TimeRange: backend.TimeRange{
				From: dn.timeRange.From,
				To:   dn.timeRange.To,
			},
			QueryType: dn.queryType,
		},
	}

	resp, err := s.queryData(ctx, &backend.QueryDataRequest{
		PluginContext: pc,
		Queries:       q,
	})

	if err != nil {
		return mathexp.Results{}, err
	}

	vals := make([]mathexp.Value, 0)
	for refID, qr := range resp.Responses {
		if qr.Error != nil {
			return mathexp.Results{}, fmt.Errorf("failed to execute query %v: %w", refID, qr.Error)
		}

		if len(qr.Frames) == 1 {
			frame := qr.Frames[0]
			if frame.TimeSeriesSchema().Type == data.TimeSeriesTypeNot && isNumberTable(frame) {
				backend.Logger.Debug("expression datasource query (numberSet)", "query", refID)
				numberSet, err := extractNumberSet(frame)
				if err != nil {
					return mathexp.Results{}, err
				}
				for _, n := range numberSet {
					vals = append(vals, n)
				}

				return mathexp.Results{
					Values: vals,
				}, nil
			}
		}

		for _, frame := range qr.Frames {
			backend.Logger.Debug("expression datasource query (seriesSet)", "query", refID)
			series, err := WideToMany(frame)
			if err != nil {
				return mathexp.Results{}, err
			}
			for _, s := range series {
				vals = append(vals, s)
			}
		}
	}
	return mathexp.Results{
		Values: vals,
	}, nil
}

func isNumberTable(frame *data.Frame) bool {
	if frame == nil || frame.Fields == nil {
		return false
	}
	numericCount := 0
	stringCount := 0
	otherCount := 0
	for _, field := range frame.Fields {
		fType := field.Type()
		switch {
		case fType.Numeric():
			numericCount++
		case fType == data.FieldTypeString || fType == data.FieldTypeNullableString:
			stringCount++
		default:
			otherCount++
		}
	}
	return numericCount == 1 && otherCount == 0
}

func extractNumberSet(frame *data.Frame) ([]mathexp.Number, error) {
	numericField := 0
	stringFieldIdxs := []int{}
	stringFieldNames := []string{}
	for i, field := range frame.Fields {
		fType := field.Type()
		switch {
		case fType.Numeric():
			numericField = i
		case fType == data.FieldTypeString || fType == data.FieldTypeNullableString:
			stringFieldIdxs = append(stringFieldIdxs, i)
			stringFieldNames = append(stringFieldNames, field.Name)
		}
	}
	numbers := make([]mathexp.Number, frame.Rows())

	for rowIdx := 0; rowIdx < frame.Rows(); rowIdx++ {
		val, _ := frame.FloatAt(numericField, rowIdx)
		var labels data.Labels
		for i := 0; i < len(stringFieldIdxs); i++ {
			if i == 0 {
				labels = make(data.Labels)
			}
			key := stringFieldNames[i] // TODO check for duplicate string column names
			val, _ := frame.ConcreteAt(stringFieldIdxs[i], rowIdx)
			labels[key] = val.(string) // TODO check assertion / return error
		}

		n := mathexp.NewNumber("", labels)
		n.SetValue(&val)
		numbers[rowIdx] = n
	}
	return numbers, nil
}

// WideToMany converts a data package wide type Frame to one or multiple Series. A series
// is created for each value type column of wide frame.
//
// This might not be a good idea long term, but works now as an adapter/shim.
func WideToMany(frame *data.Frame) ([]mathexp.Series, error) {
	tsSchema := frame.TimeSeriesSchema()
	if tsSchema.Type != data.TimeSeriesTypeWide {
		return nil, fmt.Errorf("input data must be a wide series but got type %s (input refid)", tsSchema.Type)
	}

	if len(tsSchema.ValueIndices) == 1 {
		s, err := mathexp.SeriesFromFrame(frame)
		if err != nil {
			return nil, err
		}
		return []mathexp.Series{s}, nil
	}

	series := []mathexp.Series{}
	for _, valIdx := range tsSchema.ValueIndices {
		l := frame.Rows()
		f := data.NewFrameOfFieldTypes(frame.Name, l, frame.Fields[tsSchema.TimeIndex].Type(), frame.Fields[valIdx].Type())
		f.Fields[0].Name = frame.Fields[tsSchema.TimeIndex].Name
		f.Fields[1].Name = frame.Fields[valIdx].Name
		if frame.Fields[valIdx].Labels != nil {
			f.Fields[1].Labels = frame.Fields[valIdx].Labels.Copy()
		}
		for i := 0; i < l; i++ {
			f.SetRow(i, frame.Fields[tsSchema.TimeIndex].CopyAt(i), frame.Fields[valIdx].CopyAt(i))
		}
		s, err := mathexp.SeriesFromFrame(f)
		if err != nil {
			return nil, err
		}
		series = append(series, s)
	}

	return series, nil
}
<commit_msg>Chore: Swap out use of backend plugin SDK's backend.Logger (#33783)<commit_after>package expr

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/grafana/grafana-plugin-sdk-go/backend"
	"github.com/grafana/grafana-plugin-sdk-go/data"
	"github.com/grafana/grafana/pkg/expr/classic"
	"github.com/grafana/grafana/pkg/expr/mathexp"
	"github.com/grafana/grafana/pkg/infra/log"

	"gonum.org/v1/gonum/graph/simple"
)

var (
	logger = log.New("expr")
)

// baseNode includes commmon properties used across DPNodes.
type baseNode struct {
	id    int64
	refID string
}

type rawNode struct {
	RefID         string `json:"refId"`
	Query         map[string]interface{}
	QueryType     string
	TimeRange     TimeRange
	DatasourceUID string
}

func (rn *rawNode) GetDatasourceName() (string, error) {
	rawDs, ok := rn.Query["datasource"]
	if !ok {
		return "", nil
	}
	dsName, ok := rawDs.(string)
	if !ok {
		return "", fmt.Errorf("expted datasource identifier to be a string, got %T", rawDs)
	}
	return dsName, nil
}

func (rn *rawNode) GetCommandType() (c CommandType, err error) {
	rawType, ok := rn.Query["type"]
	if !ok {
		return c, fmt.Errorf("no expression command type in query for refId %v", rn.RefID)
	}
	typeString, ok := rawType.(string)
	if !ok {
		return c, fmt.Errorf("expected expression command type to be a string, got type %T", rawType)
	}
	return ParseCommandType(typeString)
}

// String returns a string representation of the node. In particular for
// %v formating in error messages.
func (b *baseNode) String() string {
	return b.refID
}

// CMDNode is a DPNode that holds an expression command.
type CMDNode struct {
	baseNode
	CMDType CommandType
	Command Command
}

// ID returns the id of the node so it can fulfill the gonum's graph Node interface.
func (b *baseNode) ID() int64 {
	return b.id
}

// RefID returns the refId of the node.
func (b *baseNode) RefID() string {
	return b.refID
}

// NodeType returns the data pipeline node type.
func (gn *CMDNode) NodeType() NodeType {
	return TypeCMDNode
}

// Execute runs the node and adds the results to vars. If the node requires
// other nodes they must have already been executed and their results must
// already by in vars.
func (gn *CMDNode) Execute(ctx context.Context, vars mathexp.Vars, s *Service) (mathexp.Results, error) {
	return gn.Command.Execute(ctx, vars)
}

func buildCMDNode(dp *simple.DirectedGraph, rn *rawNode) (*CMDNode, error) {
	commandType, err := rn.GetCommandType()
	if err != nil {
		return nil, fmt.Errorf("invalid expression command type in '%v'", rn.RefID)
	}

	node := &CMDNode{
		baseNode: baseNode{
			id:    dp.NewNode().ID(),
			refID: rn.RefID,
		},
		CMDType: commandType,
	}

	switch commandType {
	case TypeMath:
		node.Command, err = UnmarshalMathCommand(rn)
	case TypeReduce:
		node.Command, err = UnmarshalReduceCommand(rn)
	case TypeResample:
		node.Command, err = UnmarshalResampleCommand(rn)
	case TypeClassicConditions:
		node.Command, err = classic.UnmarshalConditionsCmd(rn.Query, rn.RefID)
	default:
		return nil, fmt.Errorf("expression command type '%v' in '%v' not implemented", commandType, rn.RefID)
	}
	if err != nil {
		return nil, err
	}

	return node, nil
}

const (
	defaultIntervalMS = int64(64)
	defaultMaxDP      = int64(5000)
)

// DSNode is a DPNode that holds a datasource request.
type DSNode struct {
	baseNode
	query         json.RawMessage
	datasourceID  int64
	datasourceUID string

	orgID      int64
	queryType  string
	timeRange  TimeRange
	intervalMS int64
	maxDP      int64
}

// NodeType returns the data pipeline node type.
func (dn *DSNode) NodeType() NodeType {
	return TypeDatasourceNode
}

func (s *Service) buildDSNode(dp *simple.DirectedGraph, rn *rawNode, orgID int64) (*DSNode, error) {
	encodedQuery, err := json.Marshal(rn.Query)
	if err != nil {
		return nil, err
	}

	dsNode := &DSNode{
		baseNode: baseNode{
			id:    dp.NewNode().ID(),
			refID: rn.RefID,
		},
		orgID:      orgID,
		query:      json.RawMessage(encodedQuery),
		queryType:  rn.QueryType,
		intervalMS: defaultIntervalMS,
		maxDP:      defaultMaxDP,
		timeRange:  rn.TimeRange,
	}

	rawDsID, ok := rn.Query["datasourceId"]
	switch ok {
	case true:
		floatDsID, ok := rawDsID.(float64)
		if !ok {
			return nil, fmt.Errorf("expected datasourceId to be a float64, got type %T for refId %v", rawDsID, rn.RefID)
		}
		dsNode.datasourceID = int64(floatDsID)
	default:
		if rn.DatasourceUID == "" {
			return nil, fmt.Errorf("neither datasourceId or datasourceUid in expression data source request for refId %v", rn.RefID)
		}
		dsNode.datasourceUID = rn.DatasourceUID
	}

	var floatIntervalMS float64
	if rawIntervalMS := rn.Query["intervalMs"]; ok {
		if floatIntervalMS, ok = rawIntervalMS.(float64); !ok {
			return nil, fmt.Errorf("expected intervalMs to be an float64, got type %T for refId %v", rawIntervalMS, rn.RefID)
		}
		dsNode.intervalMS = int64(floatIntervalMS)
	}

	var floatMaxDP float64
	if rawMaxDP := rn.Query["maxDataPoints"]; ok {
		if floatMaxDP, ok = rawMaxDP.(float64); !ok {
			return nil, fmt.Errorf("expected maxDataPoints to be an float64, got type %T for refId %v", rawMaxDP, rn.RefID)
		}
		dsNode.maxDP = int64(floatMaxDP)
	}

	return dsNode, nil
}

// Execute runs the node and adds the results to vars. If the node requires
// other nodes they must have already been executed and their results must
// already by in vars.
func (dn *DSNode) Execute(ctx context.Context, vars mathexp.Vars, s *Service) (mathexp.Results, error) {
	pc := backend.PluginContext{
		OrgID: dn.orgID,
		DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{
			ID:  dn.datasourceID,
			UID: dn.datasourceUID,
		},
	}

	q := []backend.DataQuery{
		{
			RefID:         dn.refID,
			MaxDataPoints: dn.maxDP,
			Interval:      time.Duration(int64(time.Millisecond) * dn.intervalMS),
			JSON:          dn.query,
			TimeRange: backend.TimeRange{
				From: dn.timeRange.From,
				To:   dn.timeRange.To,
			},
			QueryType: dn.queryType,
		},
	}

	resp, err := s.queryData(ctx, &backend.QueryDataRequest{
		PluginContext: pc,
		Queries:       q,
	})

	if err != nil {
		return mathexp.Results{}, err
	}

	vals := make([]mathexp.Value, 0)
	for refID, qr := range resp.Responses {
		if qr.Error != nil {
			return mathexp.Results{}, fmt.Errorf("failed to execute query %v: %w", refID, qr.Error)
		}

		if len(qr.Frames) == 1 {
			frame := qr.Frames[0]
			if frame.TimeSeriesSchema().Type == data.TimeSeriesTypeNot && isNumberTable(frame) {
				logger.Debug("expression datasource query (numberSet)", "query", refID)
				numberSet, err := extractNumberSet(frame)
				if err != nil {
					return mathexp.Results{}, err
				}
				for _, n := range numberSet {
					vals = append(vals, n)
				}

				return mathexp.Results{
					Values: vals,
				}, nil
			}
		}

		for _, frame := range qr.Frames {
			logger.Debug("expression datasource query (seriesSet)", "query", refID)
			series, err := WideToMany(frame)
			if err != nil {
				return mathexp.Results{}, err
			}
			for _, s := range series {
				vals = append(vals, s)
			}
		}
	}
	return mathexp.Results{
		Values: vals,
	}, nil
}

func isNumberTable(frame *data.Frame) bool {
	if frame == nil || frame.Fields == nil {
		return false
	}
	numericCount := 0
	stringCount := 0
	otherCount := 0
	for _, field := range frame.Fields {
		fType := field.Type()
		switch {
		case fType.Numeric():
			numericCount++
		case fType == data.FieldTypeString || fType == data.FieldTypeNullableString:
			stringCount++
		default:
			otherCount++
		}
	}
	return numericCount == 1 && otherCount == 0
}

func extractNumberSet(frame *data.Frame) ([]mathexp.Number, error) {
	numericField := 0
	stringFieldIdxs := []int{}
	stringFieldNames := []string{}
	for i, field := range frame.Fields {
		fType := field.Type()
		switch {
		case fType.Numeric():
			numericField = i
		case fType == data.FieldTypeString || fType == data.FieldTypeNullableString:
			stringFieldIdxs = append(stringFieldIdxs, i)
			stringFieldNames = append(stringFieldNames, field.Name)
		}
	}
	numbers := make([]mathexp.Number, frame.Rows())

	for rowIdx := 0; rowIdx < frame.Rows(); rowIdx++ {
		val, _ := frame.FloatAt(numericField, rowIdx)
		var labels data.Labels
		for i := 0; i < len(stringFieldIdxs); i++ {
			if i == 0 {
				labels = make(data.Labels)
			}
			key := stringFieldNames[i] // TODO check for duplicate string column names
			val, _ := frame.ConcreteAt(stringFieldIdxs[i], rowIdx)
			labels[key] = val.(string) // TODO check assertion / return error
		}

		n := mathexp.NewNumber("", labels)
		n.SetValue(&val)
		numbers[rowIdx] = n
	}
	return numbers, nil
}

// WideToMany converts a data package wide type Frame to one or multiple Series. A series
// is created for each value type column of wide frame.
//
// This might not be a good idea long term, but works now as an adapter/shim.
func WideToMany(frame *data.Frame) ([]mathexp.Series, error) {
	tsSchema := frame.TimeSeriesSchema()
	if tsSchema.Type != data.TimeSeriesTypeWide {
		return nil, fmt.Errorf("input data must be a wide series but got type %s (input refid)", tsSchema.Type)
	}

	if len(tsSchema.ValueIndices) == 1 {
		s, err := mathexp.SeriesFromFrame(frame)
		if err != nil {
			return nil, err
		}
		return []mathexp.Series{s}, nil
	}

	series := []mathexp.Series{}
	for _, valIdx := range tsSchema.ValueIndices {
		l := frame.Rows()
		f := data.NewFrameOfFieldTypes(frame.Name, l, frame.Fields[tsSchema.TimeIndex].Type(), frame.Fields[valIdx].Type())
		f.Fields[0].Name = frame.Fields[tsSchema.TimeIndex].Name
		f.Fields[1].Name = frame.Fields[valIdx].Name
		if frame.Fields[valIdx].Labels != nil {
			f.Fields[1].Labels = frame.Fields[valIdx].Labels.Copy()
		}
		for i := 0; i < l; i++ {
			f.SetRow(i, frame.Fields[tsSchema.TimeIndex].CopyAt(i), frame.Fields[valIdx].CopyAt(i))
		}
		s, err := mathexp.SeriesFromFrame(f)
		if err != nil {
			return nil, err
		}
		series = append(series, s)
	}

	return series, nil
}
<|endoftext|>


<commit_before>// Released under an MIT license. See LICENSE.

package task

import (
	. "github.com/michaelmacinnis/oh/pkg/cell"
	"unsafe"
)

func Deref(name string, address uintptr) Cell {
	switch {
	case name == "bound":
		return (*bound)(unsafe.Pointer(address))
	case name == "builtin":
		return (*builtin)(unsafe.Pointer(address))
	case name == "channel":
		return (*Channel)(unsafe.Pointer(address))
	case name == "continuation":
		return (*continuation)(unsafe.Pointer(address))
	case name == "method":
		return (*method)(unsafe.Pointer(address))
	case name == "object":
		return (*object)(unsafe.Pointer(address))
	case name == "pipe":
		return (*Pipe)(unsafe.Pointer(address))
	case name == "scope":
		return (*scope)(unsafe.Pointer(address))
	case name == "syntax":
		return (*syntax)(unsafe.Pointer(address))
	case name == "task":
		return (*Task)(unsafe.Pointer(address))
	case name == "unbound":
		return (*unbound)(unsafe.Pointer(address))
	}

	return Null
}
<commit_msg>Formatting.<commit_after>// Released under an MIT license. See LICENSE.

package task

import (
	. "github.com/michaelmacinnis/oh/pkg/cell"
	"unsafe"
)

func Deref(name string, address uintptr) Cell {
	switch {
	case name == "channel":
		return (*Channel)(unsafe.Pointer(address))
	case name == "pipe":
		return (*Pipe)(unsafe.Pointer(address))
	case name == "task":
		return (*Task)(unsafe.Pointer(address))

	case name == "bound":
		return (*bound)(unsafe.Pointer(address))
	case name == "builtin":
		return (*builtin)(unsafe.Pointer(address))
	case name == "continuation":
		return (*continuation)(unsafe.Pointer(address))
	case name == "method":
		return (*method)(unsafe.Pointer(address))
	case name == "object":
		return (*object)(unsafe.Pointer(address))
	case name == "scope":
		return (*scope)(unsafe.Pointer(address))
	case name == "syntax":
		return (*syntax)(unsafe.Pointer(address))
	case name == "unbound":
		return (*unbound)(unsafe.Pointer(address))
	}

	return Null
}
<|endoftext|>


<commit_before>// Copyright (c) 2018-2018 Laurent Moussault. All rights reserved.
// Licensed under a simplified BSD license (see LICENSE file).

package plane_test

import (
	"math/rand"
	"testing"

	"github.com/cozely/cozely"
	"github.com/cozely/cozely/color"
	"github.com/cozely/cozely/coord"
	"github.com/cozely/cozely/input"
	"github.com/cozely/cozely/pixel"
	"github.com/cozely/cozely/plane"
	"github.com/cozely/cozely/x/math32"
)

////////////////////////////////////////////////////////////////////////////////

var (
	canvas1  = pixel.Canvas(pixel.Zoom(2))
	palette1 = color.Palette()
	col1     = palette1.Entry(color.LRGB{1, 1, 1})
	col2     = palette1.Entry(color.LRGB{0.4, 0.05, 0.0})
	col3     = palette1.Entry(color.LRGB{0.0, 0.4, 0.05})
	col4     = palette1.Entry(color.LRGB{0.0, 0.05, 0.45})
	col5     = palette1.Entry(color.LRGB{0.1, 0.0, 0.15})
	col6     = palette1.Entry(color.LRGB{0.25, 0.25, 0.25})
	col7     = palette1.Entry(color.LRGB{0.025, 0.025, 0.025})
)

var (
	points []coord.XY
)

var (
	ratio  float32
	offset coord.XY
)

const aboveall = int16(0x7FFF)

type loop1 struct{}

////////////////////////////////////////////////////////////////////////////////

func TestTest1(t *testing.T) {
	do(func() {
		defer cozely.Recover()

		err := cozely.Run(loop1{})
		if err != nil {
			t.Error(err)
		}
	})
}

func (loop1) Enter() {
	input.Bind(bindings)
	context.Activate(1)

	points = make([]coord.XY, 3)
	newPoints()

	palette1.Activate()
}

func (loop1) Leave() {
}

////////////////////////////////////////////////////////////////////////////////

func (loop1) React() {
	if next.JustPressed(1) {
		newPoints()
	}

	if quit.JustPressed(1) {
		cozely.Stop(nil)
	}
}

func (loop1) Update() {
}

func (loop1) Render() {
	canvas1.Clear(0)
	ratio = float32(canvas1.Size().R)
	offset = coord.XY{
		X: (float32(canvas1.Size().C) - ratio),
		Y: float32(canvas1.Size().R),
	}
	pt := make([]coord.CR, len(points))
	s := coord.CR{5, 5}
	for i, sd := range points {
		pt[i] = toScreen(sd)
		var c color.Index
		switch i {
		case 0:
			c = col2
		case 1:
			c = col3
		case 2:
			c = col4
		}
		canvas1.Box(c, c, 2, 2, pt[i].Minus(s), pt[i].Plus(s))
		canvas1.Locate(aboveall, coord.CR{pt[i].C - 2, pt[i].R + 3})
		canvas1.Text(col1-1, 0)
		canvas1.Print([]string{"A", "B", "C"}[i])
	}
	canvas1.Lines(col6, 0, pt[0], pt[1], pt[2], pt[0])
	canvas1.Triangles(col7, -5, pt[0], pt[1], pt[2], pt[0])

	m := canvas1.FromWindow(input.Cursor.Position())
	p := fromScreen(m)
	canvas1.Locate(aboveall, coord.CR{2, 8})
	canvas1.Text(col1-1, 0)
	canvas1.Printf("A: %.3f, %.3f\n", points[0].X, points[0].Y)
	canvas1.Printf("B: %.3f, %.3f\n", points[1].X, points[1].Y)
	canvas1.Printf("C: %.3f, %.3f\n", points[2].X, points[2].Y)
	if p.X >= 0 {
		canvas1.Printf("   %.3f, %.3f\n", p.X, p.Y)
	} else {
		canvas1.Println(" ")
	}
	canvas1.Point(col1, 1, m)

	canvas1.Println()

	if plane.IsCCW(points[0], points[1], points[2]) {
		canvas1.Text(col4-1, 0)
		canvas1.Println("IsCCW: TRUE")
	} else {
		canvas1.Text(col1-1, 0)
		canvas1.Println("IsCCW: false")
	}

	if plane.InTriangle(points[0], points[1], points[2], p) {
		canvas1.Text(col2-1, 0)
		canvas1.Println("InTriangle: TRUE")
	} else {
		canvas1.Text(col1-1, 0)
		canvas1.Println("InTriangle: false")
	}

	a, b, c := 0, 1, 2
	if !plane.IsCCW(points[a], points[b], points[c]) {
		b, c = c, b
	}
	if plane.InTriangleCCW(points[a], points[b], points[c], p) {
		canvas1.Text(col2-1, 0)
		canvas1.Println("InTriangleCCW: TRUE")
	} else {
		canvas1.Text(col1-1, 0)
		canvas1.Println("InTriangleCCW: false")
	}

	if plane.InCircumcircle(points[a], points[b], points[c], p) {
		canvas1.Text(col3-1, 0)
		canvas1.Println("InCircumcircle: TRUE")
	} else {
		canvas1.Text(col1-1, 0)
		canvas1.Println("InCircumcircle: false")
	}

	canvas1.Println(" ")

	d := plane.Circumcenter(points[0], points[1], points[2])
	canvas1.Text(col1-1, 0)
	canvas1.Printf("Circumcenter: %.3f, %.3f\n", d.X, d.Y)
	dd := toScreen(d)
	canvas1.Lines(col5, -2, dd.Minuss(2), dd.Pluss(2))
	canvas1.Lines(col5, -2, dd.Minuss(2).Perp(), dd.Pluss(2).Perp())

	r := d.Minus(points[a]).Length()
	cir := []coord.CR{}
	for a := float32(0); a <= 2*math32.Pi+0.01; a += math32.Pi / 32 {
		cir = append(cir, toScreen(coord.RA{r, a}.XY().Plus(d)))
	}
	canvas1.Lines(col5, -2, cir...)

	canvas1.Display()
}

func toScreen(p coord.XY) coord.CR {
	return coord.CR{
		C: int16(offset.X + p.X*ratio),
		R: int16(offset.Y - p.Y*ratio),
	}
}

func fromScreen(p coord.CR) coord.XY {
	return (p.XY().FlipY().Minus(offset.FlipY())).Slash(ratio)
}

func newPoints() {
	for i := range points {
		points[i] = coord.XY{X: rand.Float32(), Y: rand.Float32()}
	}
}
<commit_msg>Fix cosmetic bug<commit_after>// Copyright (c) 2018-2018 Laurent Moussault. All rights reserved.
// Licensed under a simplified BSD license (see LICENSE file).

package plane_test

import (
	"math/rand"
	"testing"

	"github.com/cozely/cozely"
	"github.com/cozely/cozely/color"
	"github.com/cozely/cozely/coord"
	"github.com/cozely/cozely/input"
	"github.com/cozely/cozely/pixel"
	"github.com/cozely/cozely/plane"
	"github.com/cozely/cozely/x/math32"
)

////////////////////////////////////////////////////////////////////////////////

var (
	canvas1  = pixel.Canvas(pixel.Zoom(2))
	palette1 = color.Palette()
	col1     = palette1.Entry(color.LRGB{1, 1, 1})
	col2     = palette1.Entry(color.LRGB{0.4, 0.05, 0.0})
	col3     = palette1.Entry(color.LRGB{0.0, 0.4, 0.05})
	col4     = palette1.Entry(color.LRGB{0.0, 0.05, 0.45})
	col5     = palette1.Entry(color.LRGB{0.1, 0.0, 0.15})
	col6     = palette1.Entry(color.LRGB{0.25, 0.25, 0.25})
	col7     = palette1.Entry(color.LRGB{0.025, 0.025, 0.025})
)

var (
	points []coord.XY
)

var (
	ratio  float32
	offset coord.XY
)

const aboveall = int16(0x7FFF)

type loop1 struct{}

////////////////////////////////////////////////////////////////////////////////

func TestTest1(t *testing.T) {
	do(func() {
		defer cozely.Recover()

		err := cozely.Run(loop1{})
		if err != nil {
			t.Error(err)
		}
	})
}

func (loop1) Enter() {
	input.Bind(bindings)
	context.Activate(1)

	points = make([]coord.XY, 3)
	newPoints()

	palette1.Activate()
}

func (loop1) Leave() {
}

////////////////////////////////////////////////////////////////////////////////

func (loop1) React() {
	if next.JustPressed(1) {
		newPoints()
	}

	if quit.JustPressed(1) {
		cozely.Stop(nil)
	}
}

func (loop1) Update() {
}

func (loop1) Render() {
	canvas1.Clear(0)
	ratio = float32(canvas1.Size().R)
	offset = coord.XY{
		X: (float32(canvas1.Size().C) - ratio),
		Y: float32(canvas1.Size().R),
	}
	pt := make([]coord.CR, len(points))
	s := coord.CR{5, 5}
	for i, sd := range points {
		pt[i] = toScreen(sd)
		var c color.Index
		switch i {
		case 0:
			c = col2
		case 1:
			c = col3
		case 2:
			c = col4
		}
		canvas1.Box(c, c, 2, 2, pt[i].Minus(s), pt[i].Plus(s))
		canvas1.Locate(aboveall, coord.CR{pt[i].C - 2, pt[i].R + 3})
		canvas1.Text(col1-1, 0)
		canvas1.Print([]string{"A", "B", "C"}[i])
	}
	canvas1.Lines(col6, 0, pt[0], pt[1], pt[2], pt[0])
	canvas1.Triangles(col7, -5, pt[0], pt[1], pt[2], pt[0])

	m := canvas1.FromWindow(input.Cursor.Position())
	p := fromScreen(m)
	canvas1.Locate(aboveall, coord.CR{2, 8})
	canvas1.Text(col1-1, 0)
	canvas1.Printf("A: %.3f, %.3f\n", points[0].X, points[0].Y)
	canvas1.Printf("B: %.3f, %.3f\n", points[1].X, points[1].Y)
	canvas1.Printf("C: %.3f, %.3f\n", points[2].X, points[2].Y)
	if p.X >= 0 {
		canvas1.Printf("   %.3f, %.3f\n", p.X, p.Y)
	} else {
		canvas1.Println(" ")
	}
	canvas1.Point(col1, 1, m)

	canvas1.Println()

	if plane.IsCCW(points[0], points[1], points[2]) {
		canvas1.Text(col4-1, 0)
		canvas1.Println("IsCCW: TRUE")
	} else {
		canvas1.Text(col1-1, 0)
		canvas1.Println("IsCCW: false")
	}

	if plane.InTriangle(points[0], points[1], points[2], p) {
		canvas1.Text(col2-1, 0)
		canvas1.Println("InTriangle: TRUE")
	} else {
		canvas1.Text(col1-1, 0)
		canvas1.Println("InTriangle: false")
	}

	a, b, c := 0, 1, 2
	if !plane.IsCCW(points[a], points[b], points[c]) {
		b, c = c, b
	}
	if plane.InTriangleCCW(points[a], points[b], points[c], p) {
		canvas1.Text(col2-1, 0)
		canvas1.Println("InTriangleCCW: TRUE")
	} else {
		canvas1.Text(col1-1, 0)
		canvas1.Println("InTriangleCCW: false")
	}

	if plane.InCircumcircle(points[a], points[b], points[c], p) {
		canvas1.Text(col3-1, 0)
		canvas1.Println("InCircumcircle: TRUE")
	} else {
		canvas1.Text(col1-1, 0)
		canvas1.Println("InCircumcircle: false")
	}

	canvas1.Println(" ")

	d := plane.Circumcenter(points[0], points[1], points[2])
	canvas1.Text(col1-1, 0)
	canvas1.Printf("Circumcenter: %.3f, %.3f\n", d.X, d.Y)
	dd := toScreen(d)
	canvas1.Lines(col5, -2, dd.Minuss(2), dd.Pluss(2))
	canvas1.Lines(col5, -2, dd.Minus(coord.CR{-2,2}), dd.Plus(coord.CR{-2,2}))

	r := d.Minus(points[a]).Length()
	cir := []coord.CR{}
	for a := float32(0); a <= 2*math32.Pi+0.01; a += math32.Pi / 32 {
		cir = append(cir, toScreen(coord.RA{r, a}.XY().Plus(d)))
	}
	canvas1.Lines(col5, -2, cir...)

	canvas1.Display()
}

func toScreen(p coord.XY) coord.CR {
	return coord.CR{
		C: int16(offset.X + p.X*ratio),
		R: int16(offset.Y - p.Y*ratio),
	}
}

func fromScreen(p coord.CR) coord.XY {
	return (p.XY().FlipY().Minus(offset.FlipY())).Slash(ratio)
}

func newPoints() {
	for i := range points {
		points[i] = coord.XY{X: rand.Float32(), Y: rand.Float32()}
	}
}
<|endoftext|>


<commit_before>package owl

import (
	"fmt"
	"time"

	"database/sql"

	cdb "github.com/Cepave/open-falcon-backend/common/db"
	sqlxExt "github.com/Cepave/open-falcon-backend/common/db/sqlx"
	"github.com/jmoiron/sqlx"
	"github.com/satori/go.uuid"
)

type Schedule struct {
	Name    string
	Timeout int
	Uuid    uuid.UUID
}

type OwlSchedule struct {
	Id             int       `db:"sch_id"`
	Name           string    `db:"sch_name"`
	Lock           byte      `db:"sch_lock"`
	LastUpdateTime time.Time `db:"sch_modify_time"`
}

type OwlScheduleLog struct {
	Uuid      db.DbUuid `db:"sl_uuid"`
	SchId     int       `db:"sl_sch_id"`
	StartTime time.Time `db:"sl_start_time"`
	EndTime   time.Time `db:"sl_end_time"`
	Timeout   int       `db:"sl_timeout"`
	Status    byte      `db:"sl_status"`
	Message   string    `db:"sl_message"`
}

var insertSql = `
	INSERT INTO owl_schedule_log(
		sl_uuid, sl_sch_id,
		sl_start_time, sl_timeout, sl_status
	)
	VALUES (:uuid, :schid, :starttime, :timeout, :status)
`

type TimeOutOfSchedule struct {
	Name          string
	AcquiredTime  time.Time
	LastStartTime time.Time
	Timeout       int
}

func (t *TimeOutOfSchedule) Error() string {
	return fmt.Sprintf("%s error: from %s to %s less than %ds", t.Name,
		t.LastStartTime.Format(time.RFC3339),
		t.AcquiredTime.Format(time.RFC3339),
		t.Timeout)
}

type LockStatus byte

const (
	FREE LockStatus = iota
	LOCKED
)

type TaskStatus byte

const (
	DONE TaskStatus = iota
	RUN
	FAIL
	TIMEOUT
)

func AcquireLock(schedule *Schedule) error {
	txProcessor := &txAcquireLock{schedule, nil}
	DbFacade.SqlxDbCtrl.InTx(txProcessor)
	return txProcessor.timeoutError
}

type txAcquireLock struct {
	schedule     *Schedule
	timeoutError *TimeOutOfSchedule
}

func (ack *txAcquireLock) InTx(tx *sqlx.Tx) cdb.TxFinale {

	now := time.Now()

	/**
	 * Lock table
	 */
	ret := ack.selectOrInsertLock(tx, now)
	// The previous task is not timeout()
	if ret.isLocked() && ack.notTimeout(tx, now) {
		return cdb.TxCommit
	}

	if !ack.successUpdateLock(tx, now) {
		return cdb.TxRollback
	}
	// :~)

	/**
	 * Log table
	 */
	generatedUuid := uuid.NewV4()
	r := sqlxExt.ToTxExt(tx).NamedExec(insertSql,
		map[string]interface{}{
			"uuid":      generatedUuid,
			"schid":     ret.Id,
			"starttime": now,
			"timeout":   ack.schedule.Timeout,
			"status":    RUN,
		},
	)
	if !isCorrectRowsAffected(r, 1) {
		return cdb.TxRollback
	}

	ack.schedule.Uuid = generatedUuid
	// :~)

	return cdb.TxCommit
}

type scheduleLock struct {
	Id   int  `db:"sch_id"`
	Lock byte `db:"sch_lock"`
}

func (sck *scheduleLock) isLocked() bool {
	return sck.Lock == byte(LOCKED)
}

func (ack *txAcquireLock) selectOrInsertLock(tx *sqlx.Tx, now time.Time) (ret scheduleLock) {
	name := ack.schedule.Name
	exist := sqlxExt.ToTxExt(tx).GetOrNoRow(&ret, `
		SELECT sch_id, sch_lock
		FROM owl_schedule
		WHERE sch_name = ?
		FOR UPDATE
	`, name)

	if !exist {
		r := tx.MustExec(`
			INSERT INTO owl_schedule(
				sch_name,
				sch_lock, sch_modify_time
			)
			VALUES (?, 0, ?)
		`, name, now)
		ret.Id = int(cdb.ToResultExt(r).LastInsertId())
		ret.Lock = byte(FREE)
	}

	return
}

func (ack *txAcquireLock) successUpdateLock(tx *sqlx.Tx, now time.Time) bool {
	r := tx.MustExec(`
		UPDATE owl_schedule
		SET sch_lock = 1,
			sch_modify_time = ?
		WHERE sch_name = ?
	`, now, ack.schedule.Name)
	return isCorrectRowsAffected(r, 1)
}

func (ack *txAcquireLock) notTimeout(tx *sqlx.Tx, now time.Time) bool {
	ret := struct {
		StartTime time.Time `db:"sl_start_time"`
		Timeout   int       `db:"sl_timeout"`
	}{}
	sqlxExt.ToTxExt(tx).Get(&ret, `
		SELECT sl.sl_start_time, sl.sl_timeout
		FROM owl_schedule sch
		LEFT JOIN owl_schedule_log sl
		ON sch.sch_id = sl.sl_sch_id
		WHERE sch.sch_name = ?
		ORDER BY sl.sl_start_time DESC
		LIMIT 1
	`, ack.schedule.Name)

	shouldLocked := now.Sub(ret.StartTime) <= time.Duration(ret.Timeout)*time.Second
	if shouldLocked {
		ack.timeoutError = &TimeOutOfSchedule{
			Name:          "Schedule locked",
			LastStartTime: ret.StartTime,
			AcquiredTime:  now,
			Timeout:       ret.Timeout,
		}
	}
	return shouldLocked
}

func isCorrectRowsAffected(r sql.Result, expectRowsAffected int64) bool {
	return cdb.ToResultExt(r).RowsAffected() == expectRowsAffected
}
<commit_msg>[OWL-1997] Fix undefined import name.<commit_after>package owl

import (
	"fmt"
	"time"

	"database/sql"

	cdb "github.com/Cepave/open-falcon-backend/common/db"
	sqlxExt "github.com/Cepave/open-falcon-backend/common/db/sqlx"
	"github.com/jmoiron/sqlx"
	"github.com/satori/go.uuid"
)

type Schedule struct {
	Name    string
	Timeout int
	Uuid    uuid.UUID
}

type OwlSchedule struct {
	Id             int       `db:"sch_id"`
	Name           string    `db:"sch_name"`
	Lock           byte      `db:"sch_lock"`
	LastUpdateTime time.Time `db:"sch_modify_time"`
}

type OwlScheduleLog struct {
	Uuid      cdb.DbUuid `db:"sl_uuid"`
	SchId     int        `db:"sl_sch_id"`
	StartTime time.Time  `db:"sl_start_time"`
	EndTime   time.Time  `db:"sl_end_time"`
	Timeout   int        `db:"sl_timeout"`
	Status    byte       `db:"sl_status"`
	Message   string     `db:"sl_message"`
}

var insertSql = `
	INSERT INTO owl_schedule_log(
		sl_uuid, sl_sch_id,
		sl_start_time, sl_timeout, sl_status
	)
	VALUES (:uuid, :schid, :starttime, :timeout, :status)
`

type TimeOutOfSchedule struct {
	Name          string
	AcquiredTime  time.Time
	LastStartTime time.Time
	Timeout       int
}

func (t *TimeOutOfSchedule) Error() string {
	return fmt.Sprintf("%s error: from %s to %s less than %ds", t.Name,
		t.LastStartTime.Format(time.RFC3339),
		t.AcquiredTime.Format(time.RFC3339),
		t.Timeout)
}

type LockStatus byte

const (
	FREE LockStatus = iota
	LOCKED
)

type TaskStatus byte

const (
	DONE TaskStatus = iota
	RUN
	FAIL
	TIMEOUT
)

func AcquireLock(schedule *Schedule) error {
	txProcessor := &txAcquireLock{schedule, nil}
	DbFacade.SqlxDbCtrl.InTx(txProcessor)
	return txProcessor.timeoutError
}

type txAcquireLock struct {
	schedule     *Schedule
	timeoutError *TimeOutOfSchedule
}

func (ack *txAcquireLock) InTx(tx *sqlx.Tx) cdb.TxFinale {

	now := time.Now()

	/**
	 * Lock table
	 */
	ret := ack.selectOrInsertLock(tx, now)
	// The previous task is not timeout()
	if ret.isLocked() && ack.notTimeout(tx, now) {
		return cdb.TxCommit
	}

	if !ack.successUpdateLock(tx, now) {
		return cdb.TxRollback
	}
	// :~)

	/**
	 * Log table
	 */
	generatedUuid := uuid.NewV4()
	r := sqlxExt.ToTxExt(tx).NamedExec(insertSql,
		map[string]interface{}{
			"uuid":      generatedUuid,
			"schid":     ret.Id,
			"starttime": now,
			"timeout":   ack.schedule.Timeout,
			"status":    RUN,
		},
	)
	if !isCorrectRowsAffected(r, 1) {
		return cdb.TxRollback
	}

	ack.schedule.Uuid = generatedUuid
	// :~)

	return cdb.TxCommit
}

type scheduleLock struct {
	Id   int  `db:"sch_id"`
	Lock byte `db:"sch_lock"`
}

func (sck *scheduleLock) isLocked() bool {
	return sck.Lock == byte(LOCKED)
}

func (ack *txAcquireLock) selectOrInsertLock(tx *sqlx.Tx, now time.Time) (ret scheduleLock) {
	name := ack.schedule.Name
	exist := sqlxExt.ToTxExt(tx).GetOrNoRow(&ret, `
		SELECT sch_id, sch_lock
		FROM owl_schedule
		WHERE sch_name = ?
		FOR UPDATE
	`, name)

	if !exist {
		r := tx.MustExec(`
			INSERT INTO owl_schedule(
				sch_name,
				sch_lock, sch_modify_time
			)
			VALUES (?, 0, ?)
		`, name, now)
		ret.Id = int(cdb.ToResultExt(r).LastInsertId())
		ret.Lock = byte(FREE)
	}

	return
}

func (ack *txAcquireLock) successUpdateLock(tx *sqlx.Tx, now time.Time) bool {
	r := tx.MustExec(`
		UPDATE owl_schedule
		SET sch_lock = 1,
			sch_modify_time = ?
		WHERE sch_name = ?
	`, now, ack.schedule.Name)
	return isCorrectRowsAffected(r, 1)
}

func (ack *txAcquireLock) notTimeout(tx *sqlx.Tx, now time.Time) bool {
	ret := struct {
		StartTime time.Time `db:"sl_start_time"`
		Timeout   int       `db:"sl_timeout"`
	}{}
	sqlxExt.ToTxExt(tx).Get(&ret, `
		SELECT sl.sl_start_time, sl.sl_timeout
		FROM owl_schedule sch
		LEFT JOIN owl_schedule_log sl
		ON sch.sch_id = sl.sl_sch_id
		WHERE sch.sch_name = ?
		ORDER BY sl.sl_start_time DESC
		LIMIT 1
	`, ack.schedule.Name)

	shouldLocked := now.Sub(ret.StartTime) <= time.Duration(ret.Timeout)*time.Second
	if shouldLocked {
		ack.timeoutError = &TimeOutOfSchedule{
			Name:          "Schedule locked",
			LastStartTime: ret.StartTime,
			AcquiredTime:  now,
			Timeout:       ret.Timeout,
		}
	}
	return shouldLocked
}

func isCorrectRowsAffected(r sql.Result, expectRowsAffected int64) bool {
	return cdb.ToResultExt(r).RowsAffected() == expectRowsAffected
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package flogging

import (
	"io"

	"go.uber.org/zap/zapcore"
	"google.golang.org/grpc/grpclog"
)

const (
	defaultFormat = "%{color}%{time:2006-01-02 15:04:05.000 MST} [%{module}] %{shortfunc} -> %{level:.4s} %{id:03x}%{color:reset} %{message}"
	defaultLevel  = zapcore.InfoLevel
)

var Global *Logging

func init() {
	logging, err := New(Config{})
	if err != nil {
		panic(err)
	}

	Global = logging
	grpcLogger := Global.ZapLogger("grpc")
	grpclog.SetLogger(NewGRPCLogger(grpcLogger))
}

// Init initializes logging with the provided config.
func Init(config Config) {
	err := Global.Apply(config)
	if err != nil {
		panic(err)
	}
}

// Reset sets logging to the defaults defined in this package.
//
// Used in tests and in the package init
func Reset() {
	Global.Apply(Config{})
}

// LoggerLevel gets the current logging level for the logger with the
// provided name.
func LoggerLevel(loggerName string) string {
	return Global.Level(loggerName).String()
}

// MustGetLogger creates a logger with the specified name. If an invalid name
// is provided, the operation will panic.
func MustGetLogger(loggerName string) *FabricLogger {
	return Global.Logger(loggerName)
}

// ActivateSpec is used to activate a logging specification.
func ActivateSpec(spec string) {
	err := Global.ActivateSpec(spec)
	if err != nil {
		panic(err)
	}
}

// DefaultLevel returns the default log level.
func DefaultLevel() string {
	return defaultLevel.String()
}

// SetWriter calls SetWriter returning the previous value
// of the writer.
func SetWriter(w io.Writer) io.Writer {
	return Global.SetWriter(w)
}

// SetObserver calls SetObserver returning the previous value
// of the observer.
func SetObserver(observer Observer) Observer {
	return Global.SetObserver(observer)
}
<commit_msg>Move all fixed width log entry fields to the left<commit_after>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package flogging

import (
	"io"

	"go.uber.org/zap/zapcore"
	"google.golang.org/grpc/grpclog"
)

const (
	defaultFormat = "%{color}%{time:2006-01-02 15:04:05.000 MST} %{id:04x} %{level:.4s}%{color:reset} [%{module}] %{color:bold}%{shortfunc}%{color:reset} -> %{message}"
	defaultLevel  = zapcore.InfoLevel
)

var Global *Logging

func init() {
	logging, err := New(Config{})
	if err != nil {
		panic(err)
	}

	Global = logging
	grpcLogger := Global.ZapLogger("grpc")
	grpclog.SetLogger(NewGRPCLogger(grpcLogger))
}

// Init initializes logging with the provided config.
func Init(config Config) {
	err := Global.Apply(config)
	if err != nil {
		panic(err)
	}
}

// Reset sets logging to the defaults defined in this package.
//
// Used in tests and in the package init
func Reset() {
	Global.Apply(Config{})
}

// LoggerLevel gets the current logging level for the logger with the
// provided name.
func LoggerLevel(loggerName string) string {
	return Global.Level(loggerName).String()
}

// MustGetLogger creates a logger with the specified name. If an invalid name
// is provided, the operation will panic.
func MustGetLogger(loggerName string) *FabricLogger {
	return Global.Logger(loggerName)
}

// ActivateSpec is used to activate a logging specification.
func ActivateSpec(spec string) {
	err := Global.ActivateSpec(spec)
	if err != nil {
		panic(err)
	}
}

// DefaultLevel returns the default log level.
func DefaultLevel() string {
	return defaultLevel.String()
}

// SetWriter calls SetWriter returning the previous value
// of the writer.
func SetWriter(w io.Writer) io.Writer {
	return Global.SetWriter(w)
}

// SetObserver calls SetObserver returning the previous value
// of the observer.
func SetObserver(observer Observer) Observer {
	return Global.SetObserver(observer)
}
<|endoftext|>


<commit_before>package common

import (
	"bytes"
	"encoding/json"
	base "github.com/omegaup/go-base"
	"math/big"
	"time"
)

// LimitsSettings represents runtime limits for the Input.
type LimitsSettings struct {
	ExtraWallTime        base.Duration
	MemoryLimit          base.Byte
	OutputLimit          base.Byte
	OverallWallTimeLimit base.Duration
	TimeLimit            base.Duration
}

// ValidatorSettings represents the options used to validate outputs.
type ValidatorSettings struct {
	Lang      *string         `json:"Lang,omitempty"`
	Name      string          `json:"Name"`
	Tolerance *float64        `json:"Tolerance,omitempty"`
	Limits    *LimitsSettings `json:"Limits,omitempty"`
}

// InteractiveInterface represents the metadata needed to compile and run
// libinteractive problems.
type InteractiveInterface struct {
	MakefileRules []struct {
		Targets    []string
		Requisites []string
		Compiler   string
		Params     string
		Debug      bool
	}
	ExecutableDescription struct {
		Args []string
		Env  map[string]string
	}
	Files map[string]string
}

// InteractiveSettings contains the information needed by libinteractive to
// generate interactive shims.
type InteractiveSettings struct {
	Interfaces            map[string]map[string]*InteractiveInterface
	Templates             map[string]string
	Main                  string
	ModuleName            string
	ParentLang            string
	LibinteractiveVersion string
}

// CaseSettings contains the information of a single test case.
type CaseSettings struct {
	Name   string
	Weight *big.Rat
}

// MarshalJSON implements the json.Marshaler interface.
func (c *CaseSettings) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		Name   string
		Weight float64
	}{
		Name:   c.Name,
		Weight: RationalToFloat(c.Weight),
	})
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (c *CaseSettings) UnmarshalJSON(data []byte) error {
	if bytes.Equal(data, []byte("null")) {
		return nil
	}

	settings := struct {
		Name   string
		Weight float64
	}{}

	if err := json.Unmarshal(data, &settings); err != nil {
		return err
	}

	c.Name = settings.Name
	c.Weight = FloatToRational(settings.Weight)

	return nil
}

// A ByCaseName represents a list of CaseSettings associated with a group that
// implements the sort.Interface.
type ByCaseName []CaseSettings

func (c ByCaseName) Len() int           { return len(c) }
func (c ByCaseName) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }
func (c ByCaseName) Less(i, j int) bool { return c[i].Name < c[j].Name }

// GroupSettings contains the information of the test case groups.
type GroupSettings struct {
	Cases []CaseSettings
	Name  string
}

// Weight returns the sum of the individual case weights.
func (g *GroupSettings) Weight() *big.Rat {
	weight := &big.Rat{}
	for _, c := range g.Cases {
		weight.Add(weight, c.Weight)
	}
	return weight
}

// A ByGroupName represents a list of GroupSettings associated with a problem
// that implements the sort.Interface.
type ByGroupName []GroupSettings

func (g ByGroupName) Len() int           { return len(g) }
func (g ByGroupName) Swap(i, j int)      { g[i], g[j] = g[j], g[i] }
func (g ByGroupName) Less(i, j int) bool { return g[i].Name < g[j].Name }

// ProblemSettings represents the settings of a problem for a particular Input
// set.
type ProblemSettings struct {
	Cases       []GroupSettings      `json:"Cases"`
	Interactive *InteractiveSettings `json:"Interactive,omitempty"`
	Limits      LimitsSettings       `json:"Limits"`
	Slow        bool                 `json:"Slow"`
	Validator   ValidatorSettings    `json:"Validator"`
}

var (
	// DefaultValidatorLimits specifies the default limits for a validator.
	DefaultValidatorLimits = LimitsSettings{
		ExtraWallTime:        base.Duration(0),
		MemoryLimit:          base.Byte(256) * base.Mebibyte,
		OutputLimit:          base.Byte(10) * base.Kibibyte,
		OverallWallTimeLimit: base.Duration(time.Duration(5) * time.Second),
		TimeLimit:            base.Duration(time.Duration(1) * time.Second),
	}

	// DefaultLimits specifies the default limits for a problem.
	DefaultLimits = LimitsSettings{
		ExtraWallTime:        base.Duration(0),
		MemoryLimit:          base.Byte(32) * base.Mebibyte,
		OutputLimit:          base.Byte(10) * base.Kibibyte,
		OverallWallTimeLimit: base.Duration(time.Duration(1) * time.Minute),
		TimeLimit:            base.Duration(time.Duration(1) * time.Second),
	}
)

// SolutionSettings represents a single testcase with an expected score range
// and/or verdict. At least one of those must be present.
type SolutionSettings struct {
	Filename   string     `json:"filename"`
	ScoreRange []*big.Rat `json:"score_range,omitempty"`
	Verdict    string     `json:"verdict,omitempty"`
}

// MarshalJSON implements the json.Marshaler interface.
func (s *SolutionSettings) MarshalJSON() ([]byte, error) {
	var scoreRange []float64
	for _, score := range s.ScoreRange {
		scoreRange = append(scoreRange, RationalToFloat(score))
	}
	return json.Marshal(&struct {
		Filename   string    `json:"filename"`
		ScoreRange []float64 `json:"score_range,omitempty"`
		Verdict    string    `json:"verdict,omitempty"`
	}{
		Filename:   s.Filename,
		ScoreRange: scoreRange,
		Verdict:    s.Verdict,
	})
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (s *SolutionSettings) UnmarshalJSON(data []byte) error {
	settings := struct {
		Filename   string    `json:"filename"`
		ScoreRange []float64 `json:"score_range,omitempty"`
		Verdict    string    `json:"verdict,omitempty"`
	}{}

	if err := json.Unmarshal(data, &settings); err != nil {
		return err
	}

	s.Filename = settings.Filename
	for _, score := range settings.ScoreRange {
		s.ScoreRange = append(s.ScoreRange, FloatToRational(score))
	}
	s.Verdict = settings.Verdict

	return nil
}

// TestsSettings represent the tests that are to be run against the problem
// itself. They are stored in tests/settings.json.
type TestsSettings struct {
	Solutions []SolutionSettings `json:"solutions"`
}

var (
	// VerdictList is the sorted list of verdicts from worse to better.
	VerdictList = []string{
		"JE",
		"CE",
		"MLE",
		"RFE",
		"RTE",
		"TLE",
		"OLE",
		"WA",
		"PA",
		"AC",
		"OK",
	}
)
<commit_msg>Update the tests/tests.json schema<commit_after>package common

import (
	"bytes"
	"encoding/json"
	"fmt"
	base "github.com/omegaup/go-base"
	"github.com/pkg/errors"
	"math/big"
	"time"
)

// LimitsSettings represents runtime limits for the Input.
type LimitsSettings struct {
	ExtraWallTime        base.Duration
	MemoryLimit          base.Byte
	OutputLimit          base.Byte
	OverallWallTimeLimit base.Duration
	TimeLimit            base.Duration
}

// ValidatorSettings represents the options used to validate outputs.
type ValidatorSettings struct {
	Lang      *string         `json:"Lang,omitempty"`
	Name      string          `json:"Name"`
	Tolerance *float64        `json:"Tolerance,omitempty"`
	Limits    *LimitsSettings `json:"Limits,omitempty"`
}

// InteractiveInterface represents the metadata needed to compile and run
// libinteractive problems.
type InteractiveInterface struct {
	MakefileRules []struct {
		Targets    []string
		Requisites []string
		Compiler   string
		Params     string
		Debug      bool
	}
	ExecutableDescription struct {
		Args []string
		Env  map[string]string
	}
	Files map[string]string
}

// InteractiveSettings contains the information needed by libinteractive to
// generate interactive shims.
type InteractiveSettings struct {
	Interfaces            map[string]map[string]*InteractiveInterface
	Templates             map[string]string
	Main                  string
	ModuleName            string
	ParentLang            string
	LibinteractiveVersion string
}

// CaseSettings contains the information of a single test case.
type CaseSettings struct {
	Name   string
	Weight *big.Rat
}

// MarshalJSON implements the json.Marshaler interface.
func (c *CaseSettings) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		Name   string
		Weight float64
	}{
		Name:   c.Name,
		Weight: RationalToFloat(c.Weight),
	})
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (c *CaseSettings) UnmarshalJSON(data []byte) error {
	if bytes.Equal(data, []byte("null")) {
		return nil
	}

	settings := struct {
		Name   string
		Weight float64
	}{}

	if err := json.Unmarshal(data, &settings); err != nil {
		return err
	}

	c.Name = settings.Name
	c.Weight = FloatToRational(settings.Weight)

	return nil
}

// A ByCaseName represents a list of CaseSettings associated with a group that
// implements the sort.Interface.
type ByCaseName []CaseSettings

func (c ByCaseName) Len() int           { return len(c) }
func (c ByCaseName) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }
func (c ByCaseName) Less(i, j int) bool { return c[i].Name < c[j].Name }

// GroupSettings contains the information of the test case groups.
type GroupSettings struct {
	Cases []CaseSettings
	Name  string
}

// Weight returns the sum of the individual case weights.
func (g *GroupSettings) Weight() *big.Rat {
	weight := &big.Rat{}
	for _, c := range g.Cases {
		weight.Add(weight, c.Weight)
	}
	return weight
}

// A ByGroupName represents a list of GroupSettings associated with a problem
// that implements the sort.Interface.
type ByGroupName []GroupSettings

func (g ByGroupName) Len() int           { return len(g) }
func (g ByGroupName) Swap(i, j int)      { g[i], g[j] = g[j], g[i] }
func (g ByGroupName) Less(i, j int) bool { return g[i].Name < g[j].Name }

// ProblemSettings represents the settings of a problem for a particular Input
// set.
type ProblemSettings struct {
	Cases       []GroupSettings      `json:"Cases"`
	Interactive *InteractiveSettings `json:"Interactive,omitempty"`
	Limits      LimitsSettings       `json:"Limits"`
	Slow        bool                 `json:"Slow"`
	Validator   ValidatorSettings    `json:"Validator"`
}

var (
	// DefaultValidatorLimits specifies the default limits for a validator.
	DefaultValidatorLimits = LimitsSettings{
		ExtraWallTime:        base.Duration(0),
		MemoryLimit:          base.Byte(256) * base.Mebibyte,
		OutputLimit:          base.Byte(10) * base.Kibibyte,
		OverallWallTimeLimit: base.Duration(time.Duration(5) * time.Second),
		TimeLimit:            base.Duration(time.Duration(1) * time.Second),
	}

	// DefaultLimits specifies the default limits for a problem.
	DefaultLimits = LimitsSettings{
		ExtraWallTime:        base.Duration(0),
		MemoryLimit:          base.Byte(32) * base.Mebibyte,
		OutputLimit:          base.Byte(10) * base.Kibibyte,
		OverallWallTimeLimit: base.Duration(time.Duration(1) * time.Minute),
		TimeLimit:            base.Duration(time.Duration(1) * time.Second),
	}
)

// ScoreRange represents a minimum and a maximum score.
type ScoreRange struct {
	Min *big.Rat
	Max *big.Rat
}

// MarshalJSON implements the json.Marshaler interface.
func (r *ScoreRange) MarshalJSON() ([]byte, error) {
	return []byte(fmt.Sprintf(
		"[%f, %f]",
		RationalToFloat(r.Min),
		RationalToFloat(r.Max),
	)), nil
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (r *ScoreRange) UnmarshalJSON(data []byte) error {
	var floatScores []float64
	if err := json.Unmarshal(data, &floatScores); err != nil {
		return err
	}
	if len(floatScores) != 2 {
		return errors.Errorf("score_range should be an array with two numbers")
	}
	minScore := FloatToRational(floatScores[0])
	maxScore := FloatToRational(floatScores[1])

	if minScore.Cmp(maxScore) > 0 {
		return errors.Errorf("values for score_range should be sorted")
	}

	if (&big.Rat{}).Cmp(minScore) > 0 || maxScore.Cmp(big.NewRat(1, 1)) > 0 {
		return errors.Errorf("values for score_range should be in the interval [0, 1]")
	}

	r.Min = minScore
	r.Max = maxScore

	return nil
}

// SolutionSettings represents a single testcase with an expected score range
// and/or verdict. At least one of those must be present.
type SolutionSettings struct {
	Filename   string      `json:"filename"`
	ScoreRange *ScoreRange `json:"score_range,omitempty"`
	Verdict    string      `json:"verdict,omitempty"`
	Language   string      `json:"language,omitempty"`
}

// InputsValidatorSettings represents a validator for the .in files.
type InputsValidatorSettings struct {
	Filename string `json:"filename"`
	Language string `json:"language,omitempty"`
}

// TestsSettings represent the tests that are to be run against the problem
// itself. They are stored in tests/settings.json.
type TestsSettings struct {
	Solutions       []SolutionSettings       `json:"solutions"`
	InputsValidator *InputsValidatorSettings `json:"inputs,omitempty"`
}

var (
	// VerdictList is the sorted list of verdicts from worse to better.
	VerdictList = []string{
		"JE",
		"CE",
		"MLE",
		"RFE",
		"RTE",
		"TLE",
		"OLE",
		"WA",
		"PA",
		"AC",
		"OK",
	}
)
<|endoftext|>


<commit_before>package app

import (
	"flag"
	"net/http"
	"net/url"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/weaveworks/scope/render"
	"github.com/weaveworks/scope/report"
	"github.com/weaveworks/scope/test/fixture"
)

var (
	benchReportFile = flag.String("bench-report-file", "", "json report file to use for benchmarking (relative to this package)")
)

func loadReport() (report.Report, error) {
	if *benchReportFile == "" {
		return fixture.Report, nil
	}

	c, err := NewFileCollector(*benchReportFile, 0)
	if err != nil {
		return fixture.Report, err
	}

	return c.Report(context.Background(), time.Now())
}

func BenchmarkTopologyList(b *testing.B) {
	benchmarkRender(b, func(report report.Report) {
		request := &http.Request{
			Form: url.Values{},
		}
		topologyRegistry.renderTopologies(report, request)
	})
}

func benchmarkRender(b *testing.B, f func(report.Report)) {
	report, err := loadReport()
	if err != nil {
		b.Fatal(err)
	}
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		render.ResetCache()
		b.StartTimer()
		f(report)
	}
}

func BenchmarkTopologyHosts(b *testing.B) {
	benchmarkOneTopology(b, "hosts")
}

func BenchmarkTopologyContainers(b *testing.B) {
	benchmarkOneTopology(b, "containers")
}

func benchmarkOneTopology(b *testing.B, topologyID string) {
	benchmarkRender(b, func(report report.Report) {
		renderer, filter, err := topologyRegistry.RendererForTopology(topologyID, url.Values{}, report)
		if err != nil {
			b.Fatal(err)
		}
		render.Render(report, renderer, filter)
	})
}
<commit_msg>simplify render benchmark code<commit_after>package app

import (
	"flag"
	"net/http"
	"net/url"
	"testing"

	"github.com/weaveworks/scope/render"
	"github.com/weaveworks/scope/report"
	"github.com/weaveworks/scope/test/fixture"
)

var (
	benchReportFile = flag.String("bench-report-file", "", "report file to use for benchmarking (relative to this package)")
)

func BenchmarkTopologyList(b *testing.B) {
	benchmarkRender(b, func(report report.Report) {
		request := &http.Request{
			Form: url.Values{},
		}
		topologyRegistry.renderTopologies(report, request)
	})
}

func benchmarkRender(b *testing.B, f func(report.Report)) {
	r := fixture.Report
	if *benchReportFile != "" {
		var err error
		if r, err = report.MakeFromFile(*benchReportFile); err != nil {
			b.Fatal(err)
		}
	}

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		b.StopTimer()
		render.ResetCache()
		b.StartTimer()
		f(r)
	}
}

func BenchmarkTopologyHosts(b *testing.B) {
	benchmarkOneTopology(b, "hosts")
}

func BenchmarkTopologyContainers(b *testing.B) {
	benchmarkOneTopology(b, "containers")
}

func benchmarkOneTopology(b *testing.B, topologyID string) {
	benchmarkRender(b, func(report report.Report) {
		renderer, filter, err := topologyRegistry.RendererForTopology(topologyID, url.Values{}, report)
		if err != nil {
			b.Fatal(err)
		}
		render.Render(report, renderer, filter)
	})
}
<|endoftext|>


<commit_before>package dos

import (
	"fmt"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"unicode"
	"unsafe"
)

var msvcrt = syscall.NewLazyDLL("msvcrt")
var _chdrive = msvcrt.NewProc("_chdrive")
var _wchdir = msvcrt.NewProc("_wchdir")

func chdrive_(n rune) uintptr {
	rc, _, _ := _chdrive.Call(uintptr(n & 0x1F))
	return rc
}

func getFirst(s string) (rune, error) {
	reader := strings.NewReader(s)
	drive, _, err := reader.ReadRune()
	if err != nil {
		return 0, err
	}
	return unicode.ToUpper(drive), nil
}

// Change drive without changing the working directory there.
func Chdrive(drive string) error {
	driveLetter, driveErr := getFirst(drive)
	if driveErr != nil {
		return driveErr
	}
	chdrive_(driveLetter)
	return nil
}

var rxPath = regexp.MustCompile("^([a-zA-Z]):(.*)$")

// Change the current working directory
// without changeing the working directory
// in the last drive.
func Chdir(folder_ string) error {
	folder := folder_
	if m := rxPath.FindStringSubmatch(folder_); m != nil {
		status := chdrive_(rune(m[1][0]))
		if status != 0 {
			return fmt.Errorf("%s: no such directory", folder_)
		}
		folder = m[2]
		if len(folder) <= 0 {
			return nil
		}
	}
	utf16, err := syscall.UTF16PtrFromString(folder)
	if err == nil {
		status, _, _ := _wchdir.Call(uintptr(unsafe.Pointer(utf16)))
		if status != 0 {
			err = fmt.Errorf("%s: no such directory", folder_)
		}
	}
	return err
}

var rxRoot = regexp.MustCompile("^([a-zA-Z]:)?[/\\\\]")
var rxDrive = regexp.MustCompile("^[a-zA-Z]:")

func joinPath2(a, b string) string {
	if len(a) <= 0 || rxRoot.MatchString(b) || rxDrive.MatchString(b) {
		return b
	}
	switch a[len(a)-1] {
	case '\\', '/', ':':
		return a + b
	default:
		return a + "\\" + b
	}
}

// Equals filepath.Join but this works right when path has drive-letter.
func Join(paths ...string) string {
	result := paths[len(paths)-1]
	for i := len(paths) - 2; i >= 0; i-- {
		result = joinPath2(paths[i], result)
	}
	return result
}

// Expand filenames matching with wildcard-pattern.
func Glob(pattern string) ([]string, error) {
	name := filepath.Base(pattern)
	if strings.IndexAny(name, "*?") < 0 {
		return nil, nil
	}
	findf, err := FindFirst(pattern)
	if err != nil {
		return nil, err
	}
	defer findf.Close()
	dirname := filepath.Dir(pattern)
	match := make([]string, 0, 100)
	for {
		if name := findf.Name(); name[0] != '.' || pattern[0] == '.' {
			match = append(match, filepath.Join(dirname, name))
		}
		err := findf.FindNext()
		if err != nil {
			return match, nil
		}
	}
}
<commit_msg>Fixed #75 dotsfiles were checked not basename but pathstring-top<commit_after>package dos

import (
	"fmt"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"unicode"
	"unsafe"
)

var msvcrt = syscall.NewLazyDLL("msvcrt")
var _chdrive = msvcrt.NewProc("_chdrive")
var _wchdir = msvcrt.NewProc("_wchdir")

func chdrive_(n rune) uintptr {
	rc, _, _ := _chdrive.Call(uintptr(n & 0x1F))
	return rc
}

func getFirst(s string) (rune, error) {
	reader := strings.NewReader(s)
	drive, _, err := reader.ReadRune()
	if err != nil {
		return 0, err
	}
	return unicode.ToUpper(drive), nil
}

// Change drive without changing the working directory there.
func Chdrive(drive string) error {
	driveLetter, driveErr := getFirst(drive)
	if driveErr != nil {
		return driveErr
	}
	chdrive_(driveLetter)
	return nil
}

var rxPath = regexp.MustCompile("^([a-zA-Z]):(.*)$")

// Change the current working directory
// without changeing the working directory
// in the last drive.
func Chdir(folder_ string) error {
	folder := folder_
	if m := rxPath.FindStringSubmatch(folder_); m != nil {
		status := chdrive_(rune(m[1][0]))
		if status != 0 {
			return fmt.Errorf("%s: no such directory", folder_)
		}
		folder = m[2]
		if len(folder) <= 0 {
			return nil
		}
	}
	utf16, err := syscall.UTF16PtrFromString(folder)
	if err == nil {
		status, _, _ := _wchdir.Call(uintptr(unsafe.Pointer(utf16)))
		if status != 0 {
			err = fmt.Errorf("%s: no such directory", folder_)
		}
	}
	return err
}

var rxRoot = regexp.MustCompile("^([a-zA-Z]:)?[/\\\\]")
var rxDrive = regexp.MustCompile("^[a-zA-Z]:")

func joinPath2(a, b string) string {
	if len(a) <= 0 || rxRoot.MatchString(b) || rxDrive.MatchString(b) {
		return b
	}
	switch a[len(a)-1] {
	case '\\', '/', ':':
		return a + b
	default:
		return a + "\\" + b
	}
}

// Equals filepath.Join but this works right when path has drive-letter.
func Join(paths ...string) string {
	result := paths[len(paths)-1]
	for i := len(paths) - 2; i >= 0; i-- {
		result = joinPath2(paths[i], result)
	}
	return result
}

// Expand filenames matching with wildcard-pattern.
func Glob(pattern string) ([]string, error) {
	pname := filepath.Base(pattern)
	if strings.IndexAny(pname, "*?") < 0 {
		return nil, nil
	}
	findf, err := FindFirst(pattern)
	if err != nil {
		return nil, err
	}
	defer findf.Close()
	dirname := filepath.Dir(pattern)
	match := make([]string, 0, 100)
	for {
		if name := findf.Name(); name[0] != '.' || pname[0] == '.' {
			match = append(match, filepath.Join(dirname, name))
		}
		err := findf.FindNext()
		if err != nil {
			return match, nil
		}
	}
}
<|endoftext|>


<commit_before>package blog

import (
	"fmt"
	"github.com/astaxie/beego/orm"
	"github.com/ulricqin/beego-blog/g"
	. "github.com/ulricqin/beego-blog/models"
	"time"
)

func OneById(id int64) *Blog {
	if id <= 0 {
		return nil
	}

	key := fmt.Sprintf("%d", id)
	val := g.BlogCacheGet(key)
	if val == nil {
		if p := OneByIdInDB(id); p != nil {
			g.BlogCachePut(key, *p)
			return p
		}
		return nil
	}
	ret := val.(Blog)
	return &ret
}

func OneByIdInDB(id int64) *Blog {
	if id <= 0 {
		return nil
	}

	o := Blog{Id: id}
	err := orm.NewOrm().Read(&o, "Id")
	if err != nil {
		return nil
	}
	return &o
}

func IdByIdent(ident string) int64 {
	if ident == "" {
		return 0
	}

	val := g.BlogCacheGet(ident)
	if val == nil {
		if p := OneByIdentInDB(ident); p != nil {
			g.BlogCachePut(ident, p.Id)
			return p.Id
		} else {
			return 0
		}
	}

	return val.(int64)
}

func IdentExists(ident string) bool {
	id := IdByIdent(ident)
	return id > 0
}

func OneByIdent(ident string) *Blog {
	id := IdByIdent(ident)
	return OneById(id)
}

func OneByIdentInDB(ident string) *Blog {
	if ident == "" {
		return nil
	}

	c := Blog{Ident: ident}
	err := orm.NewOrm().Read(&c, "Ident")
	if err != nil {
		return nil
	}

	return &c
}

func IdsInDB(catalog_id int64) []int64 {
	if catalog_id <= 0 {
		return []int64{}
	}

	var blogs []Blog
	Blogs().Filter("CatalogId", catalog_id).Filter("Status", 1).OrderBy("-Created").All(&blogs, "Id")
	size := len(blogs)
	if size == 0 {
		return []int64{}
	}

	ret := make([]int64, size)
	for i := 0; i < size; i++ {
		ret[i] = blogs[i].Id
	}

	return ret
}

func ReadBlogContent(b *Blog) *BlogContent {
	if b.Id <= 0 || b.BlogContentId <= 0 {
		return nil
	}

	key := fmt.Sprintf("content_of_%d_%d", b.Id, b.BlogContentLastUpdate)
	val := g.BlogCacheGet(key)
	if val == nil {
		if p := readBlogContentInDB(b); p != nil {
			g.BlogCachePut(key, *p)
			return p
		}
		return nil
	}
	ret := val.(BlogContent)
	return &ret
}

func readBlogContentInDB(b *Blog) *BlogContent {
	o := BlogContent{Id: b.BlogContentId}
	err := orm.NewOrm().Read(&o, "Id")
	if err != nil {
		return nil
	}
	return &o
}

func Ids(catalog_id int64) []int64 {
	if catalog_id <= 0 {
		return []int64{}
	}

	key := fmt.Sprintf("article_ids_of_%d", catalog_id)
	val := g.BlogCacheGet(key)
	if val == nil {
		if ids := IdsInDB(catalog_id); len(ids) != 0 {
			g.BlogCachePut(key, ids)
			return ids
		} else {
			return []int64{}
		}
	}

	return val.([]int64)
}

func ByCatalog(catalog_id int64, offset, limit int) []*Blog {
	ids := Ids(catalog_id)
	size := len(ids)
	if size == 0 {
		return []*Blog{}
	}

	if size > limit {
		ids = ids[offset:(offset + limit)]
	}

	size = len(ids)
	ret := make([]*Blog, size)
	for i := 0; i < size; i++ {
		ret[i] = OneById(ids[i])
		ret[i].Content = ReadBlogContent(ret[i])
	}
	return ret
}

func Save(this *Blog, blogContent string) (int64, error) {
	if IdentExists(this.Ident) {
		return 0, fmt.Errorf("blog english identity exists")
	}

	bc := &BlogContent{Content: blogContent}
	or := orm.NewOrm()
	blogContentId, e := or.Insert(bc)
	if e != nil {
		return 0, e
	}

	this.BlogContentId = blogContentId
	this.BlogContentLastUpdate = time.Now().Unix()

	id, err := or.Insert(this)
	if err == nil {
		g.BlogCacheDel(fmt.Sprintf("article_ids_of_%d", this.CatalogId))
	}

	return id, err
}

func Del(b *Blog) error {
	num, err := Blogs().Filter("Id", b.Id).Delete()
	if err != nil {
		return err
	}

	if num > 0 {
		g.BlogCacheDel(fmt.Sprintf("article_ids_of_%d", b.CatalogId))
		BlogContents().Filter("Id", b.BlogContentId).Delete()
	}

	return nil
}

func Update(b *Blog, content string) error {
	if b.Id == 0 {
		return fmt.Errorf("primary key:id not set")
	}

	bc := ReadBlogContent(b)
	if content != "" && bc.Content != content {
		bc.Content = content
		_, e := orm.NewOrm().Update(bc)
		if e != nil {
			return e
		}
		b.BlogContentLastUpdate = time.Now().Unix()
	}

	_, err := orm.NewOrm().Update(b)
	if err == nil {
		g.BlogCacheDel(fmt.Sprintf("%d", b.Id))
	}
	return err
}

func Blogs() orm.QuerySeter {
	return orm.NewOrm().QueryTable(new(Blog))
}

func BlogContents() orm.QuerySeter {
	return orm.NewOrm().QueryTable(new(BlogContent))
}
<commit_msg>fixbug: pager<commit_after>package blog

import (
	"fmt"
	"github.com/astaxie/beego/orm"
	"github.com/ulricqin/beego-blog/g"
	. "github.com/ulricqin/beego-blog/models"
	"time"
)

func OneById(id int64) *Blog {
	if id <= 0 {
		return nil
	}

	key := fmt.Sprintf("%d", id)
	val := g.BlogCacheGet(key)
	if val == nil {
		if p := OneByIdInDB(id); p != nil {
			g.BlogCachePut(key, *p)
			return p
		}
		return nil
	}
	ret := val.(Blog)
	return &ret
}

func OneByIdInDB(id int64) *Blog {
	if id <= 0 {
		return nil
	}

	o := Blog{Id: id}
	err := orm.NewOrm().Read(&o, "Id")
	if err != nil {
		return nil
	}
	return &o
}

func IdByIdent(ident string) int64 {
	if ident == "" {
		return 0
	}

	val := g.BlogCacheGet(ident)
	if val == nil {
		if p := OneByIdentInDB(ident); p != nil {
			g.BlogCachePut(ident, p.Id)
			return p.Id
		} else {
			return 0
		}
	}

	return val.(int64)
}

func IdentExists(ident string) bool {
	id := IdByIdent(ident)
	return id > 0
}

func OneByIdent(ident string) *Blog {
	id := IdByIdent(ident)
	return OneById(id)
}

func OneByIdentInDB(ident string) *Blog {
	if ident == "" {
		return nil
	}

	c := Blog{Ident: ident}
	err := orm.NewOrm().Read(&c, "Ident")
	if err != nil {
		return nil
	}

	return &c
}

func IdsInDB(catalog_id int64) []int64 {
	if catalog_id <= 0 {
		return []int64{}
	}

	var blogs []Blog
	Blogs().Filter("CatalogId", catalog_id).Filter("Status", 1).OrderBy("-Created").All(&blogs, "Id")
	size := len(blogs)
	if size == 0 {
		return []int64{}
	}

	ret := make([]int64, size)
	for i := 0; i < size; i++ {
		ret[i] = blogs[i].Id
	}

	return ret
}

func ReadBlogContent(b *Blog) *BlogContent {
	if b.Id <= 0 || b.BlogContentId <= 0 {
		return nil
	}

	key := fmt.Sprintf("content_of_%d_%d", b.Id, b.BlogContentLastUpdate)
	val := g.BlogCacheGet(key)
	if val == nil {
		if p := readBlogContentInDB(b); p != nil {
			g.BlogCachePut(key, *p)
			return p
		}
		return nil
	}
	ret := val.(BlogContent)
	return &ret
}

func readBlogContentInDB(b *Blog) *BlogContent {
	o := BlogContent{Id: b.BlogContentId}
	err := orm.NewOrm().Read(&o, "Id")
	if err != nil {
		return nil
	}
	return &o
}

func Ids(catalog_id int64) []int64 {
	if catalog_id <= 0 {
		return []int64{}
	}

	key := fmt.Sprintf("article_ids_of_%d", catalog_id)
	val := g.BlogCacheGet(key)
	if val == nil {
		if ids := IdsInDB(catalog_id); len(ids) != 0 {
			g.BlogCachePut(key, ids)
			return ids
		} else {
			return []int64{}
		}
	}

	return val.([]int64)
}

func ByCatalog(catalog_id int64, offset, limit int) []*Blog {
	ids := Ids(catalog_id)
	size := len(ids)
	if size == 0 {
		return []*Blog{}
	}

	if size > limit {
		end = offset + limit
		if end > size {
			end = size
		}

		ids = ids[offset:end]
	}

	size = len(ids)
	ret := make([]*Blog, size)
	for i := 0; i < size; i++ {
		ret[i] = OneById(ids[i])
		ret[i].Content = ReadBlogContent(ret[i])
	}
	return ret
}

func Save(this *Blog, blogContent string) (int64, error) {
	if IdentExists(this.Ident) {
		return 0, fmt.Errorf("blog english identity exists")
	}

	bc := &BlogContent{Content: blogContent}
	or := orm.NewOrm()
	blogContentId, e := or.Insert(bc)
	if e != nil {
		return 0, e
	}

	this.BlogContentId = blogContentId
	this.BlogContentLastUpdate = time.Now().Unix()

	id, err := or.Insert(this)
	if err == nil {
		g.BlogCacheDel(fmt.Sprintf("article_ids_of_%d", this.CatalogId))
	}

	return id, err
}

func Del(b *Blog) error {
	num, err := Blogs().Filter("Id", b.Id).Delete()
	if err != nil {
		return err
	}

	if num > 0 {
		g.BlogCacheDel(fmt.Sprintf("article_ids_of_%d", b.CatalogId))
		BlogContents().Filter("Id", b.BlogContentId).Delete()
	}

	return nil
}

func Update(b *Blog, content string) error {
	if b.Id == 0 {
		return fmt.Errorf("primary key:id not set")
	}

	bc := ReadBlogContent(b)
	if content != "" && bc.Content != content {
		bc.Content = content
		_, e := orm.NewOrm().Update(bc)
		if e != nil {
			return e
		}
		b.BlogContentLastUpdate = time.Now().Unix()
	}

	_, err := orm.NewOrm().Update(b)
	if err == nil {
		g.BlogCacheDel(fmt.Sprintf("%d", b.Id))
	}
	return err
}

func Blogs() orm.QuerySeter {
	return orm.NewOrm().QueryTable(new(Blog))
}

func BlogContents() orm.QuerySeter {
	return orm.NewOrm().QueryTable(new(BlogContent))
}
<|endoftext|>