<commit_before>package commands

import (
	"strings"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands/datamigrations"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
	"github.com/grafana/grafana/pkg/services/sqlstore"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/util/errutil"
	"github.com/urfave/cli/v2"
)

func runDbCommand(command func(commandLine utils.CommandLine, sqlStore *sqlstore.SQLStore) error) func(context *cli.Context) error {
	return func(context *cli.Context) error {
		cmd := &utils.ContextCommandLine{Context: context}
		debug := cmd.Bool("debug")

		cfg := setting.NewCfg()

		configOptions := strings.Split(cmd.String("configOverrides"), " ")
		if err := cfg.Load(&setting.CommandLineArgs{
			Config:   cmd.ConfigFile(),
			HomePath: cmd.HomePath(),
			Args:     append(configOptions, cmd.Args().Slice()...), // tailing arguments have precedence over the options string
		}); err != nil {
			return errutil.Wrap("failed to load configuration", err)
		}

		if debug {
			cfg.LogConfigSources()
		}

		engine := &sqlstore.SQLStore{}
		engine.Cfg = cfg
		engine.Bus = bus.GetBus()
		if err := engine.Init(); err != nil {
			return errutil.Wrap("failed to initialize SQL engine", err)
		}

		if err := command(cmd, engine); err != nil {
			return err
		}

		logger.Info("\n\n")
		return nil
	}
}

func runPluginCommand(command func(commandLine utils.CommandLine) error) func(context *cli.Context) error {
	return func(context *cli.Context) error {
		cmd := &utils.ContextCommandLine{Context: context}
		if err := command(cmd); err != nil {
			return err
		}

		logger.Info("\nRestart grafana after installing plugins . <service grafana-server restart>\n\n")
		return nil
	}
}

// Command contains command state.
type Command struct {
	Client utils.ApiClient
}

var cmd Command = Command{
	Client: &services.GrafanaComClient{},
}

var pluginCommands = []*cli.Command{
	{
		Name:   "install",
		Usage:  "install <plugin id> <plugin version (optional)>",
		Action: runPluginCommand(cmd.installCommand),
	}, {
		Name:   "list-remote",
		Usage:  "list remote available plugins",
		Action: runPluginCommand(cmd.listRemoteCommand),
	}, {
		Name:   "list-versions",
		Usage:  "list-versions <plugin id>",
		Action: runPluginCommand(cmd.listVersionsCommand),
	}, {
		Name:    "update",
		Usage:   "update <plugin id>",
		Aliases: []string{"upgrade"},
		Action:  runPluginCommand(cmd.upgradeCommand),
	}, {
		Name:    "update-all",
		Aliases: []string{"upgrade-all"},
		Usage:   "update all your installed plugins",
		Action:  runPluginCommand(cmd.upgradeAllCommand),
	}, {
		Name:   "ls",
		Usage:  "list all installed plugins",
		Action: runPluginCommand(cmd.lsCommand),
	}, {
		Name:    "uninstall",
		Aliases: []string{"remove"},
		Usage:   "uninstall <plugin id>",
		Action:  runPluginCommand(cmd.removeCommand),
	},
}

var adminCommands = []*cli.Command{
	{
		Name:   "reset-admin-password",
		Usage:  "reset-admin-password <new password>",
		Action: runDbCommand(resetPasswordCommand),
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:  "password-from-stdin",
				Usage: "Read the password from stdin",
				Value: false,
			},
		},
	},
	{
		Name:  "data-migration",
		Usage: "Runs a script that migrates or cleanups data in your db",
		Subcommands: []*cli.Command{
			{
				Name:   "encrypt-datasource-passwords",
				Usage:  "Migrates passwords from unsecured fields to secure_json_data field. Return ok unless there is an error. Safe to execute multiple times.",
				Action: runDbCommand(datamigrations.EncryptDatasourcePasswords),
			},
		},
	},
}

var Commands = []*cli.Command{
	{
		Name:        "plugins",
		Usage:       "Manage plugins for grafana",
		Subcommands: pluginCommands,
	},
	{
		Name:        "admin",
		Usage:       "Grafana admin commands",
		Subcommands: adminCommands,
	},
}
<commit_msg>Update logger message to be more generic (#32403)<commit_after>package commands

import (
	"strings"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands/datamigrations"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
	"github.com/grafana/grafana/pkg/services/sqlstore"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/util/errutil"
	"github.com/urfave/cli/v2"
)

func runDbCommand(command func(commandLine utils.CommandLine, sqlStore *sqlstore.SQLStore) error) func(context *cli.Context) error {
	return func(context *cli.Context) error {
		cmd := &utils.ContextCommandLine{Context: context}
		debug := cmd.Bool("debug")

		cfg := setting.NewCfg()

		configOptions := strings.Split(cmd.String("configOverrides"), " ")
		if err := cfg.Load(&setting.CommandLineArgs{
			Config:   cmd.ConfigFile(),
			HomePath: cmd.HomePath(),
			Args:     append(configOptions, cmd.Args().Slice()...), // tailing arguments have precedence over the options string
		}); err != nil {
			return errutil.Wrap("failed to load configuration", err)
		}

		if debug {
			cfg.LogConfigSources()
		}

		engine := &sqlstore.SQLStore{}
		engine.Cfg = cfg
		engine.Bus = bus.GetBus()
		if err := engine.Init(); err != nil {
			return errutil.Wrap("failed to initialize SQL engine", err)
		}

		if err := command(cmd, engine); err != nil {
			return err
		}

		logger.Info("\n\n")
		return nil
	}
}

func runPluginCommand(command func(commandLine utils.CommandLine) error) func(context *cli.Context) error {
	return func(context *cli.Context) error {
		cmd := &utils.ContextCommandLine{Context: context}
		if err := command(cmd); err != nil {
			return err
		}

		logger.Info("\nRestart Grafana after installing plugins. Refer to Grafana documentation for instructions if necessary.\n\n\n\n")
		return nil
	}
}

// Command contains command state.
type Command struct {
	Client utils.ApiClient
}

var cmd Command = Command{
	Client: &services.GrafanaComClient{},
}

var pluginCommands = []*cli.Command{
	{
		Name:   "install",
		Usage:  "install <plugin id> <plugin version (optional)>",
		Action: runPluginCommand(cmd.installCommand),
	}, {
		Name:   "list-remote",
		Usage:  "list remote available plugins",
		Action: runPluginCommand(cmd.listRemoteCommand),
	}, {
		Name:   "list-versions",
		Usage:  "list-versions <plugin id>",
		Action: runPluginCommand(cmd.listVersionsCommand),
	}, {
		Name:    "update",
		Usage:   "update <plugin id>",
		Aliases: []string{"upgrade"},
		Action:  runPluginCommand(cmd.upgradeCommand),
	}, {
		Name:    "update-all",
		Aliases: []string{"upgrade-all"},
		Usage:   "update all your installed plugins",
		Action:  runPluginCommand(cmd.upgradeAllCommand),
	}, {
		Name:   "ls",
		Usage:  "list all installed plugins",
		Action: runPluginCommand(cmd.lsCommand),
	}, {
		Name:    "uninstall",
		Aliases: []string{"remove"},
		Usage:   "uninstall <plugin id>",
		Action:  runPluginCommand(cmd.removeCommand),
	},
}

var adminCommands = []*cli.Command{
	{
		Name:   "reset-admin-password",
		Usage:  "reset-admin-password <new password>",
		Action: runDbCommand(resetPasswordCommand),
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:  "password-from-stdin",
				Usage: "Read the password from stdin",
				Value: false,
			},
		},
	},
	{
		Name:  "data-migration",
		Usage: "Runs a script that migrates or cleanups data in your db",
		Subcommands: []*cli.Command{
			{
				Name:   "encrypt-datasource-passwords",
				Usage:  "Migrates passwords from unsecured fields to secure_json_data field. Return ok unless there is an error. Safe to execute multiple times.",
				Action: runDbCommand(datamigrations.EncryptDatasourcePasswords),
			},
		},
	},
}

var Commands = []*cli.Command{
	{
		Name:        "plugins",
		Usage:       "Manage plugins for grafana",
		Subcommands: pluginCommands,
	},
	{
		Name:        "admin",
		Usage:       "Grafana admin commands",
		Subcommands: adminCommands,
	},
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package bootstrap

import (
	"time"

	"github.com/golang/glog"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/watch"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/cache"
	bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
	"k8s.io/kubernetes/pkg/util/metrics"
)

// TokenCleanerOptions contains options for the TokenCleaner
type TokenCleanerOptions struct {
	// TokenSecretNamespace string is the namespace for token Secrets.
	TokenSecretNamespace string

	// SecretResync is the time.Duration at which to fully re-list secrets.
	// If zero, re-list will be delayed as long as possible
	SecretResync time.Duration
}

// DefaultTokenCleanerOptions returns a set of default options for creating a
// TokenCleaner
func DefaultTokenCleanerOptions() TokenCleanerOptions {
	return TokenCleanerOptions{}
}

// TokenCleaner is a controller that deletes expired tokens
type TokenCleaner struct {
	tokenSecretNamespace string

	client clientset.Interface

	secrets           cache.Store
	secretsController cache.Controller
}

// NewTokenCleaner returns a new *NewTokenCleaner.
//
// TODO: Switch to shared informers
func NewTokenCleaner(cl clientset.Interface, options TokenCleanerOptions) *TokenCleaner {
	e := &TokenCleaner{
		client:               cl,
		tokenSecretNamespace: options.TokenSecretNamespace,
	}
	if cl.Core().RESTClient().GetRateLimiter() != nil {
		metrics.RegisterMetricAndTrackRateLimiterUsage("token_cleaner", cl.Core().RESTClient().GetRateLimiter())
	}

	secretSelector := fields.SelectorFromSet(map[string]string{api.SecretTypeField: string(bootstrapapi.SecretTypeBootstrapToken)})
	e.secrets, e.secretsController = cache.NewInformer(
		&cache.ListWatch{
			ListFunc: func(lo metav1.ListOptions) (runtime.Object, error) {
				lo.FieldSelector = secretSelector.String()
				return e.client.Core().Secrets(e.tokenSecretNamespace).List(lo)
			},
			WatchFunc: func(lo metav1.ListOptions) (watch.Interface, error) {
				lo.FieldSelector = secretSelector.String()
				return e.client.Core().Secrets(e.tokenSecretNamespace).Watch(lo)
			},
		},
		&v1.Secret{},
		options.SecretResync,
		cache.ResourceEventHandlerFuncs{
			AddFunc:    e.evalSecret,
			UpdateFunc: func(oldSecret, newSecret interface{}) { e.evalSecret(newSecret) },
		},
	)
	return e
}

// Run runs controller loops and returns when they are done
func (tc *TokenCleaner) Run(stopCh <-chan struct{}) {
	go tc.secretsController.Run(stopCh)
	<-stopCh
}

func (tc *TokenCleaner) evalSecret(o interface{}) {
	secret := o.(*v1.Secret)
	if isSecretExpired(secret) {
		glog.V(3).Infof("Deleting expired secret %s/%s", secret.Namespace, secret.Name)
		var options *metav1.DeleteOptions
		if len(secret.UID) > 0 {
			options = &metav1.DeleteOptions{Preconditions: &metav1.Preconditions{UID: &secret.UID}}
		}
		err := tc.client.Core().Secrets(secret.Namespace).Delete(secret.Name, options)
		// NotFound isn't a real error (it's already been deleted)
		// Conflict isn't a real error (the UID precondition failed)
		if err != nil && !apierrors.IsConflict(err) && !apierrors.IsNotFound(err) {
			glog.V(3).Infof("Error deleting Secret: %v", err)
		}
	}
}
<commit_msg>Small fix to the bootstrap TokenCleaner<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package bootstrap

import (
	"time"

	"github.com/golang/glog"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/cache"
	bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
	"k8s.io/kubernetes/pkg/util/metrics"
)

// TokenCleanerOptions contains options for the TokenCleaner
type TokenCleanerOptions struct {
	// TokenSecretNamespace string is the namespace for token Secrets.
	TokenSecretNamespace string

	// SecretResync is the time.Duration at which to fully re-list secrets.
	// If zero, re-list will be delayed as long as possible
	SecretResync time.Duration
}

// DefaultTokenCleanerOptions returns a set of default options for creating a
// TokenCleaner
func DefaultTokenCleanerOptions() TokenCleanerOptions {
	return TokenCleanerOptions{
		TokenSecretNamespace: api.NamespaceSystem,
	}
}

// TokenCleaner is a controller that deletes expired tokens
type TokenCleaner struct {
	tokenSecretNamespace string

	client clientset.Interface

	secrets           cache.Store
	secretsController cache.Controller
}

// NewTokenCleaner returns a new *NewTokenCleaner.
//
// TODO: Switch to shared informers
func NewTokenCleaner(cl clientset.Interface, options TokenCleanerOptions) *TokenCleaner {
	e := &TokenCleaner{
		client:               cl,
		tokenSecretNamespace: options.TokenSecretNamespace,
	}
	if cl.Core().RESTClient().GetRateLimiter() != nil {
		metrics.RegisterMetricAndTrackRateLimiterUsage("token_cleaner", cl.Core().RESTClient().GetRateLimiter())
	}

	secretSelector := fields.SelectorFromSet(map[string]string{api.SecretTypeField: string(bootstrapapi.SecretTypeBootstrapToken)})
	e.secrets, e.secretsController = cache.NewInformer(
		&cache.ListWatch{
			ListFunc: func(lo metav1.ListOptions) (runtime.Object, error) {
				lo.FieldSelector = secretSelector.String()
				return e.client.Core().Secrets(e.tokenSecretNamespace).List(lo)
			},
			WatchFunc: func(lo metav1.ListOptions) (watch.Interface, error) {
				lo.FieldSelector = secretSelector.String()
				return e.client.Core().Secrets(e.tokenSecretNamespace).Watch(lo)
			},
		},
		&v1.Secret{},
		options.SecretResync,
		cache.ResourceEventHandlerFuncs{
			AddFunc:    e.evalSecret,
			UpdateFunc: func(oldSecret, newSecret interface{}) { e.evalSecret(newSecret) },
		},
	)
	return e
}

// Run runs controller loops and returns when they are done
func (tc *TokenCleaner) Run(stopCh <-chan struct{}) {
	go tc.secretsController.Run(stopCh)
	go wait.Until(tc.evalSecrets, 10*time.Second, stopCh)
	<-stopCh
}

func (tc *TokenCleaner) evalSecrets() {
	for _, obj := range tc.secrets.List() {
		tc.evalSecret(obj)
	}
}

func (tc *TokenCleaner) evalSecret(o interface{}) {
	secret := o.(*v1.Secret)
	if isSecretExpired(secret) {
		glog.V(3).Infof("Deleting expired secret %s/%s", secret.Namespace, secret.Name)
		var options *metav1.DeleteOptions
		if len(secret.UID) > 0 {
			options = &metav1.DeleteOptions{Preconditions: &metav1.Preconditions{UID: &secret.UID}}
		}
		err := tc.client.Core().Secrets(secret.Namespace).Delete(secret.Name, options)
		// NotFound isn't a real error (it's already been deleted)
		// Conflict isn't a real error (the UID precondition failed)
		if err != nil && !apierrors.IsConflict(err) && !apierrors.IsNotFound(err) {
			glog.V(3).Infof("Error deleting Secret: %v", err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
    "net"
//    "golang.org/x/net/ipv4"
    "fmt"
)

type IP_Conn struct {
    pc        *net.IPConn
    version   uint8
    dst, src  string
    headerLen uint16
    //len uint16
    //id uint16
    ttl      uint8
    protocol uint8
    //checksum int
}

func NewIP_Conn(dst string) (*IP_Conn, error) {
    pc, err := net.ListenIP("ip4:17", &net.IPAddr{IP: net.ParseIP(dst)})
    if err != nil {
        fmt.Println("Failed to ListenIP")
        return nil, err
    }

    return &IP_Conn{
        pc:        pc,
        version:   4,
        headerLen: 20,
        dst:       dst,
        src:       "127.0.0.1",
        ttl:       8,
        protocol:  17,
    }, nil
}

func calcChecksum(head []byte, excludeChecksum bool) uint16 {
    totalSum := uint64(0)
    for ind, elem := range head {
        if (ind == 10 || ind == 11) && excludeChecksum { // Ignore the checksum in some situations
            continue
        }

        if ind%2 == 0 {
            totalSum += (uint64(elem) << 8)
        } else {
            totalSum += uint64(elem)
        }
    }
    fmt.Println(totalSum)

    for prefix := (totalSum >> 16); prefix != 0; prefix = (totalSum >> 16) {
//        fmt.Println(prefix)
//        fmt.Println(totalSum)
//        fmt.Println(totalSum & 0xffff)
        totalSum = uint64(totalSum & 0xffff) + prefix
    }
    fmt.Println(totalSum)

    carried := uint16(totalSum)

    return ^carried
}

func slicePacket(b []byte) (payload []byte) {
    hdrLen := int(b[0] & 0x0f) * 4
    fmt.Println("HdrLen: ", hdrLen)
    return b[hdrLen:]
}

func (ipc *IP_Conn) ReadFrom(b []byte) (payload []byte, e error) {
    n, _, err := ipc.pc.ReadFrom(b)
    b = b[:n]
    fmt.Println("Read Length: ", n)
    fmt.Println("Full Read Data (after trim): ", b)
    p := slicePacket(b)

    return p, err
}

func (ipc *IP_Conn) WriteTo(p []byte) error {
    totalLen := uint16(ipc.headerLen) + uint16(len(p))
    fmt.Println(totalLen)
    packet := make([]byte, ipc.headerLen)
    packet[0] = (byte)((ipc.version << 4) + (uint8)(ipc.headerLen / 4)) // Version, IHL
    packet[1] = 0
    packet[2] = (byte)(totalLen >> 8) // Total Len
    packet[3] = (byte)(totalLen)
    packet[4] = 0 // Identification (for now)
    packet[5] = 0
    packet[6] = byte(1 << 6)         // Flags: Don't fragment
    packet[7] = 0                    // Fragment Offset
    packet[8] = (byte)(ipc.ttl)      // Time to Live
    packet[9] = (byte)(ipc.protocol) // Protocol

    // Src and Dst IPs
    srcIP := net.ParseIP(ipc.src)
    fmt.Println(srcIP)
//    fmt.Println(srcIP[12])
//    fmt.Println(srcIP[13])
//    fmt.Println(srcIP[14])
//    fmt.Println(srcIP[15])
    dstIP := net.ParseIP(ipc.dst)
    fmt.Println(dstIP)
    packet[12] = srcIP[12]
    packet[13] = srcIP[13]
    packet[14] = srcIP[14]
    packet[15] = srcIP[15]
    packet[16] = dstIP[12]
    packet[17] = dstIP[13]
    packet[18] = dstIP[14]
    packet[19] = dstIP[15]

    // IPv4 header test (before checksum)
    fmt.Println(packet)

    // Checksum
    checksum := calcChecksum(packet[:20], true)
    packet[10] = byte(checksum >> 8)
    packet[11] = byte(checksum)

    // Payload
    packet = append(packet, p...)
    fmt.Println("Full Packet:  ", packet)

    dstIPAddr, err := net.ResolveIPAddr("ip", ipc.dst)
    if err != nil {
//        fmt.Println(err)
        return err
    }
    fmt.Println("Full Address: ", dstIPAddr)

    ipc.pc.WriteMsgIP(packet, nil, dstIPAddr)
    return err
}

func (ipc *IP_Conn) Close() error {
    return ipc.pc.Close()
}

/* h := &ipv4.Header{
	Version:  ipv4.Version,      // protocol version
	Len:      20,                // header length
	TOS:      0,                 // type-of-service (0 is everything normal)
	TotalLen: len(x) + 20,       // packet total length (octets)
	ID:       0,                 // identification
	Flags:    ipv4.DontFragment, // flags
	FragOff:  0,                 // fragment offset
	TTL:      8,                 // time-to-live (maximum lifespan in seconds)
	Protocol: 17,                // next protocol (17 is UDP)
	Checksum: 0,                 // checksum (apparently autocomputed)
	//Src:    net.IPv4(127, 0, 0, 1), // source address, apparently done automatically
	Dst: net.ParseIP(c.manager.ipAddress), // destination address
	//Options                         // options, extension headers
}
*/
<commit_msg>Added labels to printlns<commit_after>package main

import (
    "net"
//    "golang.org/x/net/ipv4"
    "fmt"
)

type IP_Conn struct {
    pc        *net.IPConn
    version   uint8
    dst, src  string
    headerLen uint16
    //len uint16
    //id uint16
    ttl      uint8
    protocol uint8
    //checksum int
}

func NewIP_Conn(dst string) (*IP_Conn, error) {
    pc, err := net.ListenIP("ip4:17", &net.IPAddr{IP: net.ParseIP(dst)})
    if err != nil {
        fmt.Println("Failed to ListenIP")
        return nil, err
    }

    return &IP_Conn{
        pc:        pc,
        version:   4,
        headerLen: 20,
        dst:       dst,
        src:       "127.0.0.1",
        ttl:       8,
        protocol:  17,
    }, nil
}

func calcChecksum(head []byte, excludeChecksum bool) uint16 {
    totalSum := uint64(0)
    for ind, elem := range head {
        if (ind == 10 || ind == 11) && excludeChecksum { // Ignore the checksum in some situations
            continue
        }

        if ind%2 == 0 {
            totalSum += (uint64(elem) << 8)
        } else {
            totalSum += uint64(elem)
        }
    }
    fmt.Println("Checksum total: ", totalSum)

    for prefix := (totalSum >> 16); prefix != 0; prefix = (totalSum >> 16) {
//        fmt.Println(prefix)
//        fmt.Println(totalSum)
//        fmt.Println(totalSum & 0xffff)
        totalSum = uint64(totalSum & 0xffff) + prefix
    }
    fmt.Println("Checksum after carry: ", totalSum)

    carried := uint16(totalSum)

    return ^carried
}

func slicePacket(b []byte) (payload []byte) {
    hdrLen := int(b[0] & 0x0f) * 4
    fmt.Println("HdrLen: ", hdrLen)
    return b[hdrLen:]
}

func (ipc *IP_Conn) ReadFrom(b []byte) (payload []byte, e error) {
    n, _, err := ipc.pc.ReadFrom(b)
    b = b[:n]
    fmt.Println("Read Length: ", n)
    fmt.Println("Full Read Data (after trim): ", b)
    p := slicePacket(b)

    return p, err
}

func (ipc *IP_Conn) WriteTo(p []byte) error {
    totalLen := uint16(ipc.headerLen) + uint16(len(p))
    fmt.Println("Total Len: ", totalLen)
    packet := make([]byte, ipc.headerLen)
    packet[0] = (byte)((ipc.version << 4) + (uint8)(ipc.headerLen / 4)) // Version, IHL
    packet[1] = 0
    packet[2] = (byte)(totalLen >> 8) // Total Len
    packet[3] = (byte)(totalLen)
    packet[4] = 0 // Identification (for now)
    packet[5] = 0
    packet[6] = byte(1 << 6)         // Flags: Don't fragment
    packet[7] = 0                    // Fragment Offset
    packet[8] = (byte)(ipc.ttl)      // Time to Live
    packet[9] = (byte)(ipc.protocol) // Protocol

    // Src and Dst IPs
    srcIP := net.ParseIP(ipc.src)
    fmt.Println(srcIP)
//    fmt.Println(srcIP[12])
//    fmt.Println(srcIP[13])
//    fmt.Println(srcIP[14])
//    fmt.Println(srcIP[15])
    dstIP := net.ParseIP(ipc.dst)
    fmt.Println(dstIP)
    packet[12] = srcIP[12]
    packet[13] = srcIP[13]
    packet[14] = srcIP[14]
    packet[15] = srcIP[15]
    packet[16] = dstIP[12]
    packet[17] = dstIP[13]
    packet[18] = dstIP[14]
    packet[19] = dstIP[15]

    // IPv4 header test (before checksum)
    fmt.Println("Packet before checksum: ", packet)

    // Checksum
    checksum := calcChecksum(packet[:20], true)
    packet[10] = byte(checksum >> 8)
    packet[11] = byte(checksum)

    // Payload
    packet = append(packet, p...)
    fmt.Println("Full Packet:  ", packet)

    dstIPAddr, err := net.ResolveIPAddr("ip", ipc.dst)
    if err != nil {
//        fmt.Println(err)
        return err
    }
    fmt.Println("Full Address: ", dstIPAddr)

    ipc.pc.WriteMsgIP(packet, nil, dstIPAddr)
    return err
}

func (ipc *IP_Conn) Close() error {
    return ipc.pc.Close()
}

/* h := &ipv4.Header{
	Version:  ipv4.Version,      // protocol version
	Len:      20,                // header length
	TOS:      0,                 // type-of-service (0 is everything normal)
	TotalLen: len(x) + 20,       // packet total length (octets)
	ID:       0,                 // identification
	Flags:    ipv4.DontFragment, // flags
	FragOff:  0,                 // fragment offset
	TTL:      8,                 // time-to-live (maximum lifespan in seconds)
	Protocol: 17,                // next protocol (17 is UDP)
	Checksum: 0,                 // checksum (apparently autocomputed)
	//Src:    net.IPv4(127, 0, 0, 1), // source address, apparently done automatically
	Dst: net.ParseIP(c.manager.ipAddress), // destination address
	//Options                         // options, extension headers
}
*/
<|endoftext|>


<commit_before>// +build windows darwin,cgo

package filesystem

import (
	"context"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/pkg/errors"
)

const (
	watchRootParameterPollingInterval = 1 * time.Second
	watchRestartWait                  = 1 * time.Second
)

// isParentOrSelf returns true if and only if parent is a parent path of child
// or equal to child. It is designed to be cheap at the (potential) cost of
// correctness, but it is only designed for internal usage with file
// notifications, so this is probably acceptable. It assumes UTF-8 encoding.
func isParentOrSelf(parent, child string) bool {
	parentLength := len(parent)
	childLength := len(child)
	if childLength < parentLength {
		return false
	} else if parent != child[:parentLength] {
		return false
	} else if childLength > parentLength {
		return os.IsPathSeparator(child[parentLength])
	}
	return true
}

func watchNative(context context.Context, root string, events chan struct{}, _ uint32) error {
	// Compute the watch root. If we're on macOS, this will be the root itself.
	// If we're on Windows, this will be the parent directory of the root.
	var watchRoot string
	if runtime.GOOS == "darwin" {
		watchRoot = root
	} else if runtime.GOOS == "windows" {
		watchRoot = filepath.Dir(root)
	} else {
		panic("unhandled platform case")
	}

	// Set up initial watch root parameters.
	var watchRootExists bool
	var watchRootMetadata os.FileInfo
	var forceRecreate bool

	// Set up our initial event paths channel.
	dummyEventPaths := make(chan string)
	eventPaths := dummyEventPaths

	// Create a placeholder for the watch.
	var watch *recursiveWatch

	// Create a timer that we can use to coalesce events. It will be created
	// running, so make sure to stop it and consume its first event, if any.
	coalescingTimer := time.NewTimer(watchNativeCoalescingWindow)
	if !coalescingTimer.Stop() {
		<-coalescingTimer.C
	}

	// Create a timer to watch for changes to the watch root device ID and/or
	// inode. Start it with a 0 duration so that the first check takes place
	// immediately. Subsequent checks will take place at the normal interval.
	watchRootCheckTimer := time.NewTimer(0)

	// Create a function to clean up after ourselves.
	defer func() {
		// Cancel any watch.
		if watch != nil {
			watch.stop()
		}

		// Cancel the coalescing timer.
		coalescingTimer.Stop()

		// Cancel the root check timer.
		watchRootCheckTimer.Stop()
	}()

	// Poll for cancellation, the next raw event, the coalescing timer, or the
	// root check timer.
	for {
		select {
		case <-context.Done():
			// Abort the watch.
			return errors.New("watch cancelled")
		case path, ok := <-eventPaths:
			// If the event channel has been closed, then something's gone wrong
			// with the watch (e.g. a buffer overflow in ReadDirectoryChangesW),
			// but it should be a recoverable error, so we need to recreate the
			// watch.
			if !ok {
				// Close out the watch and clear the event channel. We know that
				// the watch is non-nil here because it's set at the same time
				// as eventPaths, from which we just read (the dummy event
				// channel never closes).
				watch.stop()
				watch = nil
				eventPaths = dummyEventPaths

				// Mark forced recreation.
				forceRecreate = true

				// Trigger the root check timer to re-run after a short delay.
				// We could set it to run immediately, but if the error is due
				// to rapid disk events causing a read buffer overflow, then
				// we're better off just waiting until that's done, otherwise
				// we'll just burn CPU cycles recreating over and over again.
				if !watchRootCheckTimer.Stop() {
					<-watchRootCheckTimer.C
				}
				watchRootCheckTimer.Reset(watchRestartWait)

				// Continue polling.
				continue
			}

			// Handle the path appropriately.
			// NOTE: When using FSEvents, event paths are (a) relative to the
			// device root and (b) fully resolved in terms of symlinks. This
			// means that the isExecutabilityTestPath and
			// isDecompositionTestPath checks will still work but isParentOrSelf
			// will not. Fortunately isParentOrSelf isn't necessary when using
			// FSEvents since we watch the root itself.
			if isExecutabilityTestPath(path) || isDecompositionTestPath(path) {
				// Ignore any probe files created by Mutagen.
				continue
			} else if runtime.GOOS == "windows" && !isParentOrSelf(root, path) {
				// If we're on Windows, then we're monitoring the parent
				// directory of the synchronization root, so if the notification
				// is for a path outside that root, ignore it.
				continue
			} else {
				// Otherwise we're looking at a relevant event, so reset the
				// coalescing timer. Perform a non-blocking drain since we don't
				// know if the timer was running or not.
				if !coalescingTimer.Stop() {
					select {
					case <-coalescingTimer.C:
					default:
					}
				}
				coalescingTimer.Reset(watchNativeCoalescingWindow)
			}
		case <-coalescingTimer.C:
			// Forward a coalesced event in a non-blocking fashion.
			select {
			case events <- struct{}{}:
			default:
			}
		case <-watchRootCheckTimer.C:
			// Grab current watch root parameters.
			var watchRootCurrentlyExists bool
			var currentWatchRootMetadata os.FileInfo
			if m, err := os.Lstat(watchRoot); err != nil {
				if !os.IsNotExist(err) {
					return errors.Wrap(err, "unable to probe root metadata")
				}
			} else {
				watchRootCurrentlyExists = true
				currentWatchRootMetadata = m
			}

			// Check if we need to recreate the watcher.
			recreate := forceRecreate ||
				watchRootCurrentlyExists != watchRootExists ||
				!watchRootParametersEqual(currentWatchRootMetadata, watchRootMetadata)

			// Unmark forced recreation.
			forceRecreate = false

			// Recreate the watcher if necessary.
			if recreate {
				// Close out any existing watcher and reset the event paths
				// channel.
				if watch != nil {
					watch.stop()
					watch = nil
					eventPaths = dummyEventPaths
				}

				// If the watch root exists, then attempt to start watching. If
				// the restart attempt fails, then just mark a forced
				// recreation and try again after a delay.
				if watchRootCurrentlyExists {
					if w, err := newRecursiveWatch(watchRoot, currentWatchRootMetadata); err != nil {
						forceRecreate = true
						watchRootCheckTimer.Reset(watchRestartWait)
						continue
					} else {
						watch = w
						eventPaths = w.eventPaths
					}
				}

				// Since the root changed (or we're in a forced recreate,
				// probably due to or resulting in missed events), we'll also
				// want to send an event.
				select {
				case events <- struct{}{}:
				default:
				}
			}

			// Update parameters.
			watchRootExists = watchRootCurrentlyExists
			watchRootMetadata = currentWatchRootMetadata

			// Reset the timer and continue polling.
			watchRootCheckTimer.Reset(watchRootParameterPollingInterval)
		}
	}
}
<commit_msg>Modified FSEvents watcher to avoid watch recreation if unnecessary.<commit_after>// +build windows darwin,cgo

package filesystem

import (
	"context"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/pkg/errors"
)

const (
	watchRootParameterPollingInterval = 1 * time.Second
	watchRestartWait                  = 1 * time.Second
)

// isParentOrSelf returns true if and only if parent is a parent path of child
// or equal to child. It is designed to be cheap at the (potential) cost of
// correctness, but it is only designed for internal usage with file
// notifications, so this is probably acceptable. It assumes UTF-8 encoding.
func isParentOrSelf(parent, child string) bool {
	parentLength := len(parent)
	childLength := len(child)
	if childLength < parentLength {
		return false
	} else if parent != child[:parentLength] {
		return false
	} else if childLength > parentLength {
		return os.IsPathSeparator(child[parentLength])
	}
	return true
}

func watchNative(context context.Context, root string, events chan struct{}, _ uint32) error {
	// Compute the watch root. If we're on macOS, this will be the root itself.
	// If we're on Windows, this will be the parent directory of the root.
	var watchRoot string
	if runtime.GOOS == "darwin" {
		watchRoot = root
	} else if runtime.GOOS == "windows" {
		watchRoot = filepath.Dir(root)
	} else {
		panic("unhandled platform case")
	}

	// Set up initial watch root parameters.
	var watchRootExists bool
	var watchRootMetadata os.FileInfo
	var forceRecreate bool

	// Set up our initial event paths channel.
	dummyEventPaths := make(chan string)
	eventPaths := dummyEventPaths

	// Create a placeholder for the watch.
	var watch *recursiveWatch

	// Create a timer that we can use to coalesce events. It will be created
	// running, so make sure to stop it and consume its first event, if any.
	coalescingTimer := time.NewTimer(watchNativeCoalescingWindow)
	if !coalescingTimer.Stop() {
		<-coalescingTimer.C
	}

	// Create a timer to watch for changes to the watch root device ID and/or
	// inode. Start it with a 0 duration so that the first check takes place
	// immediately. Subsequent checks will take place at the normal interval.
	watchRootCheckTimer := time.NewTimer(0)

	// Create a function to clean up after ourselves.
	defer func() {
		// Cancel any watch.
		if watch != nil {
			watch.stop()
		}

		// Cancel the coalescing timer.
		coalescingTimer.Stop()

		// Cancel the root check timer.
		watchRootCheckTimer.Stop()
	}()

	// Poll for cancellation, the next raw event, the coalescing timer, or the
	// root check timer.
	for {
		select {
		case <-context.Done():
			// Abort the watch.
			return errors.New("watch cancelled")
		case path, ok := <-eventPaths:
			// If the event channel has been closed, then something's gone wrong
			// with the watch (e.g. a buffer overflow in ReadDirectoryChangesW),
			// but it should be a recoverable error, so we need to recreate the
			// watch.
			if !ok {
				// Close out the watch and clear the event channel. We know that
				// the watch is non-nil here because it's set at the same time
				// as eventPaths, from which we just read (the dummy event
				// channel never closes).
				watch.stop()
				watch = nil
				eventPaths = dummyEventPaths

				// Mark forced recreation.
				forceRecreate = true

				// Trigger the root check timer to re-run after a short delay.
				// We could set it to run immediately, but if the error is due
				// to rapid disk events causing a read buffer overflow, then
				// we're better off just waiting until that's done, otherwise
				// we'll just burn CPU cycles recreating over and over again.
				if !watchRootCheckTimer.Stop() {
					<-watchRootCheckTimer.C
				}
				watchRootCheckTimer.Reset(watchRestartWait)

				// Continue polling.
				continue
			}

			// Handle the path appropriately.
			// NOTE: When using FSEvents, event paths are (a) relative to the
			// device root and (b) fully resolved in terms of symlinks. This
			// means that the isExecutabilityTestPath and
			// isDecompositionTestPath checks will still work but isParentOrSelf
			// will not. Fortunately isParentOrSelf isn't necessary when using
			// FSEvents since we watch the root itself.
			if isExecutabilityTestPath(path) || isDecompositionTestPath(path) {
				// Ignore any probe files created by Mutagen.
				continue
			} else if runtime.GOOS == "windows" && !isParentOrSelf(root, path) {
				// If we're on Windows, then we're monitoring the parent
				// directory of the synchronization root, so if the notification
				// is for a path outside that root, ignore it.
				continue
			} else {
				// Otherwise we're looking at a relevant event, so reset the
				// coalescing timer. Perform a non-blocking drain since we don't
				// know if the timer was running or not.
				if !coalescingTimer.Stop() {
					select {
					case <-coalescingTimer.C:
					default:
					}
				}
				coalescingTimer.Reset(watchNativeCoalescingWindow)
			}
		case <-coalescingTimer.C:
			// Forward a coalesced event in a non-blocking fashion.
			select {
			case events <- struct{}{}:
			default:
			}
		case <-watchRootCheckTimer.C:
			// Grab current watch root parameters.
			var watchRootCurrentlyExists bool
			var currentWatchRootMetadata os.FileInfo
			if m, err := os.Lstat(watchRoot); err != nil {
				if !os.IsNotExist(err) {
					return errors.Wrap(err, "unable to probe root metadata")
				}
			} else {
				watchRootCurrentlyExists = true
				currentWatchRootMetadata = m
			}

			// Check if we need to recreate the watcher.
			recreate := forceRecreate ||
				watchRootCurrentlyExists != watchRootExists ||
				!watchRootParametersEqual(currentWatchRootMetadata, watchRootMetadata)

			// HACK: On macOS, it's not necessary to recreate a watcher for a
			// file root, even if the file itself has changed.
			if recreate && runtime.GOOS == "darwin" {
				overrideRecreate := !forceRecreate &&
					watchRootExists && watchRootCurrentlyExists &&
					currentWatchRootMetadata.Mode() == watchRootMetadata.Mode() &&
					currentWatchRootMetadata.Mode()&os.ModeType == 0
				if overrideRecreate {
					recreate = false
				}
			}

			// Unmark forced recreation.
			forceRecreate = false

			// Recreate the watcher if necessary.
			if recreate {
				// Close out any existing watcher and reset the event paths
				// channel.
				if watch != nil {
					watch.stop()
					watch = nil
					eventPaths = dummyEventPaths
				}

				// If the watch root exists, then attempt to start watching. If
				// the restart attempt fails, then just mark a forced
				// recreation and try again after a delay.
				if watchRootCurrentlyExists {
					if w, err := newRecursiveWatch(watchRoot, currentWatchRootMetadata); err != nil {
						forceRecreate = true
						watchRootCheckTimer.Reset(watchRestartWait)
						continue
					} else {
						watch = w
						eventPaths = w.eventPaths
					}
				}

				// Since the root changed (or we're in a forced recreate,
				// probably due to or resulting in missed events), we'll also
				// want to send an event.
				select {
				case events <- struct{}{}:
				default:
				}
			}

			// Update parameters.
			watchRootExists = watchRootCurrentlyExists
			watchRootMetadata = currentWatchRootMetadata

			// Reset the timer and continue polling.
			watchRootCheckTimer.Reset(watchRootParameterPollingInterval)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 The Vanadium 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 templates

import "html/template"

var SelectCaveats = template.Must(selectCaveats.Parse(headPartial))

var selectCaveats = template.Must(template.New("bless").Parse(`<!doctype html>

  {{template "head" .}}

  <title>Blessings: Select Caveats</title>

    Vanadium

    Select Caveats

    {{.Extension}}

  <form method="POST" id="caveats-form" name="input" action="{{.MacaroonURL}}" role="form">

{{.Extension}}

  <input type="text" class="hidden" name="macaroon" value="{{.Macaroon}}">

      Extension
      <input name="blessingExtension" type="text" id="blessing-extension" placeholder="(optional) name of the device/application for which the blessing is being sought, e.g. homelaptop">
      <input type="text" class="hidden" id="timezoneOffset" name="timezoneOffset">

    Expiration

        <input type="radio" name="requiredCaveat" id="requiredCaveat" value="Revocation" checked>
        When explicitly revoked

          <input type="radio" name="requiredCaveat" id="requiredCaveat" value="Expiry">
          <input type="datetime-local" id="expiry" name="expiry">

Additional caveats

  Optional additional restrictions on the use of the blessing

      <select name="caveat" class="caveats">
        <option value="none" selected="selected">Select a caveat.</option>
        {{ $caveatList := .CaveatList }}
        {{range $index, $name := $caveatList}}
          {{if eq $name "ExpiryCaveat"}}
          <option name="{{$name}}" value="{{$name}}">Expires</option>
          {{else if eq $name "MethodCaveat"}}
          <option name="{{$name}}" value="{{$name}}">Allowed Methods</option>
          {{else if eq $name "PeerBlessingsCaveat"}}
          <option name="{{$name}}" value="{{$name}}">Allowed Peers</option>
          {{else}}
          <option name="{{$name}}" value="{{$name}}">{{$name}}</option>
          {{end}}
        {{end}}
      </select>

      {{range $index, $name := $caveatList}}
        {{if eq $name "ExpiryCaveat"}}
        <input type="datetime-local" class="caveatInput" id="{{$name}}" name="{{$name}}">
        {{else if eq $name "MethodCaveat"}}
        <input type="text" id="{{$name}}" class="caveatInput" name="{{$name}}" placeholder="comma-separated method list">
        {{else if eq $name "PeerBlessingsCaveat"}}
        <input type="text" id="{{$name}}" class="form-control caveatInput" name="{{$name}}" placeholder="comma-separated blessing-pattern list">
        {{end}}
      {{end}}
      <button type="button" class="button-passive right addCaveat">Add Caveat</button>

  The blessing name will be visible to any peers that this blessing is shared
with. Thus, if your email address is in the blessing name, it will be visible
to peers you share the blessing with.

  By clicking "Bless", you consent to be bound by Google's general Terms of Service
  and Google's general Privacy Policy.

    <button class="cell button-passive" type="submit">Bless</button>
    <button class="cell button-passive" id="cancel">Cancel</button>

  </form>

`))
<commit_msg>identityd: Fix minute display in datepicker.<commit_after>// Copyright 2015 The Vanadium 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 templates

import "html/template"

var SelectCaveats = template.Must(selectCaveats.Parse(headPartial))

var selectCaveats = template.Must(template.New("bless").Parse(`<!doctype html>

  {{template "head" .}}

  <title>Blessings: Select Caveats</title>

    Vanadium

    Select Caveats

    {{.Extension}}

  <form method="POST" id="caveats-form" name="input" action="{{.MacaroonURL}}" role="form">

{{.Extension}}

  <input type="text" class="hidden" name="macaroon" value="{{.Macaroon}}">

      Extension
      <input name="blessingExtension" type="text" id="blessing-extension" placeholder="(optional) name of the device/application for which the blessing is being sought, e.g. homelaptop">
      <input type="text" class="hidden" id="timezoneOffset" name="timezoneOffset">

    Expiration

        <input type="radio" name="requiredCaveat" id="requiredCaveat" value="Revocation" checked>
        When explicitly revoked

          <input type="radio" name="requiredCaveat" id="requiredCaveat" value="Expiry">
          <input type="datetime-local" id="expiry" name="expiry">

Additional caveats

  Optional additional restrictions on the use of the blessing

      <select name="caveat" class="caveats">
        <option value="none" selected="selected">Select a caveat.</option>
        {{ $caveatList := .CaveatList }}
        {{range $index, $name := $caveatList}}
          {{if eq $name "ExpiryCaveat"}}
          <option name="{{$name}}" value="{{$name}}">Expires</option>
          {{else if eq $name "MethodCaveat"}}
          <option name="{{$name}}" value="{{$name}}">Allowed Methods</option>
          {{else if eq $name "PeerBlessingsCaveat"}}
          <option name="{{$name}}" value="{{$name}}">Allowed Peers</option>
          {{else}}
          <option name="{{$name}}" value="{{$name}}">{{$name}}</option>
          {{end}}
        {{end}}
      </select>

      {{range $index, $name := $caveatList}}
        {{if eq $name "ExpiryCaveat"}}
        <input type="datetime-local" class="caveatInput" id="{{$name}}" name="{{$name}}">
        {{else if eq $name "MethodCaveat"}}
        <input type="text" id="{{$name}}" class="caveatInput" name="{{$name}}" placeholder="comma-separated method list">
        {{else if eq $name "PeerBlessingsCaveat"}}
        <input type="text" id="{{$name}}" class="form-control caveatInput" name="{{$name}}" placeholder="comma-separated blessing-pattern list">
        {{end}}
      {{end}}
      <button type="button" class="button-passive right addCaveat">Add Caveat</button>

  The blessing name will be visible to any peers that this blessing is shared
with. Thus, if your email address is in the blessing name, it will be visible
to peers you share the blessing with.

  By clicking "Bless", you consent to be bound by Google's general Terms of Service
  and Google's general Privacy Policy.

    <button class="cell button-passive" type="submit">Bless</button>
    <button class="cell button-passive" id="cancel">Cancel</button>

  </form>

`))
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package schema

import (
	"testing"
)

func TestGroupVersionParse(t *testing.T) {
	tests := []struct {
		input string
		out   GroupVersion
		err   func(error) bool
	}{
		{input: "v1", out: GroupVersion{Version: "v1"}},
		{input: "v2", out: GroupVersion{Version: "v2"}},
		{input: "/v1", out: GroupVersion{Version: "v1"}},
		{input: "v1/", out: GroupVersion{Group: "v1"}},
		{input: "/v1/", err: func(err error) bool { return err.Error() == "unexpected GroupVersion string: /v1/" }},
		{input: "v1/a", out: GroupVersion{Group: "v1", Version: "a"}},
	}
	for i, test := range tests {
		out, err := ParseGroupVersion(test.input)
		if test.err == nil && err != nil || err == nil && test.err != nil {
			t.Errorf("%d: unexpected error: %v", i, err)
			continue
		}
		if test.err != nil && !test.err(err) {
			t.Errorf("%d: unexpected error: %v", i, err)
			continue
		}
		if out != test.out {
			t.Errorf("%d: unexpected output: %#v", i, out)
		}
	}
}

func TestGroupResourceParse(t *testing.T) {
	tests := []struct {
		input string
		out   GroupResource
	}{
		{input: "v1", out: GroupResource{Resource: "v1"}},
		{input: ".v1", out: GroupResource{Group: "v1"}},
		{input: "v1.", out: GroupResource{Resource: "v1"}},
		{input: "v1.a", out: GroupResource{Group: "a", Resource: "v1"}},
		{input: "b.v1.a", out: GroupResource{Group: "v1.a", Resource: "b"}},
	}
	for i, test := range tests {
		out := ParseGroupResource(test.input)
		if out != test.out {
			t.Errorf("%d: unexpected output: %#v", i, out)
		}
	}
}

func TestParseResourceArg(t *testing.T) {
	tests := []struct {
		input string
		gvr   *GroupVersionResource
		gr    GroupResource
	}{
		{input: "v1", gr: GroupResource{Resource: "v1"}},
		{input: ".v1", gr: GroupResource{Group: "v1"}},
		{input: "v1.", gr: GroupResource{Resource: "v1"}},
		{input: "v1.a", gr: GroupResource{Group: "a", Resource: "v1"}},
		{input: "b.v1.a", gvr: &GroupVersionResource{Group: "a", Version: "v1", Resource: "b"}, gr: GroupResource{Group: "v1.a", Resource: "b"}},
	}
	for i, test := range tests {
		gvr, gr := ParseResourceArg(test.input)
		if (gvr != nil && test.gvr == nil) || (gvr == nil && test.gvr != nil) || (test.gvr != nil && *gvr != *test.gvr) {
			t.Errorf("%d: unexpected output: %#v", i, gvr)
		}
		if gr != test.gr {
			t.Errorf("%d: unexpected output: %#v", i, gr)
		}
	}
}

func TestKindForGroupVersionKinds(t *testing.T) {
	gvks := GroupVersions{
		GroupVersion{Group: "batch", Version: "v1"},
		GroupVersion{Group: "batch", Version: "v2alpha1"},
		GroupVersion{Group: "policy", Version: "v1beta1"},
	}
	cases := []struct {
		input  []GroupVersionKind
		target GroupVersionKind
		ok     bool
	}{
		{
			input:  []GroupVersionKind{{Group: "batch", Version: "v2alpha1", Kind: "ScheduledJob"}},
			target: GroupVersionKind{Group: "batch", Version: "v2alpha1", Kind: "ScheduledJob"},
			ok:     true,
		},
		{
			input:  []GroupVersionKind{{Group: "batch", Version: "v3alpha1", Kind: "CronJob"}},
			target: GroupVersionKind{Group: "batch", Version: "v1", Kind: "CronJob"},
			ok:     true,
		},
		{
			input:  []GroupVersionKind{{Group: "policy", Version: "v1beta1", Kind: "PodDisruptionBudget"}},
			target: GroupVersionKind{Group: "policy", Version: "v1beta1", Kind: "PodDisruptionBudget"},
			ok:     true,
		},
		{
			input:  []GroupVersionKind{{Group: "apps", Version: "v1alpha1", Kind: "StatefulSet"}},
			target: GroupVersionKind{},
			ok:     false,
		},
	}

	for i, c := range cases {
		target, ok := gvks.KindForGroupVersionKinds(c.input)
		if c.target != target {
			t.Errorf("%d: unexpected target: %v, expected %v", i, target, c.target)
		}
		if c.ok != ok {
			t.Errorf("%d: unexpected ok: %v, expected %v", i, ok, c.ok)
		}
	}
}

func TestParseKindArg(t *testing.T) {
	tests := []struct {
		input string
		gvk   *GroupVersionKind
		gk    GroupKind
	}{
		{input: "Pod", gk: GroupKind{Kind: "Pod"}},
		{input: ".apps", gk: GroupKind{Group: "apps"}},
		{input: "Pod.", gk: GroupKind{Kind: "Pod"}},
		{input: "StatefulSet.apps", gk: GroupKind{Group: "apps", Kind: "StatefulSet"}},
		{input: "StatefulSet.v1.apps", gvk: &GroupVersionKind{Group: "apps", Version: "v1", Kind: "StatefulSet"}, gk: GroupKind{Group: "v1.apps", Kind: "StatefulSet"}},
	}
	for i, test := range tests {
		t.Run(test.input, func(t *testing.T) {
			gvk, gk := ParseKindArg(test.input)
			if (gvk != nil && test.gvk == nil) || (gvk == nil && test.gvk != nil) || (test.gvk != nil && *gvk != *test.gvk) {
				t.Errorf("%d: expected output: %#v, got: %#v", i, test.gvk, gvk)
			}
			if gk != test.gk {
				t.Errorf("%d: expected output: %#v, got: %#v", i, test.gk, gk)
			}
		})
	}
}

func TestParseGroupKind(t *testing.T) {
	tests := []struct {
		input string
		out   GroupKind
	}{
		{input: "Pod", out: GroupKind{Kind: "Pod"}},
		{input: ".StatefulSet", out: GroupKind{Group: "StatefulSet"}},
		{input: "StatefulSet.apps", out: GroupKind{Group: "apps", Kind: "StatefulSet"}},
	}
	for i, test := range tests {
		t.Run(test.input, func(t *testing.T) {
			out := ParseGroupKind(test.input)
			if out != test.out {
				t.Errorf("%d: expected output: %#v, got: %#v", i, test.out, out)
			}
		})
	}
}
<commit_msg>add some uts of group_version.go<commit_after>/*
Copyright 2015 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package schema

import (
	"testing"
)

func TestGroupVersionParse(t *testing.T) {
	tests := []struct {
		input string
		out   GroupVersion
		err   func(error) bool
	}{
		{input: "v1", out: GroupVersion{Version: "v1"}},
		{input: "v2", out: GroupVersion{Version: "v2"}},
		{input: "/v1", out: GroupVersion{Version: "v1"}},
		{input: "v1/", out: GroupVersion{Group: "v1"}},
		{input: "/v1/", err: func(err error) bool { return err.Error() == "unexpected GroupVersion string: /v1/" }},
		{input: "v1/a", out: GroupVersion{Group: "v1", Version: "a"}},
	}
	for i, test := range tests {
		out, err := ParseGroupVersion(test.input)
		if test.err == nil && err != nil || err == nil && test.err != nil {
			t.Errorf("%d: unexpected error: %v", i, err)
			continue
		}
		if test.err != nil && !test.err(err) {
			t.Errorf("%d: unexpected error: %v", i, err)
			continue
		}
		if out != test.out {
			t.Errorf("%d: unexpected output: %#v", i, out)
		}
	}
}

func TestGroupResourceParse(t *testing.T) {
	tests := []struct {
		input string
		out   GroupResource
	}{
		{input: "v1", out: GroupResource{Resource: "v1"}},
		{input: ".v1", out: GroupResource{Group: "v1"}},
		{input: "v1.", out: GroupResource{Resource: "v1"}},
		{input: "v1.a", out: GroupResource{Group: "a", Resource: "v1"}},
		{input: "b.v1.a", out: GroupResource{Group: "v1.a", Resource: "b"}},
	}
	for i, test := range tests {
		out := ParseGroupResource(test.input)
		if out != test.out {
			t.Errorf("%d: unexpected output: %#v", i, out)
		}
	}
}

func TestParseResourceArg(t *testing.T) {
	tests := []struct {
		input string
		gvr   *GroupVersionResource
		gr    GroupResource
	}{
		{input: "v1", gr: GroupResource{Resource: "v1"}},
		{input: ".v1", gr: GroupResource{Group: "v1"}},
		{input: "v1.", gr: GroupResource{Resource: "v1"}},
		{input: "v1.a", gr: GroupResource{Group: "a", Resource: "v1"}},
		{input: "b.v1.a", gvr: &GroupVersionResource{Group: "a", Version: "v1", Resource: "b"}, gr: GroupResource{Group: "v1.a", Resource: "b"}},
	}
	for i, test := range tests {
		gvr, gr := ParseResourceArg(test.input)
		if (gvr != nil && test.gvr == nil) || (gvr == nil && test.gvr != nil) || (test.gvr != nil && *gvr != *test.gvr) {
			t.Errorf("%d: unexpected output: %#v", i, gvr)
		}
		if gr != test.gr {
			t.Errorf("%d: unexpected output: %#v", i, gr)
		}
	}
}

func TestKindForGroupVersionKinds(t *testing.T) {
	gvks := GroupVersions{
		GroupVersion{Group: "batch", Version: "v1"},
		GroupVersion{Group: "batch", Version: "v2alpha1"},
		GroupVersion{Group: "policy", Version: "v1beta1"},
	}
	cases := []struct {
		input  []GroupVersionKind
		target GroupVersionKind
		ok     bool
	}{
		{
			input:  []GroupVersionKind{{Group: "batch", Version: "v2alpha1", Kind: "ScheduledJob"}},
			target: GroupVersionKind{Group: "batch", Version: "v2alpha1", Kind: "ScheduledJob"},
			ok:     true,
		},
		{
			input:  []GroupVersionKind{{Group: "batch", Version: "v3alpha1", Kind: "CronJob"}},
			target: GroupVersionKind{Group: "batch", Version: "v1", Kind: "CronJob"},
			ok:     true,
		},
		{
			input:  []GroupVersionKind{{Group: "policy", Version: "v1beta1", Kind: "PodDisruptionBudget"}},
			target: GroupVersionKind{Group: "policy", Version: "v1beta1", Kind: "PodDisruptionBudget"},
			ok:     true,
		},
		{
			input:  []GroupVersionKind{{Group: "apps", Version: "v1alpha1", Kind: "StatefulSet"}},
			target: GroupVersionKind{},
			ok:     false,
		},
	}

	for i, c := range cases {
		target, ok := gvks.KindForGroupVersionKinds(c.input)
		if c.target != target {
			t.Errorf("%d: unexpected target: %v, expected %v", i, target, c.target)
		}
		if c.ok != ok {
			t.Errorf("%d: unexpected ok: %v, expected %v", i, ok, c.ok)
		}
	}
}

func TestParseKindArg(t *testing.T) {
	tests := []struct {
		input string
		gvk   *GroupVersionKind
		gk    GroupKind
	}{
		{input: "Pod", gk: GroupKind{Kind: "Pod"}},
		{input: ".apps", gk: GroupKind{Group: "apps"}},
		{input: "Pod.", gk: GroupKind{Kind: "Pod"}},
		{input: "StatefulSet.apps", gk: GroupKind{Group: "apps", Kind: "StatefulSet"}},
		{input: "StatefulSet.v1.apps", gvk: &GroupVersionKind{Group: "apps", Version: "v1", Kind: "StatefulSet"}, gk: GroupKind{Group: "v1.apps", Kind: "StatefulSet"}},
	}
	for i, test := range tests {
		t.Run(test.input, func(t *testing.T) {
			gvk, gk := ParseKindArg(test.input)
			if (gvk != nil && test.gvk == nil) || (gvk == nil && test.gvk != nil) || (test.gvk != nil && *gvk != *test.gvk) {
				t.Errorf("%d: expected output: %#v, got: %#v", i, test.gvk, gvk)
			}
			if gk != test.gk {
				t.Errorf("%d: expected output: %#v, got: %#v", i, test.gk, gk)
			}
		})
	}
}

func TestParseGroupKind(t *testing.T) {
	tests := []struct {
		input string
		out   GroupKind
	}{
		{input: "Pod", out: GroupKind{Kind: "Pod"}},
		{input: ".StatefulSet", out: GroupKind{Group: "StatefulSet"}},
		{input: "StatefulSet.apps", out: GroupKind{Group: "apps", Kind: "StatefulSet"}},
	}
	for i, test := range tests {
		t.Run(test.input, func(t *testing.T) {
			out := ParseGroupKind(test.input)
			if out != test.out {
				t.Errorf("%d: expected output: %#v, got: %#v", i, test.out, out)
			}
		})
	}
}

func TestToAPIVersionAndKind(t *testing.T) {
	tests := []struct {
		desc         string
		input        GroupVersionKind
		GroupVersion string
		Kind         string
	}{
		{
			desc:         "gvk object is not empty",
			input:        GroupVersionKind{Version: "V1", Kind: "pod"},
			GroupVersion: "V1",
			Kind:         "pod",
		},
		{
			desc:         "gvk object is empty",
			input:        GroupVersionKind{},
			GroupVersion: "",
			Kind:         "",
		},
	}
	for i, test := range tests {
		version, kind := test.input.ToAPIVersionAndKind()
		if version != test.GroupVersion {
			t.Errorf("%d: expected version: %#v, got: %#v", i, test.GroupVersion, version)
		}
		if kind != test.Kind {
			t.Errorf("%d: expected kind: %#v, got: %#v", i, test.Kind, kind)
		}
	}
}

func TestBestMatch(t *testing.T) {
	tests := []struct {
		desc    string
		kinds   []GroupVersionKind
		targets []GroupVersionKind
		output  GroupVersionKind
	}{
		{
			desc:    "targets and kinds have match items",
			kinds:   []GroupVersionKind{{Version: "V1", Kind: "pod"}, {Version: "V2", Kind: "pod"}},
			targets: []GroupVersionKind{{Version: "V1", Kind: "pod"}},
			output:  GroupVersionKind{Version: "V1", Kind: "pod"},
		},
		{
			desc:    "targets and kinds do not have match items",
			kinds:   []GroupVersionKind{{Version: "V1", Kind: "pod"}, {Version: "V2", Kind: "pod"}},
			targets: []GroupVersionKind{{Version: "V3", Kind: "pod"}, {Version: "V4", Kind: "pod"}},
			output:  GroupVersionKind{Version: "V3", Kind: "pod"},
		},
	}

	for i, test := range tests {
		out := bestMatch(test.kinds, test.targets)
		if out != test.output {
			t.Errorf("%d: expected out: %#v, got: %#v", i, test.output, out)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"fmt"

	"code.gitea.io/gitea/modules/log"

	api "code.gitea.io/gitea/modules/structs"
	"github.com/go-xorm/xorm"
)

// IssueAssignees saves all issue assignees
type IssueAssignees struct {
	ID         int64 `xorm:"pk autoincr"`
	AssigneeID int64 `xorm:"INDEX"`
	IssueID    int64 `xorm:"INDEX"`
}

// This loads all assignees of an issue
func (issue *Issue) loadAssignees(e Engine) (err error) {
	// Reset maybe preexisting assignees
	issue.Assignees = []*User{}

	err = e.Table("`user`").
		Join("INNER", "issue_assignees", "assignee_id = `user`.id").
		Where("issue_assignees.issue_id = ?", issue.ID).
		Find(&issue.Assignees)

	if err != nil {
		return err
	}

	// Check if we have at least one assignee and if yes put it in as `Assignee`
	if len(issue.Assignees) > 0 {
		issue.Assignee = issue.Assignees[0]
	}

	return
}

// GetAssigneesByIssue returns everyone assigned to that issue
func GetAssigneesByIssue(issue *Issue) (assignees []*User, err error) {
	return getAssigneesByIssue(x, issue)
}

func getAssigneesByIssue(e Engine, issue *Issue) (assignees []*User, err error) {
	err = issue.loadAssignees(e)
	if err != nil {
		return assignees, err
	}

	return issue.Assignees, nil
}

// IsUserAssignedToIssue returns true when the user is assigned to the issue
func IsUserAssignedToIssue(issue *Issue, user *User) (isAssigned bool, err error) {
	isAssigned, err = x.Exist(&IssueAssignees{IssueID: issue.ID, AssigneeID: user.ID})
	return
}

// DeleteNotPassedAssignee deletes all assignees who aren't passed via the "assignees" array
func DeleteNotPassedAssignee(issue *Issue, doer *User, assignees []*User) (err error) {
	var found bool

	for _, assignee := range issue.Assignees {

		found = false
		for _, alreadyAssignee := range assignees {
			if assignee.ID == alreadyAssignee.ID {
				found = true
				break
			}
		}

		if !found {
			// This function also does comments and hooks, which is why we call it seperatly instead of directly removing the assignees here
			if err := UpdateAssignee(issue, doer, assignee.ID); err != nil {
				return err
			}
		}
	}

	return nil
}

// MakeAssigneeList concats a string with all names of the assignees. Useful for logs.
func MakeAssigneeList(issue *Issue) (assigneeList string, err error) {
	err = issue.loadAssignees(x)
	if err != nil {
		return "", err
	}

	for in, assignee := range issue.Assignees {
		assigneeList += assignee.Name

		if len(issue.Assignees) > (in + 1) {
			assigneeList += ", "
		}
	}
	return
}

// ClearAssigneeByUserID deletes all assignments of an user
func clearAssigneeByUserID(sess *xorm.Session, userID int64) (err error) {
	_, err = sess.Delete(&IssueAssignees{AssigneeID: userID})
	return
}

// AddAssigneeIfNotAssigned adds an assignee only if he isn't aleady assigned to the issue
func AddAssigneeIfNotAssigned(issue *Issue, doer *User, assigneeID int64) (err error) {
	// Check if the user is already assigned
	isAssigned, err := IsUserAssignedToIssue(issue, &User{ID: assigneeID})
	if err != nil {
		return err
	}

	if !isAssigned {
		return issue.ChangeAssignee(doer, assigneeID)
	}
	return nil
}

// UpdateAssignee deletes or adds an assignee to an issue
func UpdateAssignee(issue *Issue, doer *User, assigneeID int64) (err error) {
	return issue.ChangeAssignee(doer, assigneeID)
}

// ChangeAssignee changes the Assignee of this issue.
func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
	sess := x.NewSession()
	defer sess.Close()

	if err := sess.Begin(); err != nil {
		return err
	}

	if err := issue.changeAssignee(sess, doer, assigneeID, false); err != nil {
		return err
	}

	return sess.Commit()
}

func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID int64, isCreate bool) (err error) {

	// Update the assignee
	removed, err := updateIssueAssignee(sess, issue, assigneeID)
	if err != nil {
		return fmt.Errorf("UpdateIssueUserByAssignee: %v", err)
	}

	// Repo infos
	if err = issue.loadRepo(sess); err != nil {
		return fmt.Errorf("loadRepo: %v", err)
	}

	// Comment
	if _, err = createAssigneeComment(sess, doer, issue.Repo, issue, assigneeID, removed); err != nil {
		return fmt.Errorf("createAssigneeComment: %v", err)
	}

	// if pull request is in the middle of creation - don't call webhook
	if isCreate {
		return nil
	}

	if issue.IsPull {
		mode, _ := accessLevelUnit(sess, doer, issue.Repo, UnitTypePullRequests)

		if err = issue.loadPullRequest(sess); err != nil {
			return fmt.Errorf("loadPullRequest: %v", err)
		}
		issue.PullRequest.Issue = issue
		apiPullRequest := &api.PullRequestPayload{
			Index:       issue.Index,
			PullRequest: issue.PullRequest.apiFormat(sess),
			Repository:  issue.Repo.innerAPIFormat(sess, mode, false),
			Sender:      doer.APIFormat(),
		}
		if removed {
			apiPullRequest.Action = api.HookIssueUnassigned
		} else {
			apiPullRequest.Action = api.HookIssueAssigned
		}
		if err := prepareWebhooks(sess, issue.Repo, HookEventPullRequest, apiPullRequest); err != nil {
			log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
			return nil
		}
	} else {
		mode, _ := accessLevelUnit(sess, doer, issue.Repo, UnitTypeIssues)

		apiIssue := &api.IssuePayload{
			Index:      issue.Index,
			Issue:      issue.apiFormat(sess),
			Repository: issue.Repo.innerAPIFormat(sess, mode, false),
			Sender:     doer.APIFormat(),
		}
		if removed {
			apiIssue.Action = api.HookIssueUnassigned
		} else {
			apiIssue.Action = api.HookIssueAssigned
		}
		if err := prepareWebhooks(sess, issue.Repo, HookEventIssues, apiIssue); err != nil {
			log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
			return nil
		}
	}
	go HookQueue.Add(issue.RepoID)
	return nil
}

// UpdateAPIAssignee is a helper function to add or delete one or multiple issue assignee(s)
// Deleting is done the GitHub way (quote from their api documentation):
// https://developer.github.com/v3/issues/#edit-an-issue
// "assignees" (array): Logins for Users to assign to this issue.
// Pass one or more user logins to replace the set of assignees on this Issue.
// Send an empty array ([]) to clear all assignees from the Issue.
func UpdateAPIAssignee(issue *Issue, oneAssignee string, multipleAssignees []string, doer *User) (err error) {
	var allNewAssignees []*User

	// Keep the old assignee thingy for compatibility reasons
	if oneAssignee != "" {
		// Prevent double adding assignees
		var isDouble bool
		for _, assignee := range multipleAssignees {
			if assignee == oneAssignee {
				isDouble = true
				break
			}
		}

		if !isDouble {
			multipleAssignees = append(multipleAssignees, oneAssignee)
		}
	}

	// Loop through all assignees to add them
	for _, assigneeName := range multipleAssignees {
		assignee, err := GetUserByName(assigneeName)
		if err != nil {
			return err
		}

		allNewAssignees = append(allNewAssignees, assignee)
	}

	// Delete all old assignees not passed
	if err = DeleteNotPassedAssignee(issue, doer, allNewAssignees); err != nil {
		return err
	}

	// Add all new assignees
	// Update the assignee. The function will check if the user exists, is already
	// assigned (which he shouldn't as we deleted all assignees before) and
	// has access to the repo.
	for _, assignee := range allNewAssignees {
		// Extra method to prevent double adding (which would result in removing)
		err = AddAssigneeIfNotAssigned(issue, doer, assignee.ID)
		if err != nil {
			return err
		}
	}

	return
}

// MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs
func MakeIDsFromAPIAssigneesToAdd(oneAssignee string, multipleAssignees []string) (assigneeIDs []int64, err error) {

	// Keeping the old assigning method for compatibility reasons
	if oneAssignee != "" {

		// Prevent double adding assignees
		var isDouble bool
		for _, assignee := range multipleAssignees {
			if assignee == oneAssignee {
				isDouble = true
				break
			}
		}

		if !isDouble {
			multipleAssignees = append(multipleAssignees, oneAssignee)
		}
	}

	// Get the IDs of all assignees
	assigneeIDs = GetUserIDsByNames(multipleAssignees)

	return
}
<commit_msg>fix duplicated webhook when creating issue with assignees (#7681)<commit_after>// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"fmt"

	"code.gitea.io/gitea/modules/log"

	api "code.gitea.io/gitea/modules/structs"
	"github.com/go-xorm/xorm"
)

// IssueAssignees saves all issue assignees
type IssueAssignees struct {
	ID         int64 `xorm:"pk autoincr"`
	AssigneeID int64 `xorm:"INDEX"`
	IssueID    int64 `xorm:"INDEX"`
}

// This loads all assignees of an issue
func (issue *Issue) loadAssignees(e Engine) (err error) {
	// Reset maybe preexisting assignees
	issue.Assignees = []*User{}

	err = e.Table("`user`").
		Join("INNER", "issue_assignees", "assignee_id = `user`.id").
		Where("issue_assignees.issue_id = ?", issue.ID).
		Find(&issue.Assignees)

	if err != nil {
		return err
	}

	// Check if we have at least one assignee and if yes put it in as `Assignee`
	if len(issue.Assignees) > 0 {
		issue.Assignee = issue.Assignees[0]
	}

	return
}

// GetAssigneesByIssue returns everyone assigned to that issue
func GetAssigneesByIssue(issue *Issue) (assignees []*User, err error) {
	return getAssigneesByIssue(x, issue)
}

func getAssigneesByIssue(e Engine, issue *Issue) (assignees []*User, err error) {
	err = issue.loadAssignees(e)
	if err != nil {
		return assignees, err
	}

	return issue.Assignees, nil
}

// IsUserAssignedToIssue returns true when the user is assigned to the issue
func IsUserAssignedToIssue(issue *Issue, user *User) (isAssigned bool, err error) {
	isAssigned, err = x.Exist(&IssueAssignees{IssueID: issue.ID, AssigneeID: user.ID})
	return
}

// DeleteNotPassedAssignee deletes all assignees who aren't passed via the "assignees" array
func DeleteNotPassedAssignee(issue *Issue, doer *User, assignees []*User) (err error) {
	var found bool

	for _, assignee := range issue.Assignees {

		found = false
		for _, alreadyAssignee := range assignees {
			if assignee.ID == alreadyAssignee.ID {
				found = true
				break
			}
		}

		if !found {
			// This function also does comments and hooks, which is why we call it seperatly instead of directly removing the assignees here
			if err := UpdateAssignee(issue, doer, assignee.ID); err != nil {
				return err
			}
		}
	}

	return nil
}

// MakeAssigneeList concats a string with all names of the assignees. Useful for logs.
func MakeAssigneeList(issue *Issue) (assigneeList string, err error) {
	err = issue.loadAssignees(x)
	if err != nil {
		return "", err
	}

	for in, assignee := range issue.Assignees {
		assigneeList += assignee.Name

		if len(issue.Assignees) > (in + 1) {
			assigneeList += ", "
		}
	}
	return
}

// ClearAssigneeByUserID deletes all assignments of an user
func clearAssigneeByUserID(sess *xorm.Session, userID int64) (err error) {
	_, err = sess.Delete(&IssueAssignees{AssigneeID: userID})
	return
}

// AddAssigneeIfNotAssigned adds an assignee only if he isn't aleady assigned to the issue
func AddAssigneeIfNotAssigned(issue *Issue, doer *User, assigneeID int64) (err error) {
	// Check if the user is already assigned
	isAssigned, err := IsUserAssignedToIssue(issue, &User{ID: assigneeID})
	if err != nil {
		return err
	}

	if !isAssigned {
		return issue.ChangeAssignee(doer, assigneeID)
	}
	return nil
}

// UpdateAssignee deletes or adds an assignee to an issue
func UpdateAssignee(issue *Issue, doer *User, assigneeID int64) (err error) {
	return issue.ChangeAssignee(doer, assigneeID)
}

// ChangeAssignee changes the Assignee of this issue.
func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
	sess := x.NewSession()
	defer sess.Close()

	if err := sess.Begin(); err != nil {
		return err
	}

	if err := issue.changeAssignee(sess, doer, assigneeID, false); err != nil {
		return err
	}

	if err := sess.Commit(); err != nil {
		return err
	}

	go HookQueue.Add(issue.RepoID)
	return nil
}

func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID int64, isCreate bool) (err error) {
	// Update the assignee
	removed, err := updateIssueAssignee(sess, issue, assigneeID)
	if err != nil {
		return fmt.Errorf("UpdateIssueUserByAssignee: %v", err)
	}

	// Repo infos
	if err = issue.loadRepo(sess); err != nil {
		return fmt.Errorf("loadRepo: %v", err)
	}

	// Comment
	if _, err = createAssigneeComment(sess, doer, issue.Repo, issue, assigneeID, removed); err != nil {
		return fmt.Errorf("createAssigneeComment: %v", err)
	}

	// if pull request is in the middle of creation - don't call webhook
	if isCreate {
		return nil
	}

	if issue.IsPull {
		mode, _ := accessLevelUnit(sess, doer, issue.Repo, UnitTypePullRequests)

		if err = issue.loadPullRequest(sess); err != nil {
			return fmt.Errorf("loadPullRequest: %v", err)
		}
		issue.PullRequest.Issue = issue
		apiPullRequest := &api.PullRequestPayload{
			Index:       issue.Index,
			PullRequest: issue.PullRequest.apiFormat(sess),
			Repository:  issue.Repo.innerAPIFormat(sess, mode, false),
			Sender:      doer.APIFormat(),
		}
		if removed {
			apiPullRequest.Action = api.HookIssueUnassigned
		} else {
			apiPullRequest.Action = api.HookIssueAssigned
		}
		if err := prepareWebhooks(sess, issue.Repo, HookEventPullRequest, apiPullRequest); err != nil {
			log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
			return nil
		}
	} else {
		mode, _ := accessLevelUnit(sess, doer, issue.Repo, UnitTypeIssues)

		apiIssue := &api.IssuePayload{
			Index:      issue.Index,
			Issue:      issue.apiFormat(sess),
			Repository: issue.Repo.innerAPIFormat(sess, mode, false),
			Sender:     doer.APIFormat(),
		}
		if removed {
			apiIssue.Action = api.HookIssueUnassigned
		} else {
			apiIssue.Action = api.HookIssueAssigned
		}
		if err := prepareWebhooks(sess, issue.Repo, HookEventIssues, apiIssue); err != nil {
			log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
			return nil
		}
	}
	return nil
}

// UpdateAPIAssignee is a helper function to add or delete one or multiple issue assignee(s)
// Deleting is done the GitHub way (quote from their api documentation):
// https://developer.github.com/v3/issues/#edit-an-issue
// "assignees" (array): Logins for Users to assign to this issue.
// Pass one or more user logins to replace the set of assignees on this Issue.
// Send an empty array ([]) to clear all assignees from the Issue.
func UpdateAPIAssignee(issue *Issue, oneAssignee string, multipleAssignees []string, doer *User) (err error) {
	var allNewAssignees []*User

	// Keep the old assignee thingy for compatibility reasons
	if oneAssignee != "" {
		// Prevent double adding assignees
		var isDouble bool
		for _, assignee := range multipleAssignees {
			if assignee == oneAssignee {
				isDouble = true
				break
			}
		}

		if !isDouble {
			multipleAssignees = append(multipleAssignees, oneAssignee)
		}
	}

	// Loop through all assignees to add them
	for _, assigneeName := range multipleAssignees {
		assignee, err := GetUserByName(assigneeName)
		if err != nil {
			return err
		}

		allNewAssignees = append(allNewAssignees, assignee)
	}

	// Delete all old assignees not passed
	if err = DeleteNotPassedAssignee(issue, doer, allNewAssignees); err != nil {
		return err
	}

	// Add all new assignees
	// Update the assignee. The function will check if the user exists, is already
	// assigned (which he shouldn't as we deleted all assignees before) and
	// has access to the repo.
	for _, assignee := range allNewAssignees {
		// Extra method to prevent double adding (which would result in removing)
		err = AddAssigneeIfNotAssigned(issue, doer, assignee.ID)
		if err != nil {
			return err
		}
	}

	return
}

// MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs
func MakeIDsFromAPIAssigneesToAdd(oneAssignee string, multipleAssignees []string) (assigneeIDs []int64, err error) {

	// Keeping the old assigning method for compatibility reasons
	if oneAssignee != "" {

		// Prevent double adding assignees
		var isDouble bool
		for _, assignee := range multipleAssignees {
			if assignee == oneAssignee {
				isDouble = true
				break
			}
		}

		if !isDouble {
			multipleAssignees = append(multipleAssignees, oneAssignee)
		}
	}

	// Get the IDs of all assignees
	assigneeIDs = GetUserIDsByNames(multipleAssignees)

	return
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package poddisruptionbudget

import (
	policy "k8s.io/api/policy/v1beta1"
	apimeta "k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// UpdateDisruptionAllowedCondition updates the DisruptionAllowed condition
// on a PodDisruptionBudget based on the value of the DisruptionsAllowed field.
func UpdateDisruptionAllowedCondition(pdb *policy.PodDisruptionBudget) {
	if pdb.Status.Conditions == nil {
		pdb.Status.Conditions = make([]metav1.Condition, 0)
	}
	if pdb.Status.DisruptionsAllowed > 0 {
		apimeta.SetStatusCondition(&pdb.Status.Conditions, metav1.Condition{
			Type:               policy.DisruptionAllowedCondition,
			Reason:             policy.SufficientPodsReason,
			Status:             metav1.ConditionTrue,
			ObservedGeneration: pdb.Status.ObservedGeneration,
		})
	} else {
		apimeta.SetStatusCondition(&pdb.Status.Conditions, metav1.Condition{
			Type:               policy.DisruptionAllowedCondition,
			Reason:             policy.InsufficientPodsReason,
			Status:             metav1.ConditionFalse,
			ObservedGeneration: pdb.Status.ObservedGeneration,
		})
	}
}

// ConditionsAreUpToDate checks whether the status and reason for the
// DisruptionAllowed condition are set to the correct values based on the
// DisruptionsAllowed field.
func ConditionsAreUpToDate(pdb *policy.PodDisruptionBudget) bool {
	cond := apimeta.FindStatusCondition(pdb.Status.Conditions, policy.DisruptionAllowedCondition)
	if cond == nil {
		return false
	}

	if pdb.Status.ObservedGeneration != pdb.Generation {
		return false
	}

	if pdb.Status.DisruptionsAllowed > 0 {
		return cond.Status == metav1.ConditionTrue && cond.Reason == policy.SufficientPodsReason
	}
	return cond.Status == metav1.ConditionFalse && cond.Reason == policy.InsufficientPodsReason
}<commit_msg>generated<commit_after>/*
Copyright 2021 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package poddisruptionbudget

import (
	policy "k8s.io/api/policy/v1beta1"
	apimeta "k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// UpdateDisruptionAllowedCondition updates the DisruptionAllowed condition
// on a PodDisruptionBudget based on the value of the DisruptionsAllowed field.
func UpdateDisruptionAllowedCondition(pdb *policy.PodDisruptionBudget) {
	if pdb.Status.Conditions == nil {
		pdb.Status.Conditions = make([]metav1.Condition, 0)
	}
	if pdb.Status.DisruptionsAllowed > 0 {
		apimeta.SetStatusCondition(&pdb.Status.Conditions, metav1.Condition{
			Type:               policy.DisruptionAllowedCondition,
			Reason:             policy.SufficientPodsReason,
			Status:             metav1.ConditionTrue,
			ObservedGeneration: pdb.Status.ObservedGeneration,
		})
	} else {
		apimeta.SetStatusCondition(&pdb.Status.Conditions, metav1.Condition{
			Type:               policy.DisruptionAllowedCondition,
			Reason:             policy.InsufficientPodsReason,
			Status:             metav1.ConditionFalse,
			ObservedGeneration: pdb.Status.ObservedGeneration,
		})
	}
}

// ConditionsAreUpToDate checks whether the status and reason for the
// DisruptionAllowed condition are set to the correct values based on the
// DisruptionsAllowed field.
func ConditionsAreUpToDate(pdb *policy.PodDisruptionBudget) bool {
	cond := apimeta.FindStatusCondition(pdb.Status.Conditions, policy.DisruptionAllowedCondition)
	if cond == nil {
		return false
	}

	if pdb.Status.ObservedGeneration != pdb.Generation {
		return false
	}

	if pdb.Status.DisruptionsAllowed > 0 {
		return cond.Status == metav1.ConditionTrue && cond.Reason == policy.SufficientPodsReason
	}
	return cond.Status == metav1.ConditionFalse && cond.Reason == policy.InsufficientPodsReason
}
<|endoftext|>


<commit_before>package main

import "testing"

func TestFormatYear(t *testing.T) {
	cases := []struct {
		in, want string
	}{
		{"2015", "Thu, 01 Jan 2015 00:00:00 +0000"},
		{"20151102", "Mon, 02 Nov 2015 00:00:00 +0000"},
		{"20152402", "Tue, 24 Feb 2015 00:00:00 +0000"},
	}
	for _, c := range cases {
		got := formatYear(c.in)
		if got != c.want {
			t.Errorf("formatYear(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}
<commit_msg>formatYear: test invalid, empty or none year value<commit_after>package main

import "testing"

func TestFormatYear(t *testing.T) {
	cases := []struct {
		in, want string
	}{
		{"2015", "Thu, 01 Jan 2015 00:00:00 +0000"},
		{"20151102", "Mon, 02 Nov 2015 00:00:00 +0000"},
		{"20152402", "Tue, 24 Feb 2015 00:00:00 +0000"},
		{"", ""},
		{"NOT_A_YEAR", ""},
	}
	for _, c := range cases {
		got := formatYear(c.in)
		if got != c.want {
			t.Errorf("formatYear(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}
<|endoftext|>


<commit_before>package proto

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

	"google.golang.org/protobuf/proto"
)

func TestEncodeProtoRequest(t *testing.T) {
	cat := &Cat{Name: "Ziggy", Age: 13, Breed: "Lumpy"}

	r := httptest.NewRequest(http.MethodGet, "/cat", nil)

	err := EncodeProtoRequest(nil, r, cat)
	if err != nil {
		t.Errorf("expected no encoding errors but got: %s", err)
		return
	}

	const xproto = "application/x-protobuf"
	if typ := r.Header.Get("Content-Type"); typ != xproto {
		t.Errorf("expected content type of %q, got %q", xproto, typ)
		return
	}

	bod, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Errorf("expected no read errors but got: %s", err)
		return
	}
	defer r.Body.Close()

	var got Cat
	err = proto.Unmarshal(bod, &got)
	if err != nil {
		t.Errorf("expected no proto errors but got: %s", err)
		return
	}

	if !proto.Equal(&got, cat) {
		t.Errorf("expected cats to be equal but got:\n\n%#v\n\nwant:\n\n%#v", got, cat)
		return
	}
}

func TestEncodeProtoResponse(t *testing.T) {
	cat := &Cat{Name: "Ziggy", Age: 13, Breed: "Lumpy"}

	wr := httptest.NewRecorder()

	err := EncodeProtoResponse(nil, wr, cat)
	if err != nil {
		t.Errorf("expected no encoding errors but got: %s", err)
		return
	}

	w := wr.Result()

	const xproto = "application/x-protobuf"
	if typ := w.Header.Get("Content-Type"); typ != xproto {
		t.Errorf("expected content type of %q, got %q", xproto, typ)
		return
	}

	if w.StatusCode != http.StatusTeapot {
		t.Errorf("expected status code of %d, got %d", http.StatusTeapot, w.StatusCode)
		return
	}

	bod, err := ioutil.ReadAll(w.Body)
	if err != nil {
		t.Errorf("expected no read errors but got: %s", err)
		return
	}
	defer w.Body.Close()

	var got Cat
	err = proto.Unmarshal(bod, &got)
	if err != nil {
		t.Errorf("expected no proto errors but got: %s", err)
		return
	}

	if !proto.Equal(&got, cat) {
		t.Errorf("expected cats to be equal but got:\n\n%#v\n\nwant:\n\n%#v", got, cat)
		return
	}
}

func (c *Cat) StatusCode() int {
	return http.StatusTeapot
}
<commit_msg>Fix nil context being passed to function<commit_after>package proto

import (
	"context"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

	"google.golang.org/protobuf/proto"
)

func TestEncodeProtoRequest(t *testing.T) {
	cat := &Cat{Name: "Ziggy", Age: 13, Breed: "Lumpy"}

	r := httptest.NewRequest(http.MethodGet, "/cat", nil)

	err := EncodeProtoRequest(context.TODO(), r, cat)
	if err != nil {
		t.Errorf("expected no encoding errors but got: %s", err)
		return
	}

	const xproto = "application/x-protobuf"
	if typ := r.Header.Get("Content-Type"); typ != xproto {
		t.Errorf("expected content type of %q, got %q", xproto, typ)
		return
	}

	bod, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Errorf("expected no read errors but got: %s", err)
		return
	}
	defer r.Body.Close()

	var got Cat
	err = proto.Unmarshal(bod, &got)
	if err != nil {
		t.Errorf("expected no proto errors but got: %s", err)
		return
	}

	if !proto.Equal(&got, cat) {
		t.Errorf("expected cats to be equal but got:\n\n%#v\n\nwant:\n\n%#v", got, cat)
		return
	}
}

func TestEncodeProtoResponse(t *testing.T) {
	cat := &Cat{Name: "Ziggy", Age: 13, Breed: "Lumpy"}

	wr := httptest.NewRecorder()

	err := EncodeProtoResponse(context.TODO(), wr, cat)
	if err != nil {
		t.Errorf("expected no encoding errors but got: %s", err)
		return
	}

	w := wr.Result()

	const xproto = "application/x-protobuf"
	if typ := w.Header.Get("Content-Type"); typ != xproto {
		t.Errorf("expected content type of %q, got %q", xproto, typ)
		return
	}

	if w.StatusCode != http.StatusTeapot {
		t.Errorf("expected status code of %d, got %d", http.StatusTeapot, w.StatusCode)
		return
	}

	bod, err := ioutil.ReadAll(w.Body)
	if err != nil {
		t.Errorf("expected no read errors but got: %s", err)
		return
	}
	defer w.Body.Close()

	var got Cat
	err = proto.Unmarshal(bod, &got)
	if err != nil {
		t.Errorf("expected no proto errors but got: %s", err)
		return
	}

	if !proto.Equal(&got, cat) {
		t.Errorf("expected cats to be equal but got:\n\n%#v\n\nwant:\n\n%#v", got, cat)
		return
	}
}

func (c *Cat) StatusCode() int {
	return http.StatusTeapot
}
<|endoftext|>


<commit_before>package couch

import (
	"errors"
	"fmt"
	"net/url"
	"strings"
)

// A replication from a source to a target
type Replication struct {
	source     *Database
	target     *Database
	continuous bool
	sessionID  string
}

// A bidirectional replication
type Sync struct {
	replA2B *Replication
	replB2A *Replication
}

// CouchDB request for replication
type replRequest struct {
	CreateTarget bool   `json:"create_target"`
	Source       string `json:"source"`
	Target       string `json:"target"`
	Continuous   bool   `json:"continuous"`
	Cancel       bool   `json:"cancel,omitempty"`
}

// CouchDB response to replication request
type replResponse struct {
	Ok            bool   `json:"ok"`
	ReplIDVersion int    `json:"replication_id_version"`
	SessionID     string `json:"session_id"`
	SourceLastSeq int    `json:"source_last_seq"`
}

// Replicates given database to a target database. If the target database
// does not exist it will be created. The target database may be on a different host.
func (db *Database) ReplicateTo(target *Database, continuously bool) (*Replication, error) {
	var resp replResponse
	req := replRequest{CreateTarget: true, Source: db.URL(), Target: target.urlWithCredentials(), Continuous: continuously}
	_, err := Do(db.replicationURL(), "POST", db.Cred(), req, &resp)
	if err != nil {
		return nil, err
	}
	repl := &Replication{source: db, target: target, continuous: continuously, sessionID: resp.SessionID}
	return repl, err
}

// IsReplication returns true if a task represents a replication.
func (t Task) IsReplication() bool {
	return t["type"] == "replication"
}

// HasReplicationID returns true if a task has a given replication id.
func (t Task) HasReplicationID(id string) bool {
	s, _ := t["replication_id"].(string)
	return strings.HasPrefix(s, id)
}

// func (t Task) Replication(relativeTo *Server) *Replication {
// 	var r *Replication
// 	if t.isReplication() {
// 		sourceURL, _ := url.Parse(t["source"])
// 		sourceURL.Path
// 		//sourceDB :=
// 		//targetDB :=
// 		r = &Replication{
// 			sessionID:  t["replication_id"],
// 			continuous: t["continuous"],
// 		}
// 	}
// 	return r
// }

// IsRunning returns whether a replication is currently active or not.
func (repl *Replication) IsActive() (bool, error) {
	tasks, err := repl.Source().Server().ActiveTasks()
	if err != nil {
		return false, err
	}
	for _, task := range tasks {
		if task.IsReplication() && task.HasReplicationID(repl.SessionID()) {
			return true, nil
		}
	}
	return false, nil
}

// IsActive returns whether a sync is active or not. A sync process consists of
// two replications. If one is active and the other isn't, you get an error message.
func (sync *Sync) IsActive() (bool, error) {
	a2bIsActive, err := sync.replA2B.IsActive()
	if err != nil {
		return false, err
	}
	b2aIsActive, err := sync.replA2B.IsActive()
	if err != nil {
		return false, err
	}
	if a2bIsActive != b2aIsActive {
		return false, errors.New("corrupt sync, only one replication active instead of two")
	}
	return a2bIsActive && b2aIsActive, nil
}

// // ActiveReplications returns all currently active replications on a server
// func (s *Server) ActiveReplications() ([]*Replication, error) {
// 	var repls []*Replication
// 	err := s.ActiveTasks(func(t Task) {
// 		repl := t.Replication(s)
// 		if repl != nil {
// 			repls = append(repls, repl)
// 		}
// 	})
// 	return repls, err
// }

// Cancel a continuously running replication
func (repl *Replication) Cancel() error {
	req := replRequest{CreateTarget: true, Source: repl.source.URL(), Target: repl.target.URL(), Continuous: repl.continuous, Cancel: true}
	_, err := Do(repl.Source().replicationURL(), "POST", repl.source.Cred(), req, nil)
	return err
}

// Returns replication source
func (repl *Replication) Source() *Database {
	return repl.source
}

// Returns replication target
func (repl *Replication) Target() *Database {
	return repl.target
}

// Returns whether replication is running continuously or not
func (repl *Replication) Continuous() bool {
	return repl.continuous
}

func (repl *Replication) SessionID() string {
	return repl.sessionID
}

// Synchronizes two databases by setting up two replications, one from given database
// to target and from target to given database. If the target database does not exist it will be created.
// The target database may be on a different host.
//
// This method may be convenient but note that it is not atomic: Sync means that this method will first
// replicate db to target and then target to db. If the first one fails, both fail. If the first one works but
// the second doesn't, the first one will have executed nonetheless. If the sync has been set up to be continuous,
// the first continuous replication will be cancelled if the second one fails.
func (db *Database) SyncWith(target *Database, continuously bool) (*Sync, error) {
	replA2B, err := db.ReplicateTo(target, continuously)
	if err != nil {
		return nil, err
	}
	replB2A, err := target.ReplicateTo(db, continuously)
	if err != nil {
		replA2B.Cancel()
		return nil, err
	}
	sync := &Sync{replA2B, replB2A}
	return sync, nil
}

// Cancel a continuously running sync
func (sync *Sync) Cancel() error {
	// Call cancel directly on both replications, if replA2B.Cancel() leads to
	// an error we might still be able to cancel replB2A. Errors will be combined later.
	errA2B := sync.replA2B.Cancel()
	errB2A := sync.replB2A.Cancel()
	var err error
	if errA2B != nil || errB2A != nil {
		err = fmt.Errorf("can't cancel sync: a->b: %v, b->a: %v", errA2B, errB2A)
	}
	return err
}

// Not safe, only used body of replication request
func (db *Database) urlWithCredentials() string {
	result, _ := url.Parse(db.URL())
	cred := db.Cred()
	if cred != nil {
		result.User = url.UserPassword(db.server.cred.user, db.server.cred.password)
	}
	return result.String()
}

func (db *Database) replicationURL() string {
	return db.server.url + "/_replicate"
}
<commit_msg>Simplify checking for replication in active tasks<commit_after>package couch

import (
	"errors"
	"fmt"
	"net/url"
	"strings"
)

// A replication from a source to a target
type Replication struct {
	source     *Database
	target     *Database
	continuous bool
	sessionID  string
}

// A bidirectional replication
type Sync struct {
	replA2B *Replication
	replB2A *Replication
}

// CouchDB request for replication
type replRequest struct {
	CreateTarget bool   `json:"create_target"`
	Source       string `json:"source"`
	Target       string `json:"target"`
	Continuous   bool   `json:"continuous"`
	Cancel       bool   `json:"cancel,omitempty"`
}

// CouchDB response to replication request
type replResponse struct {
	Ok            bool   `json:"ok"`
	ReplIDVersion int    `json:"replication_id_version"`
	SessionID     string `json:"session_id"`
	SourceLastSeq int    `json:"source_last_seq"`
}

// Replicates given database to a target database. If the target database
// does not exist it will be created. The target database may be on a different host.
func (db *Database) ReplicateTo(target *Database, continuously bool) (*Replication, error) {
	var resp replResponse
	req := replRequest{CreateTarget: true, Source: db.URL(), Target: target.urlWithCredentials(), Continuous: continuously}
	_, err := Do(db.replicationURL(), "POST", db.Cred(), req, &resp)
	if err != nil {
		return nil, err
	}
	repl := &Replication{source: db, target: target, continuous: continuously, sessionID: resp.SessionID}
	return repl, err
}

// IsReplication returns true if a task represents a replication.
func (t Task) IsReplication() bool {
	return t["type"] == "replication"
}

// HasReplicationID returns true if a task has a given replication id.
func (t Task) HasReplicationID(id string) bool {
	s, _ := t["replication_id"].(string)
	return strings.HasPrefix(s, id)
}

// func (t Task) Replication(relativeTo *Server) *Replication {
// 	var r *Replication
// 	if t.isReplication() {
// 		sourceURL, _ := url.Parse(t["source"])
// 		sourceURL.Path
// 		//sourceDB :=
// 		//targetDB :=
// 		r = &Replication{
// 			sessionID:  t["replication_id"],
// 			continuous: t["continuous"],
// 		}
// 	}
// 	return r
// }

// IsRunning returns whether a replication is currently active or not.
func (repl *Replication) IsActive() (bool, error) {
	tasks, err := repl.Source().Server().ActiveTasks()
	if err != nil {
		return false, err
	}
	for _, task := range tasks {
		if task.HasReplicationID(repl.SessionID()) {
			return true, nil
		}
	}
	return false, nil
}

// IsActive returns whether a sync is active or not. A sync process consists of
// two replications. If one is active and the other isn't, you get an error message.
func (sync *Sync) IsActive() (bool, error) {
	a2bIsActive, err := sync.replA2B.IsActive()
	if err != nil {
		return false, err
	}
	b2aIsActive, err := sync.replA2B.IsActive()
	if err != nil {
		return false, err
	}
	if a2bIsActive != b2aIsActive {
		return false, errors.New("corrupt sync, only one replication active instead of two")
	}
	return a2bIsActive && b2aIsActive, nil
}

// // ActiveReplications returns all currently active replications on a server
// func (s *Server) ActiveReplications() ([]*Replication, error) {
// 	var repls []*Replication
// 	err := s.ActiveTasks(func(t Task) {
// 		repl := t.Replication(s)
// 		if repl != nil {
// 			repls = append(repls, repl)
// 		}
// 	})
// 	return repls, err
// }

// Cancel a continuously running replication
func (repl *Replication) Cancel() error {
	req := replRequest{CreateTarget: true, Source: repl.source.URL(), Target: repl.target.URL(), Continuous: repl.continuous, Cancel: true}
	_, err := Do(repl.Source().replicationURL(), "POST", repl.source.Cred(), req, nil)
	return err
}

// Returns replication source
func (repl *Replication) Source() *Database {
	return repl.source
}

// Returns replication target
func (repl *Replication) Target() *Database {
	return repl.target
}

// Returns whether replication is running continuously or not
func (repl *Replication) Continuous() bool {
	return repl.continuous
}

func (repl *Replication) SessionID() string {
	return repl.sessionID
}

// Synchronizes two databases by setting up two replications, one from given database
// to target and from target to given database. If the target database does not exist it will be created.
// The target database may be on a different host.
//
// This method may be convenient but note that it is not atomic: Sync means that this method will first
// replicate db to target and then target to db. If the first one fails, both fail. If the first one works but
// the second doesn't, the first one will have executed nonetheless. If the sync has been set up to be continuous,
// the first continuous replication will be cancelled if the second one fails.
func (db *Database) SyncWith(target *Database, continuously bool) (*Sync, error) {
	replA2B, err := db.ReplicateTo(target, continuously)
	if err != nil {
		return nil, err
	}
	replB2A, err := target.ReplicateTo(db, continuously)
	if err != nil {
		replA2B.Cancel()
		return nil, err
	}
	sync := &Sync{replA2B, replB2A}
	return sync, nil
}

// Cancel a continuously running sync
func (sync *Sync) Cancel() error {
	// Call cancel directly on both replications, if replA2B.Cancel() leads to
	// an error we might still be able to cancel replB2A. Errors will be combined later.
	errA2B := sync.replA2B.Cancel()
	errB2A := sync.replB2A.Cancel()
	var err error
	if errA2B != nil || errB2A != nil {
		err = fmt.Errorf("can't cancel sync: a->b: %v, b->a: %v", errA2B, errB2A)
	}
	return err
}

// Not safe, only used body of replication request
func (db *Database) urlWithCredentials() string {
	result, _ := url.Parse(db.URL())
	cred := db.Cred()
	if cred != nil {
		result.User = url.UserPassword(db.server.cred.user, db.server.cred.password)
	}
	return result.String()
}

func (db *Database) replicationURL() string {
	return db.server.url + "/_replicate"
}
<|endoftext|>