<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 main

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"strings"
	"time"

	flag "github.com/spf13/pflag"

	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	_ "k8s.io/client-go/plugin/pkg/client/auth" // Enable all auth provider plugins
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/tools/clientcmd/api"
	"k8s.io/test-infra/experiment/clustersecretbackup/secretmanager"
	"k8s.io/test-infra/gencred/pkg/util"

	ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
)

var (
	defaultSecretLabels = map[string]string{
		"update_time": time.Now().Format("2006-01-02-15-04-05"),
		"type":        "prow_backup",
		"source":      "",
	}
)

// options are the available command-line flags.
type options struct {
	project            string
	clusterContext     string
	namespaces         []string
	secrets            map[string]string
	update             bool
	dryRun             bool
	emitExternalSecret bool
	skipServiceAccount bool
}

type client struct {
	kubeClient          ctrlruntimeclient.Client
	secretmanagerClient secretmanager.ClientInterface
	allSi               *corev1.SecretList
	options
}

func escape(s string) string {
	return strings.ReplaceAll(s, ".", "__d_o_t__")
}

func (c *client) gsmSecretName(clusterSecret *corev1.Secret) string {
	// Use cluster name, namespace and secret name is almost unique identifier.
	// However, if consider GCP allow creating clusters with the same name under
	// different zones, probably will need to add zones to this. Will address if
	// ever needed.
	return fmt.Sprintf("%s__%s__%s", c.clusterContext, clusterSecret.Namespace, escape(clusterSecret.Name))
}

// gatherOptions parses the command-line flags.
func gatherOptions(fs *flag.FlagSet, args ...string) options {
	var o options
	fs.StringVar(&o.project, "project", "", "GCP project used for backing up secrets")
	fs.StringVar(&o.clusterContext, "cluster-context", "", "cluster context name used for backing up secrets, must be full form such as <PROVIDER>_<PROJECT>_<ZONE>_<CLUSTER>")
	fs.StringSliceVar(&o.namespaces, "namespace", []string{}, "namespace to backup, can be passed in repeatedly")
	fs.StringToStringVar(&o.secrets, "secret-name", nil, "namespace:name of secrets to be backed up, in the form of --secret-name=<namespace>=<name>. By default all secrets in the chosen namespace(s) are backed up.")
	fs.BoolVar(&o.update, "update", false, "Controls whether update existing secret or not, if false then secret will only be created")
	fs.BoolVar(&o.dryRun, "dryrun", false, "Controls whether this is dry run or not")
	fs.BoolVar(&o.skipServiceAccount, "skip-sa", false, "Controls whether to skip service account tokens")
	fs.BoolVar(&o.emitExternalSecret, "emit-external-secret", false, "Controls whether to output an ExternalSecret referencing the Secret")
	fs.Parse(args)

	return o
}

// validateFlags validates the command-line flags.
func (o *options) validateFlags() error {
	if len(o.project) == 0 {
		return errors.New("--project must be provided")
	}
	if len(o.clusterContext) == 0 {
		return errors.New("--cluster-context must be provided")
	}
	return nil
}

func newClient(o options) (*client, error) {
	secretmanagerClient, err := secretmanager.NewClient(o.project, o.dryRun)
	if err != nil {
		return nil, fmt.Errorf("failed creating secret manager client: %w", err)
	}

	kubeClient, err := newKubeClients(o.clusterContext)
	if err != nil {
		return nil, fmt.Errorf("failed creating kube client: %w", err)
	}
	return &client{
		kubeClient:          kubeClient,
		secretmanagerClient: secretmanagerClient,
		options:             o,
	}, nil
}

func newKubeClients(clusterContext string) (ctrlruntimeclient.Client, error) {
	loader := clientcmd.NewDefaultClientConfigLoadingRules()

	cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		loader, &clientcmd.ConfigOverrides{
			// Enforcing clusterContext in the full form of cluster context,
			// instead of short names for kubectl.
			Context:        api.Context{Cluster: clusterContext},
			CurrentContext: clusterContext,
		}).ClientConfig()
	if err != nil {
		return nil, fmt.Errorf("failed create rest config: %v ------ Did you supply the full form of cluster context name instead of short hand?", err)
	}
	return ctrlruntimeclient.New(cfg, ctrlruntimeclient.Options{})
}

// process merges secret into a new secret for write.
func (c *client) updateSingleSecret(ctx context.Context, clusterSecret *corev1.Secret) error {
	secretID := c.gsmSecretName(clusterSecret)
	// Google secret manager expects pure string instead of map[string][]byte,
	// so translate *corev1.Secret.Data to map[string]string, this will also be
	// what kubernetes external secret expects.
	stringData := map[string]string{}
	for key, val := range clusterSecret.Data {
		stringData[key] = string(val)
	}
	payload, err := json.Marshal(stringData)
	if err != nil {
		return fmt.Errorf("failed marshal secret %s: %w", clusterSecret.Name, err)
	}
	log := logrus.WithFields(logrus.Fields{
		"project":     c.project,
		"cluster":     c.clusterContext,
		"namespace":   clusterSecret.Namespace,
		"secret-name": clusterSecret.Name,
		"gsm-secret":  secretID,
	})
	if sat := "kubernetes.io/service-account-token"; string(clusterSecret.Type) == sat {
		log.Infof("Skipping: the secret type is %s", sat)
		return nil
	}
	if c.dryRun {
		log.Info("[Dryrun]: backing up secret")
	}

	ss, err := c.secretmanagerClient.ListSecrets(ctx)
	if err != nil {
		return err
	}
	var found bool
	for _, s := range ss {
		if strings.HasSuffix(s.Name, fmt.Sprintf("/%s", secretID)) {
			found = true
		}
	}
	if found && !c.update {
		log.Info("Skipping: the secret already exist and --update is not true")
		return nil
	}
	// Now create or update
	if !found {
		log.Info("Creating secret in GSM")
		if _, err = c.secretmanagerClient.CreateSecret(ctx, secretID); err != nil {
			return err
		}
	}
	log.Info("Create secret version in GSM")
	if err := c.secretmanagerClient.AddSecretVersion(ctx, secretID, payload); err != nil {
		return err
	}
	return c.secretmanagerClient.AddSecretLabel(ctx, secretID, defaultSecretLabels)
}

func (c *client) updateAllSecrets(ctx context.Context, allowed map[string]string) error {
	for _, secret := range c.allSi.Items {
		if allowed != nil {
			if val, ok := allowed[secret.Namespace]; !ok || val != secret.Name {
				continue
			}
		}
		if c.skipServiceAccount && secret.Type == corev1.SecretTypeServiceAccountToken {
			continue
		}
		if err := c.updateSingleSecret(ctx, &secret); err != nil {
			return err
		}
		if c.emitExternalSecret {
			fmt.Printf(`apiVersion: kubernetes-client.io/v1
kind: ExternalSecret
metadata:
  name: "%s"
  namespace: "%s"
spec:
  backendType: gcpSecretsManager
  projectId: "%s"
  dataFrom:
  - "%s" # Secret name in GSM
---
`, secret.Name, secret.Namespace, c.project, c.gsmSecretName(&secret))
		}
	}
	return nil
}

func (c *client) loadClusterSecrets(ctx context.Context) error {
	c.allSi = &corev1.SecretList{}
	var listOptions []ctrlruntimeclient.ListOption
	for _, ns := range c.namespaces {
		listOptions = append(listOptions, &ctrlruntimeclient.ListOptions{
			Namespace: ns,
		})
	}
	return c.kubeClient.List(ctx, c.allSi, listOptions...)
}

func main() {
	o := gatherOptions(flag.NewFlagSet(os.Args[0], flag.ExitOnError), os.Args[1:]...)
	if err := o.validateFlags(); err != nil {
		util.PrintErrAndExit(err)
	}
	defaultSecretLabels["source"] = o.clusterContext

	ctx := context.Background()

	c, err := newClient(o)
	if err != nil {
		logrus.WithError(err).Fatal("Failed creating client")
	}

	if err := c.loadClusterSecrets(ctx); err != nil {
		logrus.WithError(err).Fatal("Failed listing secrets")
	}

	if err := c.updateAllSecrets(ctx, o.secrets); err != nil {
		logrus.WithError(err).Fatal("Failed updating secrets")
	}
}
<commit_msg>Switch to true default<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 main

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"strings"
	"time"

	flag "github.com/spf13/pflag"

	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	_ "k8s.io/client-go/plugin/pkg/client/auth" // Enable all auth provider plugins
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/tools/clientcmd/api"
	"k8s.io/test-infra/experiment/clustersecretbackup/secretmanager"
	"k8s.io/test-infra/gencred/pkg/util"

	ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
)

var (
	defaultSecretLabels = map[string]string{
		"update_time": time.Now().Format("2006-01-02-15-04-05"),
		"type":        "prow_backup",
		"source":      "",
	}
)

// options are the available command-line flags.
type options struct {
	project            string
	clusterContext     string
	namespaces         []string
	secrets            map[string]string
	update             bool
	dryRun             bool
	emitExternalSecret bool
	skipServiceAccount bool
}

type client struct {
	kubeClient          ctrlruntimeclient.Client
	secretmanagerClient secretmanager.ClientInterface
	allSi               *corev1.SecretList
	options
}

func escape(s string) string {
	return strings.ReplaceAll(s, ".", "__d_o_t__")
}

func (c *client) gsmSecretName(clusterSecret *corev1.Secret) string {
	// Use cluster name, namespace and secret name is almost unique identifier.
	// However, if consider GCP allow creating clusters with the same name under
	// different zones, probably will need to add zones to this. Will address if
	// ever needed.
	return fmt.Sprintf("%s__%s__%s", c.clusterContext, clusterSecret.Namespace, escape(clusterSecret.Name))
}

// gatherOptions parses the command-line flags.
func gatherOptions(fs *flag.FlagSet, args ...string) options {
	var o options
	fs.StringVar(&o.project, "project", "", "GCP project used for backing up secrets")
	fs.StringVar(&o.clusterContext, "cluster-context", "", "cluster context name used for backing up secrets, must be full form such as <PROVIDER>_<PROJECT>_<ZONE>_<CLUSTER>")
	fs.StringSliceVar(&o.namespaces, "namespace", []string{}, "namespace to backup, can be passed in repeatedly")
	fs.StringToStringVar(&o.secrets, "secret-name", nil, "namespace:name of secrets to be backed up, in the form of --secret-name=<namespace>=<name>. By default all secrets in the chosen namespace(s) are backed up.")
	fs.BoolVar(&o.update, "update", false, "Controls whether update existing secret or not, if false then secret will only be created")
	fs.BoolVar(&o.dryRun, "dryrun", false, "Controls whether this is dry run or not")
	fs.BoolVar(&o.skipServiceAccount, "skip-sa", true, "Controls whether to skip service account tokens")
	fs.BoolVar(&o.emitExternalSecret, "emit-external-secret", false, "Controls whether to output an ExternalSecret referencing the Secret")
	fs.Parse(args)

	return o
}

// validateFlags validates the command-line flags.
func (o *options) validateFlags() error {
	if len(o.project) == 0 {
		return errors.New("--project must be provided")
	}
	if len(o.clusterContext) == 0 {
		return errors.New("--cluster-context must be provided")
	}
	return nil
}

func newClient(o options) (*client, error) {
	secretmanagerClient, err := secretmanager.NewClient(o.project, o.dryRun)
	if err != nil {
		return nil, fmt.Errorf("failed creating secret manager client: %w", err)
	}

	kubeClient, err := newKubeClients(o.clusterContext)
	if err != nil {
		return nil, fmt.Errorf("failed creating kube client: %w", err)
	}
	return &client{
		kubeClient:          kubeClient,
		secretmanagerClient: secretmanagerClient,
		options:             o,
	}, nil
}

func newKubeClients(clusterContext string) (ctrlruntimeclient.Client, error) {
	loader := clientcmd.NewDefaultClientConfigLoadingRules()

	cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		loader, &clientcmd.ConfigOverrides{
			// Enforcing clusterContext in the full form of cluster context,
			// instead of short names for kubectl.
			Context:        api.Context{Cluster: clusterContext},
			CurrentContext: clusterContext,
		}).ClientConfig()
	if err != nil {
		return nil, fmt.Errorf("failed create rest config: %v ------ Did you supply the full form of cluster context name instead of short hand?", err)
	}
	return ctrlruntimeclient.New(cfg, ctrlruntimeclient.Options{})
}

// process merges secret into a new secret for write.
func (c *client) updateSingleSecret(ctx context.Context, clusterSecret *corev1.Secret) error {
	secretID := c.gsmSecretName(clusterSecret)
	// Google secret manager expects pure string instead of map[string][]byte,
	// so translate *corev1.Secret.Data to map[string]string, this will also be
	// what kubernetes external secret expects.
	stringData := map[string]string{}
	for key, val := range clusterSecret.Data {
		stringData[key] = string(val)
	}
	payload, err := json.Marshal(stringData)
	if err != nil {
		return fmt.Errorf("failed marshal secret %s: %w", clusterSecret.Name, err)
	}
	log := logrus.WithFields(logrus.Fields{
		"project":     c.project,
		"cluster":     c.clusterContext,
		"namespace":   clusterSecret.Namespace,
		"secret-name": clusterSecret.Name,
		"gsm-secret":  secretID,
	})
	if sat := "kubernetes.io/service-account-token"; string(clusterSecret.Type) == sat {
		log.Infof("Skipping: the secret type is %s", sat)
		return nil
	}
	if c.dryRun {
		log.Info("[Dryrun]: backing up secret")
	}

	ss, err := c.secretmanagerClient.ListSecrets(ctx)
	if err != nil {
		return err
	}
	var found bool
	for _, s := range ss {
		if strings.HasSuffix(s.Name, fmt.Sprintf("/%s", secretID)) {
			found = true
		}
	}
	if found && !c.update {
		log.Info("Skipping: the secret already exist and --update is not true")
		return nil
	}
	// Now create or update
	if !found {
		log.Info("Creating secret in GSM")
		if _, err = c.secretmanagerClient.CreateSecret(ctx, secretID); err != nil {
			return err
		}
	}
	log.Info("Create secret version in GSM")
	if err := c.secretmanagerClient.AddSecretVersion(ctx, secretID, payload); err != nil {
		return err
	}
	return c.secretmanagerClient.AddSecretLabel(ctx, secretID, defaultSecretLabels)
}

func (c *client) updateAllSecrets(ctx context.Context, allowed map[string]string) error {
	for _, secret := range c.allSi.Items {
		if allowed != nil {
			if val, ok := allowed[secret.Namespace]; !ok || val != secret.Name {
				continue
			}
		}
		if c.skipServiceAccount && secret.Type == corev1.SecretTypeServiceAccountToken {
			continue
		}
		if err := c.updateSingleSecret(ctx, &secret); err != nil {
			return err
		}
		if c.emitExternalSecret {
			fmt.Printf(`apiVersion: kubernetes-client.io/v1
kind: ExternalSecret
metadata:
  name: "%s"
  namespace: "%s"
spec:
  backendType: gcpSecretsManager
  projectId: "%s"
  dataFrom:
  - "%s" # Secret name in GSM
---
`, secret.Name, secret.Namespace, c.project, c.gsmSecretName(&secret))
		}
	}
	return nil
}

func (c *client) loadClusterSecrets(ctx context.Context) error {
	c.allSi = &corev1.SecretList{}
	var listOptions []ctrlruntimeclient.ListOption
	for _, ns := range c.namespaces {
		listOptions = append(listOptions, &ctrlruntimeclient.ListOptions{
			Namespace: ns,
		})
	}
	return c.kubeClient.List(ctx, c.allSi, listOptions...)
}

func main() {
	o := gatherOptions(flag.NewFlagSet(os.Args[0], flag.ExitOnError), os.Args[1:]...)
	if err := o.validateFlags(); err != nil {
		util.PrintErrAndExit(err)
	}
	defaultSecretLabels["source"] = o.clusterContext

	ctx := context.Background()

	c, err := newClient(o)
	if err != nil {
		logrus.WithError(err).Fatal("Failed creating client")
	}

	if err := c.loadClusterSecrets(ctx); err != nil {
		logrus.WithError(err).Fatal("Failed listing secrets")
	}

	if err := c.updateAllSecrets(ctx, o.secrets); err != nil {
		logrus.WithError(err).Fatal("Failed updating secrets")
	}
}
<|endoftext|>


<commit_before>package archive

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path"

	"pault.ag/go/debian/control"
	"pault.ag/go/debian/dependency"
	"pault.ag/go/debian/transput"
)

func NewHashers(suite Suite, target io.Writer) (io.Writer, []*transput.Hasher, error) {
	return transput.NewHasherWriters(suite.features.Hashes, target)
}

// Archive {{{

type Archive struct {
	root string
}

func NewArchive(root string) Archive {
	return Archive{root: root}
}

func (a Archive) Suite(name string) (*Suite, error) {
	inRelease := path.Join(a.root, "dists", name, "InRelease")
	suite := Suite{Binaries: map[string]Binaries{}}

	/* Feature flags */
	suite.features.Hashes = []string{"sha256", "sha1", "md5"}

	fd, err := os.Open(inRelease)
	if err != nil {
		return nil, err
	}

	defer fd.Close()
	return &suite, control.Unmarshal(&suite, fd)
}

func (a Archive) linkObject(suite Suite, hash *transput.Hasher, targetPath string) error {
	objPath := path.Join("by-hash", hash.Name(), fmt.Sprintf("%x", hash.Sum(nil)))
	_, err := os.Readlink(targetPath)

	if err == nil {
		if err := os.Remove(targetPath); err != nil {
			return err
		}
	}

	if err := os.Symlink(objPath, targetPath); err != nil {
		return err
	}
	return nil
}

func (a Archive) objectPath(suite Suite, hash *transput.Hasher) (string, string) {
	dirPath := path.Join(a.root, "dists", suite.Suite, "by-hash", hash.Name())
	objPath := path.Join(dirPath, fmt.Sprintf("%x", hash.Sum(nil)))
	return dirPath, objPath
}

func (a Archive) objectWriteCloserFor(suite Suite, hash *transput.Hasher) (io.WriteCloser, error) {
	dirPath, objPath := a.objectPath(suite, hash)

	if err := os.MkdirAll(dirPath, 0755); err != nil {
		return nil, err
	}

	fd, err := os.Create(objPath)
	return fd, err
}

func (a Archive) writeObject(suite Suite, data io.Reader) ([]*transput.Hasher, error) {
	target := bytes.Buffer{}
	writer, hashers, err := NewHashers(suite, &target)
	if err != nil {
		return nil, err
	}
	/* So, we have a set of hashers, and the target Buffer */

	c, err := io.Copy(writer, data)
	if err != nil {
		return nil, err
	}

	for _, hash := range hashers {
		if c != hash.Size() {
			return nil, fmt.Errorf("Size mismatch: %s (%d), wrote %d",
				hash.Name(), hash.Size(), c)
		}
	}

	writers := []io.Writer{}
	for _, hash := range hashers {
		fd, err := a.objectWriteCloserFor(suite, hash)
		if err != nil {
			return nil, err
		}
		defer fd.Close()
		writers = append(writers, fd)
	}

	_, err = io.Copy(io.MultiWriter(writers...), &target)
	if err != nil {
		return nil, err
	}

	return hashers, nil
}

func (a Archive) Engross(suite Suite) (*Release, error) {
	engrossedFiles := map[string][]*transput.Hasher{}

	for component, packages := range suite.Binaries {
		for _, arch := range packages.Arches() {
			filePath := path.Join(
				component,
				fmt.Sprintf("binary-%s", arch),
				"Packages",
			)

			target := bytes.Buffer{}
			if err := packages.WriteArchTo(arch, &target); err != nil {
				return nil, err
			}

			hashers, err := a.writeObject(suite, &target)
			if err != nil {
				return nil, err
			}
			engrossedFiles[filePath] = hashers
		}
	}

	ret := Release{
		Description:   suite.Description,
		Origin:        suite.Origin,
		Label:         suite.Label,
		Version:       suite.Version,
		Suite:         suite.Suite,
		Codename:      suite.Codename,
		Components:    suite.Components(),
		Architectures: suite.Arches(),
		MD5Sum:        []control.MD5FileHash{},
		SHA1:          []control.SHA1FileHash{},
		SHA256:        []control.SHA256FileHash{},
	}

	for path, hashers := range engrossedFiles {
		for _, hasher := range hashers {
			fileHash := control.FileHashFromHasher(path, *hasher)
			switch fileHash.Algorithm {
			case "sha1":
				ret.SHA1 = append(ret.SHA1, control.SHA1FileHash{fileHash})
			case "sha256":
				ret.SHA256 = append(ret.SHA256, control.SHA256FileHash{fileHash})
			case "md5":
				ret.MD5Sum = append(ret.MD5Sum, control.MD5FileHash{fileHash})
			default:
				return nil, fmt.Errorf("Unknown hash algorithm: '%s'", fileHash.Algorithm)
			}
		}
	}

	target := bytes.Buffer{}
	if err := control.Marshal(&target, &ret); err != nil {
		return nil, err
	}

	hashers, err := a.writeObject(suite, &target)
	if err != nil {
		return nil, err
	}

	filePath := path.Join(a.root, "dists", suite.Suite, "Release")

	if err := a.linkObject(suite, hashers[0], filePath); err != nil {
		return nil, err
	}

	return &ret, nil
}

// }}}

// Suite {{{

type Suite struct {
	control.Paragraph

	Description string
	Origin      string
	Label       string
	Version     string
	Suite       string `required:"true"`
	Codename    string

	Binaries map[string]Binaries

	features struct {
		Hashes []string
		/* Compressors ... */
	}
}

func (a Suite) Arches() []dependency.Arch {
	arches := map[dependency.Arch]bool{}

	for _, binaries := range a.Binaries {
		for _, arch := range binaries.Arches() {
			arches[arch] = true
		}
	}

	seen := []dependency.Arch{}
	for arch, _ := range arches {
		seen = append(seen, arch)
	}
	return seen
}

func (s Suite) Components() []string {
	components := []string{}
	for component, _ := range s.Binaries {
		components = append(components, component)
	}
	return components
}

func (s Suite) AddPackageTo(component string, pkg Package) {
	if _, ok := s.Binaries[component]; !ok {
		s.Binaries[component] = Binaries{
			arches: map[dependency.Arch][]Package{},
		}
	}
	s.Binaries[component].Add(pkg)
}

// }}}

// Binaries {{{

type Binaries struct {
	arches map[dependency.Arch][]Package
}

func (b Binaries) Add(pkg Package) {
	arch := pkg.Architecture
	b.arches[arch] = append(b.arches[arch], pkg)
}

func (b Binaries) Get(arch dependency.Arch) []Package {
	return b.arches[arch]
}

func (b Binaries) Arches() []dependency.Arch {
	ret := []dependency.Arch{}
	for arch, _ := range b.arches {
		ret = append(ret, arch)
	}
	return ret
}

func (b Binaries) Has(arch dependency.Arch) bool {
	_, ok := b.arches[arch]
	return ok
}

func (b Binaries) WriteArchTo(arch dependency.Arch, out io.Writer) error {
	encoder, err := control.NewEncoder(out)
	if err != nil {
		return err
	}
	if packages, ok := b.arches[arch]; ok {
		for _, pkg := range packages {
			if err := encoder.Encode(pkg); err != nil {
				return err
			}
		}
	} else {
		return fmt.Errorf("No such arch: '%s'", arch)
	}
	return nil
}

// }}}

// vim: foldmethod=marker
<commit_msg>I don't think I like this structure.<commit_after>package archive

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path"

	"pault.ag/go/debian/control"
	"pault.ag/go/debian/dependency"
	"pault.ag/go/debian/transput"
)

func NewHashers(suite Suite, target io.Writer) (io.Writer, []*transput.Hasher, error) {
	return transput.NewHasherWriters(suite.features.Hashes, target)
}

// Archive {{{

type Archive struct {
	root string
}

func NewArchive(root string) Archive {
	return Archive{root: root}
}

func (a Archive) Suite(name string) (*Suite, error) {
	inRelease := path.Join(a.root, "dists", name, "InRelease")
	suite := Suite{Binaries: map[string]Binaries{}}

	/* Feature flags */
	suite.features.Hashes = []string{"sha256", "sha1", "md5"}

	fd, err := os.Open(inRelease)
	if err != nil {
		return nil, err
	}

	defer fd.Close()
	return &suite, control.Unmarshal(&suite, fd)
}

func (a Archive) linkObject(suite Suite, hash *transput.Hasher, targetPath string) error {
	objPath := path.Join("by-hash", hash.Name(), fmt.Sprintf("%x", hash.Sum(nil)))
	_, err := os.Readlink(targetPath)

	if err == nil {
		if err := os.Remove(targetPath); err != nil {
			return err
		}
	}

	dirPath := path.Dir(targetPath)
	if err := os.MkdirAll(dirPath, 0755); err != nil {
		return err
	}

	if err := os.Symlink(objPath, targetPath); err != nil {
		return err
	}
	return nil
}

func (a Archive) objectPath(suite Suite, hash *transput.Hasher) (string, string) {
	dirPath := path.Join(a.root, "dists", suite.Suite, "by-hash", hash.Name())
	objPath := path.Join(dirPath, fmt.Sprintf("%x", hash.Sum(nil)))
	return dirPath, objPath
}

func (a Archive) objectWriteCloserFor(suite Suite, hash *transput.Hasher) (io.WriteCloser, error) {
	dirPath, objPath := a.objectPath(suite, hash)

	if err := os.MkdirAll(dirPath, 0755); err != nil {
		return nil, err
	}

	fd, err := os.Create(objPath)
	return fd, err
}

func (a Archive) writeObject(suite Suite, data io.Reader) ([]*transput.Hasher, error) {
	target := bytes.Buffer{}
	writer, hashers, err := NewHashers(suite, &target)
	if err != nil {
		return nil, err
	}
	/* So, we have a set of hashers, and the target Buffer */

	c, err := io.Copy(writer, data)
	if err != nil {
		return nil, err
	}

	for _, hash := range hashers {
		if c != hash.Size() {
			return nil, fmt.Errorf("Size mismatch: %s (%d), wrote %d",
				hash.Name(), hash.Size(), c)
		}
	}

	writers := []io.Writer{}
	for _, hash := range hashers {
		fd, err := a.objectWriteCloserFor(suite, hash)
		if err != nil {
			return nil, err
		}
		defer fd.Close()
		writers = append(writers, fd)
	}

	_, err = io.Copy(io.MultiWriter(writers...), &target)
	if err != nil {
		return nil, err
	}

	return hashers, nil
}

func (a Archive) Engross(suite Suite) (*Release, error) {
	engrossedFiles := map[string][]*transput.Hasher{}

	for component, packages := range suite.Binaries {
		for _, arch := range packages.Arches() {
			filePath := path.Join(
				component,
				fmt.Sprintf("binary-%s", arch),
				"Packages",
			)

			target := bytes.Buffer{}
			if err := packages.WriteArchTo(arch, &target); err != nil {
				return nil, err
			}

			hashers, err := a.writeObject(suite, &target)
			if err != nil {
				return nil, err
			}
			engrossedFiles[filePath] = hashers
		}
	}

	ret := Release{
		Description:   suite.Description,
		Origin:        suite.Origin,
		Label:         suite.Label,
		Version:       suite.Version,
		Suite:         suite.Suite,
		Codename:      suite.Codename,
		Components:    suite.Components(),
		Architectures: suite.Arches(),
		MD5Sum:        []control.MD5FileHash{},
		SHA1:          []control.SHA1FileHash{},
		SHA256:        []control.SHA256FileHash{},
	}

	for linkPath, hashers := range engrossedFiles {
		for _, hasher := range hashers {
			fileHash := control.FileHashFromHasher(linkPath, *hasher)
			switch fileHash.Algorithm {
			case "sha1":
				ret.SHA1 = append(ret.SHA1, control.SHA1FileHash{fileHash})
			case "sha256":
				ret.SHA256 = append(ret.SHA256, control.SHA256FileHash{fileHash})
			case "md5":
				ret.MD5Sum = append(ret.MD5Sum, control.MD5FileHash{fileHash})
			default:
				return nil, fmt.Errorf("Unknown hash algorithm: '%s'", fileHash.Algorithm)
			}
		}

		if err := a.linkObject(suite, hashers[0], path.Join(
			a.root, "dists", suite.Suite, linkPath,
		)); err != nil {
			return nil, err
		}
	}

	target := bytes.Buffer{}
	if err := control.Marshal(&target, &ret); err != nil {
		return nil, err
	}

	hashers, err := a.writeObject(suite, &target)
	if err != nil {
		return nil, err
	}

	filePath := path.Join(a.root, "dists", suite.Suite, "Release")

	if err := a.linkObject(suite, hashers[0], filePath); err != nil {
		return nil, err
	}

	return &ret, nil
}

// }}}

// Suite {{{

type Suite struct {
	control.Paragraph

	Description string
	Origin      string
	Label       string
	Version     string
	Suite       string `required:"true"`
	Codename    string

	Binaries map[string]Binaries

	features struct {
		Hashes []string
		/* Compressors ... */
	}
}

func (a Suite) Arches() []dependency.Arch {
	arches := map[dependency.Arch]bool{}

	for _, binaries := range a.Binaries {
		for _, arch := range binaries.Arches() {
			arches[arch] = true
		}
	}

	seen := []dependency.Arch{}
	for arch, _ := range arches {
		seen = append(seen, arch)
	}
	return seen
}

func (s Suite) Components() []string {
	components := []string{}
	for component, _ := range s.Binaries {
		components = append(components, component)
	}
	return components
}

func (s Suite) AddPackageTo(component string, pkg Package) {
	if _, ok := s.Binaries[component]; !ok {
		s.Binaries[component] = Binaries{
			arches: map[dependency.Arch][]Package{},
		}
	}
	s.Binaries[component].Add(pkg)
}

// }}}

// Binaries {{{

type Binaries struct {
	arches map[dependency.Arch][]Package
}

func (b Binaries) Add(pkg Package) {
	arch := pkg.Architecture
	b.arches[arch] = append(b.arches[arch], pkg)
}

func (b Binaries) Get(arch dependency.Arch) []Package {
	return b.arches[arch]
}

func (b Binaries) Arches() []dependency.Arch {
	ret := []dependency.Arch{}
	for arch, _ := range b.arches {
		ret = append(ret, arch)
	}
	return ret
}

func (b Binaries) Has(arch dependency.Arch) bool {
	_, ok := b.arches[arch]
	return ok
}

func (b Binaries) WriteArchTo(arch dependency.Arch, out io.Writer) error {
	encoder, err := control.NewEncoder(out)
	if err != nil {
		return err
	}
	if packages, ok := b.arches[arch]; ok {
		for _, pkg := range packages {
			if err := encoder.Encode(pkg); err != nil {
				return err
			}
		}
	} else {
		return fmt.Errorf("No such arch: '%s'", arch)
	}
	return nil
}

// }}}

// vim: foldmethod=marker
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"strings"
	"sync"
)

func (ctx *SyncContext) tryRun(args ...string) bool {
	_, _, retCode, err := ctx.Run(args...)
	if err != nil {
		ctx.Logger().Bail(err)
	}
	return retCode == 0
}

func (ctx *SyncContext) listMissingLocalDeps() []string {
	missing := []string{}
	if !ctx.HasGutInstalled() {
		// This is needed to build & install gut, but then aren't needed after that.
		if !ctx.tryRun("git", "version") {
			missing = append(missing, "git")
		}
	}
	return missing
}

func (ctx *SyncContext) listMissingRemoteDeps() []string {
	missing := []string{}
	if !ctx.tryRun("which", "inotifywait") {
		if !ctx.tryRun("fswatch", "--version") {
			if ctx.IsDarwin() {
				missing = append(missing, "fswatch")
			} else {
				missing = append(missing, "inotify-tools")
			}
		}
	}
	if !ctx.HasGutInstalled() {
		// These are needed to build & install gut, but then aren't needed after that.
		if !ctx.tryRun("autoconf", "--version") {
			missing = append(missing, "autoconf")
		}
		if !ctx.tryRun("make", "--version") || !ctx.tryRun("gcc", "--version") {
			missing = append(missing, "build-essential")
		}
		if ctx.IsLinux() {
			// On errors, just skip this check
			exists, err := ctx.PathExists("/usr/local/include/zlib.h")
			if err == nil {
				if !exists {
					exists, err = ctx.PathExists("/usr/include/zlib.h")
					if err == nil {
						if !exists {
							missing = append(missing, "zlib1g-dev")
						}
					}
				}
			}
		}
		if ctx.IsLinux() && !ctx.tryRun("msgfmt", "--version") {
			missing = append(missing, "gettext")
		}
	}
	return missing
}

func (ctx *SyncContext) CheckLocalDeps() (err error) {
	missing := ctx.listMissingLocalDeps()
	missing = append(missing, ctx.listMissingRemoteDeps()...)
	return ctx.MissingDependency(missing...)
}

func (ctx *SyncContext) CheckRemoteDeps() (err error) {
	missing := ctx.listMissingRemoteDeps()
	return ctx.MissingDependency(missing...)
}

// FWIW, here's the snippet used in python-gut to install inotify-win:
// gut_build.git_clone_update(config.INOTIFY_WIN_REPO_URL, config.INOTIFY_WIN_PATH, config.INOTIFY_WIN_VERSION)
// status.out('(@dim)Building (@r)inotify-win(@dim)...')
// with context.cwd(context.path(config.INOTIFY_WIN_PATH)):
//     context['cmd']['/c', '%WINDIR%\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe /t:exe /out:inotifywait.exe src\\*.cs']()

var mutex sync.Mutex

func (ctx *SyncContext) MissingDependency(names ...string) (err error) {
	if len(names) == 0 {
		return nil
	}
	// Only bombard the user with on MissingDependency dialog at a time, even though we'll do the
	// dependency checks in parallel (for fast startup).
	mutex.Lock()
	defer mutex.Unlock()
	logger := ctx.Logger()
	depsStr := strings.Join(names, " ")
	depsStrCommas := strings.Join(names, logger.Colorify("@(dim:,) "))
	depsStrLong := JoinWithAndAndCommas(names...)
	noun := "dependency"
	quantifier := "A"
	pronoun := "it"
	if len(names) > 1 {
		noun = "dependencies"
		quantifier = "These"
		pronoun = "them"
	}
	depsError := errors.New(fmt.Sprintf("Missing %s: %s", noun, depsStr))
	logger.Printf("@(error:%s %s appear to be missing on) %s@(error::) %s\n", quantifier, noun, ctx.NameAnsi(), depsStrCommas)

	installCmd := ""
	if ctx.IsLinux() {
		installCmd = "sudo apt-get update && sudo apt-get install"
	} else if ctx.IsDarwin() {
		installCmd = "brew install"
	} else {
		return depsError
	}
	installCmd += " " + depsStr
	logger.Printf("I think we might be able to install %s by running this command:\n", pronoun)
	ctx.Logger().Printf("@(dim:$) %s\n", installCmd)
	attempts := 0
	for {
		logger.Printf("Would you like gut-sync to try to install %s [Y/n]? ", pronoun)
		reader := bufio.NewReader(os.Stdin)
		text, _ := reader.ReadString('\n')
		text = strings.TrimSpace(text)
		logger.Replacef("")
		attempts++
		if text == "" || text == "y" || text == "Y" {
			break
		} else if attempts >= 3 || text == "N" || text == "n" || text == "q" || text == "Q" {
			Shutdown("")
		}
	}
	logger.Printf("@(dim:Attempting to install) %s on %s@(dim:...)\n", depsStrLong, ctx.NameAnsi())
	var retCode int
	for _, cmd := range strings.Split(installCmd, " && ") {
		retCode, err = ctx.ShellInteractive(cmd)
		if err != nil {
			logger.Bail(err)
		}
		if retCode != 0 {
			break
		}
	}
	if retCode == 0 {
		logger.Printf("@(dim:Successfully installed) %s@(dim:.)\n", depsStrLong)
		ctx.ResetHasGutInstalled()
	} else {
		logger.Printf("@(error:Installation failed.)\n")
		Shutdown("")
	}
	return nil
}
<commit_msg>Don't panic if `git --version` fails to run. Prompt to install git instead.<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"strings"
	"sync"
)

func (ctx *SyncContext) tryRun(args ...string) bool {
	_, _, retCode, err := ctx.Run(args...)
	if err != nil {
		return false
	}
	return retCode == 0
}

func (ctx *SyncContext) listMissingLocalDeps() []string {
	missing := []string{}
	if !ctx.HasGutInstalled() {
		// This is needed to build & install gut, but then aren't needed after that.
		if !ctx.tryRun("git", "version") {
			missing = append(missing, "git")
		}
	}
	return missing
}

func (ctx *SyncContext) listMissingRemoteDeps() []string {
	missing := []string{}
	if !ctx.tryRun("which", "inotifywait") {
		if !ctx.tryRun("fswatch", "--version") {
			if ctx.IsDarwin() {
				missing = append(missing, "fswatch")
			} else {
				missing = append(missing, "inotify-tools")
			}
		}
	}
	if !ctx.HasGutInstalled() {
		// These are needed to build & install gut, but then aren't needed after that.
		if !ctx.tryRun("autoconf", "--version") {
			missing = append(missing, "autoconf")
		}
		if !ctx.tryRun("make", "--version") || !ctx.tryRun("gcc", "--version") {
			missing = append(missing, "build-essential")
		}
		if ctx.IsLinux() {
			// On errors, just skip this check
			exists, err := ctx.PathExists("/usr/local/include/zlib.h")
			if err == nil {
				if !exists {
					exists, err = ctx.PathExists("/usr/include/zlib.h")
					if err == nil {
						if !exists {
							missing = append(missing, "zlib1g-dev")
						}
					}
				}
			}
		}
		if ctx.IsLinux() && !ctx.tryRun("msgfmt", "--version") {
			missing = append(missing, "gettext")
		}
	}
	return missing
}

func (ctx *SyncContext) CheckLocalDeps() (err error) {
	missing := ctx.listMissingLocalDeps()
	missing = append(missing, ctx.listMissingRemoteDeps()...)
	return ctx.MissingDependency(missing...)
}

func (ctx *SyncContext) CheckRemoteDeps() (err error) {
	missing := ctx.listMissingRemoteDeps()
	return ctx.MissingDependency(missing...)
}

// FWIW, here's the snippet used in python-gut to install inotify-win:
// gut_build.git_clone_update(config.INOTIFY_WIN_REPO_URL, config.INOTIFY_WIN_PATH, config.INOTIFY_WIN_VERSION)
// status.out('(@dim)Building (@r)inotify-win(@dim)...')
// with context.cwd(context.path(config.INOTIFY_WIN_PATH)):
//     context['cmd']['/c', '%WINDIR%\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe /t:exe /out:inotifywait.exe src\\*.cs']()

var mutex sync.Mutex

func (ctx *SyncContext) MissingDependency(names ...string) (err error) {
	if len(names) == 0 {
		return nil
	}
	// Only bombard the user with on MissingDependency dialog at a time, even though we'll do the
	// dependency checks in parallel (for fast startup).
	mutex.Lock()
	defer mutex.Unlock()
	logger := ctx.Logger()
	depsStr := strings.Join(names, " ")
	depsStrCommas := strings.Join(names, logger.Colorify("@(dim:,) "))
	depsStrLong := JoinWithAndAndCommas(names...)
	noun := "dependency"
	quantifier := "A"
	pronoun := "it"
	if len(names) > 1 {
		noun = "dependencies"
		quantifier = "These"
		pronoun = "them"
	}
	depsError := errors.New(fmt.Sprintf("Missing %s: %s", noun, depsStr))
	logger.Printf("@(error:%s %s appear to be missing on) %s@(error::) %s\n", quantifier, noun, ctx.NameAnsi(), depsStrCommas)

	installCmd := ""
	if ctx.IsLinux() {
		installCmd = "sudo apt-get update && sudo apt-get install"
	} else if ctx.IsDarwin() {
		installCmd = "brew install"
	} else {
		return depsError
	}
	installCmd += " " + depsStr
	logger.Printf("I think we might be able to install %s by running this command:\n", pronoun)
	ctx.Logger().Printf("@(dim:$) %s\n", installCmd)
	attempts := 0
	for {
		logger.Printf("Would you like gut-sync to try to install %s [Y/n]? ", pronoun)
		reader := bufio.NewReader(os.Stdin)
		text, _ := reader.ReadString('\n')
		text = strings.TrimSpace(text)
		logger.Replacef("")
		attempts++
		if text == "" || text == "y" || text == "Y" {
			break
		} else if attempts >= 3 || text == "N" || text == "n" || text == "q" || text == "Q" {
			Shutdown("")
		}
	}
	logger.Printf("@(dim:Attempting to install) %s on %s@(dim:...)\n", depsStrLong, ctx.NameAnsi())
	var retCode int
	for _, cmd := range strings.Split(installCmd, " && ") {
		retCode, err = ctx.ShellInteractive(cmd)
		if err != nil {
			logger.Bail(err)
		}
		if retCode != 0 {
			break
		}
	}
	if retCode == 0 {
		logger.Printf("@(dim:Successfully installed) %s@(dim:.)\n", depsStrLong)
		ctx.ResetHasGutInstalled()
	} else {
		logger.Printf("@(error:Installation failed.)\n")
		Shutdown("")
	}
	return nil
}
<|endoftext|>


<commit_before>package ast

import (
	"strconv"
	"strings"
	"time"
)

type Position struct {
	Begin int
	End   int
}

type Value interface {
	Pos() int
	End() int
	Source() string
}

type String struct {
	Position Position
	Value    string
	Data     []rune
}

func (s *String) Pos() int {
	return s.Position.Begin
}

func (s *String) End() int {
	return s.Position.End
}

func (s *String) Source() string {
	return string(s.Data)
}

type Integer struct {
	Position Position
	Value    string
	Data     []rune
}

func (i *Integer) Pos() int {
	return i.Position.Begin
}

func (i *Integer) End() int {
	return i.Position.End
}

func (i *Integer) Source() string {
	return string(i.Data)
}

func (i *Integer) Int() (int64, error) {
	return strconv.ParseInt(i.Value, 0, 64)
}

// Sign returns -1 when the integer is negative, +1 when it is
// explicitly positive, and 0 otherwise.
func (i *Integer) Sign() int {
	if len(i.Value) > 0 {
		if i.Value[0] == '-' {
			return -1
		} else if i.Value[0] == '+' {
			return 1
		}
	}
	return 0
}

type Float struct {
	Position Position
	Value    string
	Data     []rune
}

func (f *Float) Pos() int {
	return f.Position.Begin
}

func (f *Float) End() int {
	return f.Position.End
}

func (f *Float) Source() string {
	return string(f.Data)
}

func (f *Float) Float() (float64, error) {
	return strconv.ParseFloat(f.Value, 64)
}

type Boolean struct {
	Position Position
	Value    string
	Data     []rune
}

func (b *Boolean) Pos() int {
	return b.Position.Begin
}

func (b *Boolean) End() int {
	return b.Position.End
}

func (b *Boolean) Source() string {
	return string(b.Data)
}

func (b *Boolean) Boolean() (bool, error) {
	return strconv.ParseBool(b.Value)
}

type Datetime struct {
	Position Position
	Value    string
	Data     []rune
}

func (d *Datetime) Pos() int {
	return d.Position.Begin
}

func (d *Datetime) End() int {
	return d.Position.End
}

func (d *Datetime) Source() string {
	return string(d.Data)
}

var timeFormats = [...]string{
	"2006-01-02T15:04:05.999999999Z07:00",
	"2006-01-02 15:04:05.999999999Z07:00",
	"2006-01-02T15:04:05.999999999",
	"2006-01-02 15:04:05.999999999",
}

func (d *Datetime) Time() (time.Time, error) {
	switch {
	case !strings.Contains(d.Value, ":"):
		return time.Parse("2006-01-02", d.Value)
	case !strings.Contains(d.Value, "-"):
		return time.Parse("15:04:05.999999999", d.Value)
	default:
		var t time.Time
		var err error
		for _, format := range timeFormats {
			if t, err = time.Parse(format, d.Value); err == nil {
				return t, nil
			}
		}
		return t, err
	}
}

type Array struct {
	Position Position
	Value    []Value
	Data     []rune
}

func (a *Array) Pos() int {
	return a.Position.Begin
}

func (a *Array) End() int {
	return a.Position.End
}

func (a *Array) Source() string {
	return string(a.Data)
}

type TableType uint8

const (
	TableTypeNormal TableType = iota
	TableTypeArray
	TableTypeInline
)

var tableTypes = [...]string{
	"normal",
	"array",
	"inline",
}

func (t TableType) String() string {
	if t > 0 && int(t) < len(tableTypes) {
		return tableTypes[t]
	}
	return "unknown type"
}

type Table struct {
	Position Position
	Line     int
	Name     string
	Fields   map[string]interface{}
	Type     TableType
	Data     []rune
}

func (t *Table) Pos() int {
	return t.Position.Begin
}

func (t *Table) End() int {
	return t.Position.End
}

func (t *Table) Source() string {
	return string(t.Data)
}

type KeyValue struct {
	Key   string
	Value Value
	Line  int
}
<commit_msg>Fix TableType.String<commit_after>package ast

import (
	"strconv"
	"strings"
	"time"
)

type Position struct {
	Begin int
	End   int
}

type Value interface {
	Pos() int
	End() int
	Source() string
}

type String struct {
	Position Position
	Value    string
	Data     []rune
}

func (s *String) Pos() int {
	return s.Position.Begin
}

func (s *String) End() int {
	return s.Position.End
}

func (s *String) Source() string {
	return string(s.Data)
}

type Integer struct {
	Position Position
	Value    string
	Data     []rune
}

func (i *Integer) Pos() int {
	return i.Position.Begin
}

func (i *Integer) End() int {
	return i.Position.End
}

func (i *Integer) Source() string {
	return string(i.Data)
}

func (i *Integer) Int() (int64, error) {
	return strconv.ParseInt(i.Value, 0, 64)
}

// Sign returns -1 when the integer is negative, +1 when it is
// explicitly positive, and 0 otherwise.
func (i *Integer) Sign() int {
	if len(i.Value) > 0 {
		if i.Value[0] == '-' {
			return -1
		} else if i.Value[0] == '+' {
			return 1
		}
	}
	return 0
}

type Float struct {
	Position Position
	Value    string
	Data     []rune
}

func (f *Float) Pos() int {
	return f.Position.Begin
}

func (f *Float) End() int {
	return f.Position.End
}

func (f *Float) Source() string {
	return string(f.Data)
}

func (f *Float) Float() (float64, error) {
	return strconv.ParseFloat(f.Value, 64)
}

type Boolean struct {
	Position Position
	Value    string
	Data     []rune
}

func (b *Boolean) Pos() int {
	return b.Position.Begin
}

func (b *Boolean) End() int {
	return b.Position.End
}

func (b *Boolean) Source() string {
	return string(b.Data)
}

func (b *Boolean) Boolean() (bool, error) {
	return strconv.ParseBool(b.Value)
}

type Datetime struct {
	Position Position
	Value    string
	Data     []rune
}

func (d *Datetime) Pos() int {
	return d.Position.Begin
}

func (d *Datetime) End() int {
	return d.Position.End
}

func (d *Datetime) Source() string {
	return string(d.Data)
}

var timeFormats = [...]string{
	"2006-01-02T15:04:05.999999999Z07:00",
	"2006-01-02 15:04:05.999999999Z07:00",
	"2006-01-02T15:04:05.999999999",
	"2006-01-02 15:04:05.999999999",
}

func (d *Datetime) Time() (time.Time, error) {
	switch {
	case !strings.Contains(d.Value, ":"):
		return time.Parse("2006-01-02", d.Value)
	case !strings.Contains(d.Value, "-"):
		return time.Parse("15:04:05.999999999", d.Value)
	default:
		var t time.Time
		var err error
		for _, format := range timeFormats {
			if t, err = time.Parse(format, d.Value); err == nil {
				return t, nil
			}
		}
		return t, err
	}
}

type Array struct {
	Position Position
	Value    []Value
	Data     []rune
}

func (a *Array) Pos() int {
	return a.Position.Begin
}

func (a *Array) End() int {
	return a.Position.End
}

func (a *Array) Source() string {
	return string(a.Data)
}

type TableType uint8

const (
	TableTypeNormal TableType = iota
	TableTypeArray
	TableTypeInline
)

var tableTypes = [...]string{
	"normal",
	"array",
	"inline",
}

func (t TableType) String() string {
	if int(t) < len(tableTypes) {
		return tableTypes[t]
	}
	return "unknown type"
}

type Table struct {
	Position Position
	Line     int
	Name     string
	Fields   map[string]interface{}
	Type     TableType
	Data     []rune
}

func (t *Table) Pos() int {
	return t.Position.Begin
}

func (t *Table) End() int {
	return t.Position.End
}

func (t *Table) Source() string {
	return string(t.Data)
}

type KeyValue struct {
	Key   string
	Value Value
	Line  int
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/CapillarySoftware/gostat/aggregator"
	"github.com/CapillarySoftware/gostat/bucketer"
	"github.com/CapillarySoftware/gostat/repo"
	"github.com/CapillarySoftware/gostat/socketApi"
	"github.com/CapillarySoftware/gostat/stat"
	log "github.com/cihub/seelog"
	nano "github.com/op/go-nanomsg"
	"math/rand"
	"os"
	"os/signal"
	"time"
)

func main() {
	go socketApi.SocketApiServer()

	stats := make(chan *stat.Stat)           // stats received from producers
	rawStats := make(chan *stat.Stat)        // raw stats to be archived
	bucketedStats := make(chan []*stat.Stat) // raw bucketed (non-aggregated) stats are output here
	shutdownBucketer := make(chan bool)      // used to signal the bucketer we are done
	shutdownListener := make(chan bool)      // used to signal the socket listener we are done
	shutdownAggregator := make(chan bool)    // used to signal the aggregator we are done
	shutdownStatRepo := make(chan bool)      // used to signal the stat repo we are done

	installCtrlCHandler(shutdownBucketer, shutdownListener, shutdownAggregator, shutdownStatRepo)

	// create and start a Bucketer
	b := bucketer.NewBucketer(stats, bucketedStats, shutdownBucketer)
	go b.Run(time.Second * 5)

	// create and start a stat repo
	r := repo.NewStatRepo(rawStats, shutdownStatRepo)
	go r.Run()

	// TODO: replace this simulation of an Aggregator with a real one
	go func(bucketed <-chan []*stat.Stat, shutdown <-chan bool) {
		for {
			select {
			case bucket := <-bucketed:
				log.Debugf("aggregator simulation got stats bucket with length %d", len(bucket))
				for _, stat := range bucket {
					log.Debugf("aggregator sim: %#v", stat)
				}
				a := aggregator.Aggregate(bucket)
				log.Debugf("aggregator sim aggregate: %#v", a)
			case <-shutdown:
				log.Info("aggregator simulation shutting down")
				break
			}
		}
	}(bucketedStats, shutdownAggregator)

	// start a socket listener
	go bindSocketListener(stats, shutdownListener)

	// TODO: remove this simulation of stats when the socket listener is really processing data
	for true {
		<-time.After(time.Second * time.Duration(rand.Intn(3))) // sleep 0-3 seconds

		// create a stat randomly named "stat1 ... stat10" with a random value between 1-100
		stat := stat.Stat{Name: fmt.Sprintf("stat%v", (rand.Intn(9) + 1)), Timestamp: time.Now().UTC(), Value: float64(rand.Intn(99) + 1)}
		stats <- &stat    // send it to the Bucketer
		rawStats <- &stat // for archiving
	}
}

// installCtrlCHandler starts a goroutine that will signal the workers when it's time
// to shut down
func installCtrlCHandler(shutdown ...chan<- bool) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	go func() {
		for sig := range c {
			log.Infof(" captured %v, stopping stats collection and exiting...\n", sig)
			for _, s := range shutdown {
				s <- true
			}
			<-time.After(time.Second * 5) // wait for a clean shutdown, TODO: wait on signal from all routines
			log.Info("Done")
			os.Exit(1)
		}
	}()
}

func bindSocketListener(stats chan<- *stat.Stat, shutdown <-chan bool) {
	var (
		msg  []byte
		err  error
		done = false
	)
	socket, err := nano.NewPullSocket()

	if nil != err {
		log.Error(err)
	}

	socket.SetRecvTimeout(time.Second)

	defer socket.Close()
	_, err = socket.Bind("tcp://*:2025")
	if nil != err {
		log.Error(err)
	}
	log.Info("Ready to receive data")

	for !done {
		msg, err = socket.Recv(0) //blocking
		if nil != err {
			log.Error(err)
		}
		if nil != msg {
			// TODO: new up a Stat and send it on the stats channel
			log.Debug("Received message: ", msg)
		}

		select {
		case done = <-shutdown:
		default:
			break
		}
	}

	log.Info("Exiting socket listener")
}
<commit_msg>added -sim option, silence "resource not available" message<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/CapillarySoftware/gostat/aggregator"
	"github.com/CapillarySoftware/gostat/bucketer"
	"github.com/CapillarySoftware/gostat/repo"
	"github.com/CapillarySoftware/gostat/socketApi"
	"github.com/CapillarySoftware/gostat/stat"
	log "github.com/cihub/seelog"
	nano "github.com/op/go-nanomsg"
	"math/rand"
	"os"
	"os/signal"
	"time"
)

func main() {
	simulateData := flag.Bool("sim", false, "randomly generate and insert test data")
	flag.Parse()

	go socketApi.SocketApiServer()

	stats := make(chan *stat.Stat)           // stats received from producers
	rawStats := make(chan *stat.Stat)        // raw stats to be archived
	bucketedStats := make(chan []*stat.Stat) // raw bucketed (non-aggregated) stats are output here
	shutdownBucketer := make(chan bool)      // used to signal the bucketer we are done
	shutdownListener := make(chan bool)      // used to signal the socket listener we are done
	shutdownAggregator := make(chan bool)    // used to signal the aggregator we are done
	shutdownStatRepo := make(chan bool)      // used to signal the stat repo we are done

	installCtrlCHandler(shutdownBucketer, shutdownListener, shutdownAggregator, shutdownStatRepo)

	// create and start a Bucketer
	b := bucketer.NewBucketer(stats, bucketedStats, shutdownBucketer)
	go b.Run(time.Second * 5)

	// create and start a stat repo
	r := repo.NewStatRepo(rawStats, shutdownStatRepo)
	go r.Run()

	// TODO: replace this simulation of an Aggregator with a real one
	go func(bucketed <-chan []*stat.Stat, shutdown <-chan bool) {
		for {
			select {
			case bucket := <-bucketed:
				log.Debugf("aggregator simulation got stats bucket with length %d", len(bucket))
				for _, stat := range bucket {
					log.Debugf("aggregator sim: %#v", stat)
				}
				a := aggregator.Aggregate(bucket)
				log.Debugf("aggregator sim aggregate: %#v", a)
			case <-shutdown:
				log.Info("aggregator simulation shutting down")
				break
			}
		}
	}(bucketedStats, shutdownAggregator)

	// start a socket listener
	go bindSocketListener(stats, shutdownListener)

	// TODO: remove this simulation of stats when the socket listener is really processing data
	for true {
		<-time.After(time.Second * time.Duration(rand.Intn(3))) // sleep 0-3 seconds

		if *simulateData {
			// create a stat randomly named "stat1 ... stat10" with a random value between 1-100
			stat := stat.Stat{Name: fmt.Sprintf("stat%v", (rand.Intn(9) + 1)), Timestamp: time.Now().UTC(), Value: float64(rand.Intn(99) + 1)}
			log.Debug("Generated simulated stat: ", stat)
			stats <- &stat    // send it to the Bucketer
			rawStats <- &stat // for archiving
		}
	}
}

// installCtrlCHandler starts a goroutine that will signal the workers when it's time
// to shut down
func installCtrlCHandler(shutdown ...chan<- bool) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	go func() {
		for sig := range c {
			log.Infof(" captured %v, stopping stats collection and exiting...\n", sig)
			for _, s := range shutdown {
				s <- true
			}
			<-time.After(time.Second * 5) // wait for a clean shutdown, TODO: wait on signal from all routines
			log.Info("Done")
			os.Exit(1)
		}
	}()
}

func bindSocketListener(stats chan<- *stat.Stat, shutdown <-chan bool) {
	var (
		msg  []byte
		err  error
		done = false
	)
	socket, err := nano.NewPullSocket()

	if nil != err {
		log.Error(err)
	}

	socket.SetRecvTimeout(time.Second)

	defer socket.Close()
	_, err = socket.Bind("tcp://*:2025")
	if nil != err {
		log.Error(err)
	}
	log.Info("Ready to receive data")

	for !done {
		msg, err = socket.Recv(0) //blocking
		if nil != err && err.Error() != "resource temporarily unavailable" {
			log.Error(err)
		}

		if nil != msg {
			// TODO: new up a Stat and send it on the stats channel
			log.Debug("Received message: ", msg)
		}

		select {
		case done = <-shutdown:
		default:
			break
		}
	}

	log.Info("Exiting socket listener")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

const keyword = "snap-plugin"

var (
	personalAccessToken string
	// issuesCollection    allIssues
	org string
)

// TokenSource is an encapsulation of the AccessToken string
type TokenSource struct {
	AccessToken string
}

// RepositoryContentGetOptions represents an optional ref parameter
type RepositoryContentGetOptions struct {
	Ref string `url:"ref,omitempty"`
}

// Token authenticates via oauth
func (t *TokenSource) Token() (*oauth2.Token, error) {
	token := &oauth2.Token{
		AccessToken: t.AccessToken,
	}
	return token, nil
}

func main() {
	org = os.Getenv("GH_ORG")
	personalAccessToken = os.Getenv("GITHUB_ACCESS_TOKEN")

	if len(personalAccessToken) == 0 {
		log.Fatal("Before you can use this you must set the GITHUB_ACCESS_TOKEN environment variable.")
	}
	if len(org) < 1 {
		log.Fatal("You need to have a single organization name set to GH_ORG environmental variable.")
	}

	tokenSource := &TokenSource{
		AccessToken: personalAccessToken,
	}
	oauthClient := oauth2.NewClient(oauth2.NoContext, tokenSource)
	client := github.NewClient(oauthClient) // authenticated to GitHub here

	opt := &github.RepositoryListByOrgOptions{
		ListOptions: github.ListOptions{PerPage: 10},
	}
	// get all pages of results
	var allRepos []github.Repository
	for {
		repos, resp, err := client.Repositories.ListByOrg(org, opt)
		if err != nil {
			log.Fatal(err)
		}
		allRepos = append(allRepos, repos...)
		if resp.NextPage == 0 {
			break
		}
		opt.ListOptions.Page = resp.NextPage
	}

	for _, rp := range allRepos {
		repo := *rp.Name
		//owner := *rp.Owner.Login

		if strings.Contains(repo, keyword) {
			encodedText, _, err := client.Repositories.GetReadme(org, repo, &github.RepositoryContentGetOptions{})
			if err != nil {
				log.Printf("Repositories.GetReadme returned error: %v", err)
			}
			text, err := encodedText.Decode()
			if err != nil {
				log.Printf("Decoding failed: %v", err)
			}
			readme := string(text)
			fmt.Printf("Found a readme for %v", readme)
			// look up each repo and say if you find a README
		}
	}
}
<commit_msg>Map readme name to corresponding readmes<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

const keyword = "snap-plugin"

var (
	personalAccessToken string
	// issuesCollection    allIssues
	org string
)

// TokenSource is an encapsulation of the AccessToken string
type TokenSource struct {
	AccessToken string
}

// RepositoryContentGetOptions represents an optional ref parameter
type RepositoryContentGetOptions struct {
	Ref string `url:"ref,omitempty"`
}

// Token authenticates via oauth
func (t *TokenSource) Token() (*oauth2.Token, error) {
	token := &oauth2.Token{
		AccessToken: t.AccessToken,
	}
	return token, nil
}

func main() {
	org = os.Getenv("GH_ORG")
	personalAccessToken = os.Getenv("GITHUB_ACCESS_TOKEN")

	if len(personalAccessToken) == 0 {
		log.Fatal("Before you can use this you must set the GITHUB_ACCESS_TOKEN environment variable.")
	}
	if len(org) < 1 {
		log.Fatal("You need to have a single organization name set to GH_ORG environmental variable.")
	}

	tokenSource := &TokenSource{
		AccessToken: personalAccessToken,
	}
	oauthClient := oauth2.NewClient(oauth2.NoContext, tokenSource)
	client := github.NewClient(oauthClient) // authenticated to GitHub here

	opt := &github.RepositoryListByOrgOptions{
		ListOptions: github.ListOptions{PerPage: 10},
	}
	// get all pages of results
	var allRepos []github.Repository

	// initialize the map of all Readmes
	readmeLibrary := make(map[string]string)
	for {
		repos, resp, err := client.Repositories.ListByOrg(org, opt)
		if err != nil {
			log.Fatal(err)
		}
		allRepos = append(allRepos, repos...)
		if resp.NextPage == 0 {
			break
		}
		opt.ListOptions.Page = resp.NextPage
	}

	for _, rp := range allRepos {
		repo := *rp.Name
		//owner := *rp.Owner.Login

		if strings.Contains(repo, keyword) {
			encodedText, _, err := client.Repositories.GetReadme(org, repo, &github.RepositoryContentGetOptions{})
			if err != nil {
				log.Printf("Repositories.GetReadme returned error: %v\n", err)
			}
			if encodedText == nil {
				log.Printf("The returned text from %v is nil. Are you sure it exists?\n", repo)
			}
			text, err := encodedText.Decode()
			if err != nil {
				log.Printf("Decoding failed: %v\n", err)
			}
			readme := string(text)
			fmt.Printf("Found a readme for %v\n", repo)
			// look up each repo and say if you find a README
			readmeLibrary[repo] = readme

		}
	}
	var i int
	for range readmeLibrary {
		i++
	}
	fmt.Printf("Number of readmes is: %v\n", i)
}
<|endoftext|>


<commit_before>package main

import (
	"bitbucket.org/anacrolix/dms/dlna/dms"
	"github.com/mattn/go-gtk/gdk"
	"github.com/mattn/go-gtk/gtk"
	"log"
	"os"
	"runtime"
)

func main() {
	runtime.LockOSThread()
	gtk.Init(&os.Args)
	gdk.ThreadsInit()

	window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
	window.SetTitle("DMS GUI")
	window.Connect("destroy", gtk.MainQuit)

	vbox := gtk.NewVBox(false, 0)
	window.Add(vbox)

	hbox := gtk.NewHBox(false, 0)
	vbox.PackStart(hbox, false, true, 0)

	hbox.PackStart(gtk.NewLabel("Shared directory: "), false, true, 0)

	dialog := gtk.NewFileChooserDialog(
		"Select directory to share", window, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
		gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
		gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)

	button := gtk.NewFileChooserButtonWithDialog(dialog)
	hbox.Add(button)

	logView := gtk.NewTextView()
	logView.SetEditable(false)
	logView.ModifyFontEasy("monospace")
	logView.SetWrapMode(gtk.WRAP_WORD_CHAR)
	logViewScroller := gtk.NewScrolledWindow(nil, nil)
	logViewScroller.Add(logView)
	logViewScroller.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
	vbox.PackEnd(logViewScroller, true, true, 0)

	getPath := func() string {
		return button.GetFilename()
	}

	appendToLog := func(text string) {
		var endIter gtk.GtkTextIter
		gdk.ThreadsEnter()
		logBuffer := logView.GetBuffer()
		logBuffer.GetEndIter(&endIter)
		logBuffer.Insert(&endIter, text)
		logView.ScrollToIter(&endIter, 0, false, 0, 0)
		gdk.ThreadsLeave()
	}

	window.ShowAll()
	if dialog.Run() != gtk.RESPONSE_ACCEPT {
		return
	}
	go func() {
		logReader, logWriter := io.Pipe()
		go func() {
			var buf [128]byte
			for {
				n, err := logReader.Read(buf[:])
				appendToLog(string(buf[:n]))
				if err != nil {
					panic(err)
				}
			}
		}()
		dmsLogger := log.New(logWriter, "", 0)
		log.SetOutput(logWriter)
		dmsServer, err := dms.New(getPath(), dmsLogger)
		if err != nil {
			log.Fatalln(err)
		}
		defer dmsServer.Close()
		runtime.LockOSThread()
		gdk.ThreadsEnter()
		button.Connect("selection-changed", func() {
			dmsServer.SetRootPath(getPath())
		})
		gdk.ThreadsLeave()
		runtime.UnlockOSThread()
		dmsServer.Serve()
	}()
	gtk.Main()
	runtime.UnlockOSThread()
}
<commit_msg>Remove logging in dms gtk window, and adjust to new dms API<commit_after>package main

import (
	"bitbucket.org/anacrolix/dms/dlna/dms"
	"github.com/mattn/go-gtk/gdk"
	"github.com/mattn/go-gtk/gtk"
	"log"
	"os"
	"runtime"
)

func main() {
	runtime.LockOSThread()
	gtk.Init(&os.Args)
	gdk.ThreadsInit()

	window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
	window.SetTitle("DMS GUI")
	window.Connect("destroy", gtk.MainQuit)

	vbox := gtk.NewVBox(false, 0)
	window.Add(vbox)

	hbox := gtk.NewHBox(false, 0)
	vbox.PackStart(hbox, false, true, 0)

	hbox.PackStart(gtk.NewLabel("Shared directory: "), false, true, 0)

	dialog := gtk.NewFileChooserDialog(
		"Select directory to share", window, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
		gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
		gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)

	button := gtk.NewFileChooserButtonWithDialog(dialog)
	hbox.Add(button)

	logView := gtk.NewTextView()
	logView.SetEditable(false)
	logView.ModifyFontEasy("monospace")
	logView.SetWrapMode(gtk.WRAP_WORD_CHAR)
	logViewScroller := gtk.NewScrolledWindow(nil, nil)
	logViewScroller.Add(logView)
	logViewScroller.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
	vbox.PackEnd(logViewScroller, true, true, 0)

	getPath := func() string {
		return button.GetFilename()
	}

	window.ShowAll()
	if dialog.Run() != gtk.RESPONSE_ACCEPT {
		return
	}
	go func() {
		dmsServer := dms.Server{
			RootObjectPath: getPath(),
		}
		if err := dmsServer.Serve(); err != nil {
			log.Fatalln(err)
		}
		defer dmsServer.Close()
		runtime.LockOSThread()
		gdk.ThreadsEnter()
		button.Connect("selection-changed", func() {
			dmsServer.RootObjectPath = getPath()
		})
		gdk.ThreadsLeave()
		runtime.UnlockOSThread()
		dmsServer.Serve()
	}()
	gtk.Main()
	runtime.UnlockOSThread()
}
<|endoftext|>


<commit_before>// Copyright 2014 The Dename 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 main

import (
	"encoding/base64"
	"fmt"
	"github.com/andres-erbsen/dename/client"
	"github.com/andres-erbsen/dename/dnmgr"
	"io/ioutil"
	"os"
)

func usageAndExit() {
	fmt.Fprintf(os.Stderr, `Missing arguments. Usage:
To create a new profile:
  %s init <name> <invite>
To set the value of a field on an existing profile:
  %s set <name> <field> [value]
If a value is not provided in the arguments, it will be read from standard input.
This is(useful if the value contains nul characters.
`, os.Args[0], os.Args[0])
	os.Exit(2)
}

func main() {
	if len(os.Args) < 2 {
		usageAndExit()
	}
	args := os.Args[2:]
	switch os.Args[1] {
	case "init":
		if len(args) < 2 {
			usageAndExit()
		}
		name := args[0]
		invite, err := base64.StdEncoding.DecodeString(args[1])
		if err != nil {
			fmt.Fprintf(os.Stderr, "invalid invite (base64 decoding failed: %s)\n", err)
			os.Exit(1)
		}
		profile, sk, err := client.NewProfile(nil, nil)
		if err != nil {
			panic(err)
		}
		if err := dnmgr.Register(sk, profile, name, invite, "", nil); err != nil {
			fmt.Fprintf(os.Stderr, "registration failed: %s\n", err)
			os.Exit(1)
		}
	case "set":
		var name, fieldName string
		var value []byte
		if len(args) == 2 || len(args) == 3 {
			name, fieldName = args[0], args[1]
			if len(args) == 3 {
				value = []byte(args[2])
			}
		} else {
			usageAndExit()
		}
		if value == nil {
			var err error
			value, err = ioutil.ReadAll(os.Stdin)
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to read input: %s\n", err)
				os.Exit(1)
			}
		}
		fieldNumber, err := client.FieldByName(fieldName)
		if err != nil {
			fmt.Fprintf(os.Stderr, "unknown field \"%s\" (%s)\n", fieldName, err)
			os.Exit(1)
		}
		if err := dnmgr.SetProfileField(name, fieldNumber, value, "", nil); err != nil {
			fmt.Fprintf(os.Stderr, "operation failed: %s\n", err)
			os.Exit(1)
		}
	}
}
<commit_msg>Use os.Exit() instead of panic().<commit_after>// Copyright 2014 The Dename 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 main

import (
	"encoding/base64"
	"fmt"
	"github.com/andres-erbsen/dename/client"
	"github.com/andres-erbsen/dename/dnmgr"
	"io/ioutil"
	"os"
)

func usageAndExit() {
	fmt.Fprintf(os.Stderr, `Missing arguments. Usage:
To create a new profile:
  %s init <name> <invite>
To set the value of a field on an existing profile:
  %s set <name> <field> [value]
If a value is not provided in the arguments, it will be read from standard input.
This is(useful if the value contains nul characters.
`, os.Args[0], os.Args[0])
	os.Exit(2)
}

func main() {
	if len(os.Args) < 2 {
		usageAndExit()
	}
	args := os.Args[2:]
	switch os.Args[1] {
	case "init":
		if len(args) < 2 {
			usageAndExit()
		}
		name := args[0]
		invite, err := base64.StdEncoding.DecodeString(args[1])
		if err != nil {
			fmt.Fprintf(os.Stderr, "invalid invite (base64 decoding failed: %s)\n", err)
			os.Exit(1)
		}
		profile, sk, err := client.NewProfile(nil, nil)
		if err != nil {
			fmt.Fprintf(os.Stderr, "creating a new profile failed: %s\n", err);
			os.Exit(1)
		}
		if err := dnmgr.Register(sk, profile, name, invite, "", nil); err != nil {
			fmt.Fprintf(os.Stderr, "registration failed: %s\n", err)
			os.Exit(1)
		}
	case "set":
		var name, fieldName string
		var value []byte
		if len(args) == 2 || len(args) == 3 {
			name, fieldName = args[0], args[1]
			if len(args) == 3 {
				value = []byte(args[2])
			}
		} else {
			usageAndExit()
		}
		if value == nil {
			var err error
			value, err = ioutil.ReadAll(os.Stdin)
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to read input: %s\n", err)
				os.Exit(1)
			}
		}
		fieldNumber, err := client.FieldByName(fieldName)
		if err != nil {
			fmt.Fprintf(os.Stderr, "unknown field \"%s\" (%s)\n", fieldName, err)
			os.Exit(1)
		}
		if err := dnmgr.SetProfileField(name, fieldNumber, value, "", nil); err != nil {
			fmt.Fprintf(os.Stderr, "operation failed: %s\n", err)
			os.Exit(1)
		}
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/awslabs/aws-sdk-go/aws/credentials/ec2rolecreds"
	"github.com/bobtfish/AWSnycast/healthcheck"
	"log"
	"net"
	"net/http"
	"strings"
	"time"
)

type MyEC2Conn interface {
	CreateRoute(*ec2.CreateRouteInput) (*ec2.CreateRouteOutput, error)
	ReplaceRoute(*ec2.ReplaceRouteInput) (*ec2.ReplaceRouteOutput, error)
	DescribeRouteTables(*ec2.DescribeRouteTablesInput) (*ec2.DescribeRouteTablesOutput, error)
	DeleteRoute(*ec2.DeleteRouteInput) (*ec2.DeleteRouteOutput, error)
}

type MetadataFetcher interface {
	Available() bool
	GetMetadata(string) (string, error)
}

func NewMetadataFetcher(debug bool) MetadataFetcher {
	c := ec2metadata.Config{}
	if debug {
		c.LogLevel = aws.LogLevel(aws.LogDebug)
	}
	return ec2metadata.New(&c)
}

type ManageRoutesSpec struct {
	Cidr            string `yaml:"cidr"`
	Instance        string `yaml:"instance"`
	HealthcheckName string `yaml:"healthcheck"`
	IfUnhealthy     bool   `yaml:"if_unhealthy"`
}

func (r *ManageRoutesSpec) Default(instance string) {
	if !strings.Contains(r.Cidr, "/") {
		r.Cidr = fmt.Sprintf("%s/32", r.Cidr)
	}
	if r.Instance == "" {
		r.Instance = "SELF"
	}
	if r.Instance == "SELF" {
		r.Instance = instance
	}
}
func (r *ManageRoutesSpec) Validate(name string, healthchecks map[string]*healthcheck.Healthcheck) error {
	if r.Cidr == "" {
		return errors.New(fmt.Sprintf("cidr is not defined in %s", name))
	}
	if _, _, err := net.ParseCIDR(r.Cidr); err != nil {
		return errors.New(fmt.Sprintf("Could not parse %s in %s", err.Error(), name))
	}
	if r.HealthcheckName != "" {
		if _, ok := healthchecks[r.HealthcheckName]; !ok {
			return errors.New(fmt.Sprintf("Route table %s, upsert %s cannot find healthcheck '%s'", name, r.Cidr, r.HealthcheckName))
		}
	}
	return nil
}

type RouteTableFetcher interface {
	GetRouteTables() ([]*ec2.RouteTable, error)
	ManageInstanceRoute(ec2.RouteTable, ManageRoutesSpec, bool) error
}

type RouteTableFetcherEC2 struct {
	Region string
	conn   MyEC2Conn
}

func getCreateRouteInput(rtb ec2.RouteTable, cidr string, instance string) ec2.CreateRouteInput {
	return ec2.CreateRouteInput{
		RouteTableId:         rtb.RouteTableId,
		DestinationCidrBlock: aws.String(cidr),
		InstanceId:           aws.String(instance),
	}
}

func (r RouteTableFetcherEC2) ManageInstanceRoute(rtb ec2.RouteTable, rs ManageRoutesSpec, noop bool) error {
	if err := r.ReplaceInstanceRoute(rtb, rs.Cidr, rs.Instance, rs.IfUnhealthy, noop); err != nil {
		if err.Error() != "Never found CIDR in route table to replace" {
			return err
		}
	} else {
		return nil
	}
	opts := getCreateRouteInput(rtb, rs.Cidr, rs.Instance)

	log.Printf("[INFO] Creating route for %s: %#v", *rtb.RouteTableId, opts)
	if !noop {
		if _, err := r.conn.CreateRoute(&opts); err != nil {
			return err
		}
	}
	return nil
}

func (r RouteTableFetcherEC2) ReplaceInstanceRoute(rtb ec2.RouteTable, cidr string, instance string, ifUnhealthy bool, noop bool) error {
	for _, route := range rtb.Routes {
		if *(route.DestinationCidrBlock) == cidr {
			if route.InstanceId != nil && *(route.InstanceId) == instance {
				log.Printf("Skipping doing anything, %s is already routed via %s", cidr, instance)
				return nil
			}
			params := &ec2.ReplaceRouteInput{
				DestinationCidrBlock: aws.String(cidr),
				RouteTableId:         rtb.RouteTableId,
				InstanceId:           aws.String(instance),
			}
			if ifUnhealthy && *(route.State) == "active" {
				log.Printf("Not replacing route, as current route is active/healthy")
				return nil
			}
			if !noop {
				resp, err := r.conn.ReplaceRoute(params)
				if err != nil {
					// Print the error, cast err to awserr.Error to get the Code and
					// Message from an error.
					fmt.Println(err.Error())
					return err
				}
				fmt.Println(resp)
			}
			return nil
		}
	}
	return errors.New("Never found CIDR in route table to replace")
}

func (r RouteTableFetcherEC2) GetRouteTables() ([]*ec2.RouteTable, error) {
	resp, err := r.conn.DescribeRouteTables(&ec2.DescribeRouteTablesInput{})
	if err != nil {
		log.Printf("Error on DescribeRouteTables: %s", err)
		return []*ec2.RouteTable{}, err
	}
	return resp.RouteTables, nil
}

func NewRouteTableFetcher(region string, debug bool) (RouteTableFetcher, error) {
	r := RouteTableFetcherEC2{}
	providers := []credentials.Provider{
		&credentials.EnvProvider{},
		&ec2rolecreds.EC2RoleProvider{
			Client: ec2metadata.New(&ec2metadata.Config{
				HTTPClient: &http.Client{
					Timeout: 2 * time.Second,
				},
			}),
		},
	}
	cred := credentials.NewChainCredentials(providers)
	_, credErr := cred.Get()
	if credErr != nil {
		return r, credErr
	}
	awsConfig := &aws.Config{
		Credentials: cred,
		Region:      aws.String(region),
		MaxRetries:  aws.Int(3),
	}
	iamconn := iam.New(awsConfig)
	_, err := iamconn.GetUser(nil)
	if awsErr, ok := err.(awserr.Error); ok {
		if awsErr.Code() == "SignatureDoesNotMatch" {
			return r, fmt.Errorf("Failed authenticating with AWS: please verify credentials")
		}
	}
	r.conn = ec2.New(awsConfig)
	return r, nil
}

type RouteTableFilter interface {
	Keep(*ec2.RouteTable) bool
}

type RouteTableFilterAlways struct{}

func (fs RouteTableFilterAlways) Keep(rt *ec2.RouteTable) bool {
	return false
}

type RouteTableFilterNever struct{}

func (fs RouteTableFilterNever) Keep(rt *ec2.RouteTable) bool {
	return true
}

type RouteTableFilterAnd struct {
	RouteTableFilters []RouteTableFilter
}

func (fs RouteTableFilterAnd) Keep(rt *ec2.RouteTable) bool {
	for _, f := range fs.RouteTableFilters {
		if !f.Keep(rt) {
			return false
		}
	}
	return true
}

type RouteTableFilterOr struct {
	RouteTableFilters []RouteTableFilter
}

func (fs RouteTableFilterOr) Keep(rt *ec2.RouteTable) bool {
	for _, f := range fs.RouteTableFilters {
		if f.Keep(rt) {
			return true
		}
	}
	return false
}

type RouteTableFilterMain struct{}

func (fs RouteTableFilterMain) Keep(rt *ec2.RouteTable) bool {
	for _, a := range rt.Associations {
		if *(a.Main) {
			return true
		}
	}
	return false
}

func FilterRouteTables(f RouteTableFilter, tables []*ec2.RouteTable) []*ec2.RouteTable {
	out := make([]*ec2.RouteTable, 0, len(tables))
	for _, rtb := range tables {
		if f.Keep(rtb) {
			out = append(out, rtb)
		}
	}
	return out
}

func RouteTableForSubnet(subnet string, tables []*ec2.RouteTable) *ec2.RouteTable {
	subnet_rtb := FilterRouteTables(RouteTableFilterSubnet{SubnetId: subnet}, tables)
	if len(subnet_rtb) == 0 {
		main_rtbs := FilterRouteTables(RouteTableFilterMain{}, tables)
		if len(main_rtbs) == 0 {
			return nil
		}
		return main_rtbs[0]
	}
	return subnet_rtb[0]
}

type RouteTableFilterSubnet struct {
	SubnetId string
}

func (fs RouteTableFilterSubnet) Keep(rt *ec2.RouteTable) bool {
	for _, a := range rt.Associations {
		if a.SubnetId != nil && *(a.SubnetId) == fs.SubnetId {
			return true
		}
	}
	return false
}

type RouteTableFilterDestinationCidrBlock struct {
	DestinationCidrBlock string
	ViaIGW               bool
	ViaInstance          bool
	InstanceNotActive    bool
}

func (fs RouteTableFilterDestinationCidrBlock) Keep(rt *ec2.RouteTable) bool {
	for _, r := range rt.Routes {
		if r.DestinationCidrBlock != nil && *(r.DestinationCidrBlock) == fs.DestinationCidrBlock {
			if fs.ViaIGW {
				if r.GatewayId != nil && strings.HasPrefix(*(r.GatewayId), "igw-") {
					return true
				}
			} else {
				if fs.ViaInstance {
					if r.InstanceId != nil {
						if fs.InstanceNotActive {
							if *(r.State) != "active" {
								return true
							}
						} else {
							return true
						}
					}
				} else {
					return true
				}
			}
		}
	}
	return false
}

type RouteTableFilterTagMatch struct {
	Key   string
	Value string
}

func (fs RouteTableFilterTagMatch) Keep(rt *ec2.RouteTable) bool {
	for _, t := range rt.Tags {
		if *(t.Key) == fs.Key && *(t.Value) == fs.Value {
			return true
		}
	}
	return false
}
<commit_msg>Make the upsertroutespecs hold a ref to healthchecks<commit_after>package aws

import (
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/awslabs/aws-sdk-go/aws/credentials/ec2rolecreds"
	"github.com/bobtfish/AWSnycast/healthcheck"
	"log"
	"net"
	"net/http"
	"strings"
	"time"
)

type MyEC2Conn interface {
	CreateRoute(*ec2.CreateRouteInput) (*ec2.CreateRouteOutput, error)
	ReplaceRoute(*ec2.ReplaceRouteInput) (*ec2.ReplaceRouteOutput, error)
	DescribeRouteTables(*ec2.DescribeRouteTablesInput) (*ec2.DescribeRouteTablesOutput, error)
	DeleteRoute(*ec2.DeleteRouteInput) (*ec2.DeleteRouteOutput, error)
}

type MetadataFetcher interface {
	Available() bool
	GetMetadata(string) (string, error)
}

func NewMetadataFetcher(debug bool) MetadataFetcher {
	c := ec2metadata.Config{}
	if debug {
		c.LogLevel = aws.LogLevel(aws.LogDebug)
	}
	return ec2metadata.New(&c)
}

type ManageRoutesSpec struct {
	Cidr            string `yaml:"cidr"`
	Instance        string `yaml:"instance"`
	HealthcheckName string `yaml:"healthcheck"`
	healthcheck     *healthcheck.Healthcheck
	IfUnhealthy     bool `yaml:"if_unhealthy"`
}

func (r *ManageRoutesSpec) Default(instance string) {
	if !strings.Contains(r.Cidr, "/") {
		r.Cidr = fmt.Sprintf("%s/32", r.Cidr)
	}
	if r.Instance == "" {
		r.Instance = "SELF"
	}
	if r.Instance == "SELF" {
		r.Instance = instance
	}
}
func (r *ManageRoutesSpec) Validate(name string, healthchecks map[string]*healthcheck.Healthcheck) error {
	if r.Cidr == "" {
		return errors.New(fmt.Sprintf("cidr is not defined in %s", name))
	}
	if _, _, err := net.ParseCIDR(r.Cidr); err != nil {
		return errors.New(fmt.Sprintf("Could not parse %s in %s", err.Error(), name))
	}
	if r.HealthcheckName != "" {
		hc, ok := healthchecks[r.HealthcheckName]
		if !ok {
			return errors.New(fmt.Sprintf("Route table %s, upsert %s cannot find healthcheck '%s'", name, r.Cidr, r.HealthcheckName))
		}
		r.healthcheck = hc
	}
	return nil
}

type RouteTableFetcher interface {
	GetRouteTables() ([]*ec2.RouteTable, error)
	ManageInstanceRoute(ec2.RouteTable, ManageRoutesSpec, bool) error
}

type RouteTableFetcherEC2 struct {
	Region string
	conn   MyEC2Conn
}

func getCreateRouteInput(rtb ec2.RouteTable, cidr string, instance string) ec2.CreateRouteInput {
	return ec2.CreateRouteInput{
		RouteTableId:         rtb.RouteTableId,
		DestinationCidrBlock: aws.String(cidr),
		InstanceId:           aws.String(instance),
	}
}

func (r RouteTableFetcherEC2) ManageInstanceRoute(rtb ec2.RouteTable, rs ManageRoutesSpec, noop bool) error {
	if err := r.ReplaceInstanceRoute(rtb, rs.Cidr, rs.Instance, rs.IfUnhealthy, noop); err != nil {
		if err.Error() != "Never found CIDR in route table to replace" {
			return err
		}
	} else {
		return nil
	}
	opts := getCreateRouteInput(rtb, rs.Cidr, rs.Instance)

	log.Printf("[INFO] Creating route for %s: %#v", *rtb.RouteTableId, opts)
	if !noop {
		if _, err := r.conn.CreateRoute(&opts); err != nil {
			return err
		}
	}
	return nil
}

func (r RouteTableFetcherEC2) ReplaceInstanceRoute(rtb ec2.RouteTable, cidr string, instance string, ifUnhealthy bool, noop bool) error {
	for _, route := range rtb.Routes {
		if *(route.DestinationCidrBlock) == cidr {
			if route.InstanceId != nil && *(route.InstanceId) == instance {
				log.Printf("Skipping doing anything, %s is already routed via %s", cidr, instance)
				return nil
			}
			params := &ec2.ReplaceRouteInput{
				DestinationCidrBlock: aws.String(cidr),
				RouteTableId:         rtb.RouteTableId,
				InstanceId:           aws.String(instance),
			}
			if ifUnhealthy && *(route.State) == "active" {
				log.Printf("Not replacing route, as current route is active/healthy")
				return nil
			}
			if !noop {
				resp, err := r.conn.ReplaceRoute(params)
				if err != nil {
					// Print the error, cast err to awserr.Error to get the Code and
					// Message from an error.
					fmt.Println(err.Error())
					return err
				}
				fmt.Println(resp)
			}
			return nil
		}
	}
	return errors.New("Never found CIDR in route table to replace")
}

func (r RouteTableFetcherEC2) GetRouteTables() ([]*ec2.RouteTable, error) {
	resp, err := r.conn.DescribeRouteTables(&ec2.DescribeRouteTablesInput{})
	if err != nil {
		log.Printf("Error on DescribeRouteTables: %s", err)
		return []*ec2.RouteTable{}, err
	}
	return resp.RouteTables, nil
}

func NewRouteTableFetcher(region string, debug bool) (RouteTableFetcher, error) {
	r := RouteTableFetcherEC2{}
	providers := []credentials.Provider{
		&credentials.EnvProvider{},
		&ec2rolecreds.EC2RoleProvider{
			Client: ec2metadata.New(&ec2metadata.Config{
				HTTPClient: &http.Client{
					Timeout: 2 * time.Second,
				},
			}),
		},
	}
	cred := credentials.NewChainCredentials(providers)
	_, credErr := cred.Get()
	if credErr != nil {
		return r, credErr
	}
	awsConfig := &aws.Config{
		Credentials: cred,
		Region:      aws.String(region),
		MaxRetries:  aws.Int(3),
	}
	iamconn := iam.New(awsConfig)
	_, err := iamconn.GetUser(nil)
	if awsErr, ok := err.(awserr.Error); ok {
		if awsErr.Code() == "SignatureDoesNotMatch" {
			return r, fmt.Errorf("Failed authenticating with AWS: please verify credentials")
		}
	}
	r.conn = ec2.New(awsConfig)
	return r, nil
}

type RouteTableFilter interface {
	Keep(*ec2.RouteTable) bool
}

type RouteTableFilterAlways struct{}

func (fs RouteTableFilterAlways) Keep(rt *ec2.RouteTable) bool {
	return false
}

type RouteTableFilterNever struct{}

func (fs RouteTableFilterNever) Keep(rt *ec2.RouteTable) bool {
	return true
}

type RouteTableFilterAnd struct {
	RouteTableFilters []RouteTableFilter
}

func (fs RouteTableFilterAnd) Keep(rt *ec2.RouteTable) bool {
	for _, f := range fs.RouteTableFilters {
		if !f.Keep(rt) {
			return false
		}
	}
	return true
}

type RouteTableFilterOr struct {
	RouteTableFilters []RouteTableFilter
}

func (fs RouteTableFilterOr) Keep(rt *ec2.RouteTable) bool {
	for _, f := range fs.RouteTableFilters {
		if f.Keep(rt) {
			return true
		}
	}
	return false
}

type RouteTableFilterMain struct{}

func (fs RouteTableFilterMain) Keep(rt *ec2.RouteTable) bool {
	for _, a := range rt.Associations {
		if *(a.Main) {
			return true
		}
	}
	return false
}

func FilterRouteTables(f RouteTableFilter, tables []*ec2.RouteTable) []*ec2.RouteTable {
	out := make([]*ec2.RouteTable, 0, len(tables))
	for _, rtb := range tables {
		if f.Keep(rtb) {
			out = append(out, rtb)
		}
	}
	return out
}

func RouteTableForSubnet(subnet string, tables []*ec2.RouteTable) *ec2.RouteTable {
	subnet_rtb := FilterRouteTables(RouteTableFilterSubnet{SubnetId: subnet}, tables)
	if len(subnet_rtb) == 0 {
		main_rtbs := FilterRouteTables(RouteTableFilterMain{}, tables)
		if len(main_rtbs) == 0 {
			return nil
		}
		return main_rtbs[0]
	}
	return subnet_rtb[0]
}

type RouteTableFilterSubnet struct {
	SubnetId string
}

func (fs RouteTableFilterSubnet) Keep(rt *ec2.RouteTable) bool {
	for _, a := range rt.Associations {
		if a.SubnetId != nil && *(a.SubnetId) == fs.SubnetId {
			return true
		}
	}
	return false
}

type RouteTableFilterDestinationCidrBlock struct {
	DestinationCidrBlock string
	ViaIGW               bool
	ViaInstance          bool
	InstanceNotActive    bool
}

func (fs RouteTableFilterDestinationCidrBlock) Keep(rt *ec2.RouteTable) bool {
	for _, r := range rt.Routes {
		if r.DestinationCidrBlock != nil && *(r.DestinationCidrBlock) == fs.DestinationCidrBlock {
			if fs.ViaIGW {
				if r.GatewayId != nil && strings.HasPrefix(*(r.GatewayId), "igw-") {
					return true
				}
			} else {
				if fs.ViaInstance {
					if r.InstanceId != nil {
						if fs.InstanceNotActive {
							if *(r.State) != "active" {
								return true
							}
						} else {
							return true
						}
					}
				} else {
					return true
				}
			}
		}
	}
	return false
}

type RouteTableFilterTagMatch struct {
	Key   string
	Value string
}

func (fs RouteTableFilterTagMatch) Keep(rt *ec2.RouteTable) bool {
	for _, t := range rt.Tags {
		if *(t.Key) == fs.Key && *(t.Value) == fs.Value {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>// Header parsing functionality.

package mail

func split(ts []token, s token) [][]token {
	r, l := [][]token{}, 0
	for i, t := range ts {
		if string(t) == string(s) {
			r = append(r, ts[l:i])
			l = i+1
		}
	}
	if l != len(ts) {
		r = append(r, ts[l:len(ts)])
	}
	return r
}

// BUG: We don't currently support domain literals with commas.
func parseAddressList(s []byte) ([]Address, error) {
	al := []Address{}
	ts, e := tokenize(s)
	if e != nil {
		return al, e
	}
	tts := split(ts, []byte{','})
	for _, ts := range tts {
		a, e := parseAddress(ts)
		if e != nil {
			return al, e
		}
		al = append(al, a)
	}
	return al, nil
}
<commit_msg>Preparing to work with rfc2047<commit_after>// Header parsing functionality.

package mail

import (
	"strings"
)

func split(ts []token, s token) [][]token {
	r, l := [][]token{}, 0
	for i, t := range ts {
		if string(t) == string(s) {
			r = append(r, ts[l:i])
			l = i+1
		}
	}
	if l != len(ts) {
		r = append(r, ts[l:len(ts)])
	}
	return r
}

// BUG: We don't currently support domain literals with commas.
func parseAddressList(s []byte) ([]Address, error) {
	al := []Address{}
	ts, e := tokenize(s)
	if e != nil {
		return al, e
	}
	tts := split(ts, []byte{','})
	for _, ts := range tts {
		a, e := parseAddress(ts)
		if e != nil {
			return al, e
		}
		al = append(al, a)
	}
	return al, nil
}

func decodeRFC2047(word string) string {
	if strings.HasPrefix(word, "=?") && strings.HasSuffix(word, "?=") && strings.Count(word, "?") == 4 {
		return ""
	}
	return word
}

<|endoftext|>


<commit_before>package rest

import (
	"net/http"
	"strings"
	"time"

	"github.com/labstack/echo"
	"github.com/velocity-ci/velocity/backend/pkg/domain/project"
	"github.com/velocity-ci/velocity/backend/pkg/domain/sync"
	"github.com/velocity-ci/velocity/backend/pkg/velocity"
)

type projectRequest struct {
	Name       string `json:"name"`
	Address    string `json:"address"`
	PrivateKey string `json:"key"`
}

type projectResponse struct {
	ID         string    `json:"id"`
	Slug       string    `json:"slug"`
	Name       string    `json:"name"`
	Repository string    `json:"repository"`
	CreatedAt  time.Time `json:"createdAt"`
	UpdatedAt  time.Time `json:"updatedAt"`

	Synchronising bool `json:"synchronising"`
}

type projectList struct {
	Total int                `json:"total"`
	Data  []*projectResponse `json:"data"`
}

func newProjectResponse(p *project.Project) *projectResponse {
	return &projectResponse{
		ID:            p.ID,
		Slug:          p.Slug,
		Name:          p.Name,
		Repository:    p.Config.Address,
		CreatedAt:     p.CreatedAt,
		UpdatedAt:     p.UpdatedAt,
		Synchronising: p.Synchronising,
	}
}

type projectHandler struct {
	projectManager *project.Manager
	syncManager    *sync.Manager
}

func newProjectHandler(projectManager *project.Manager, syncManager *sync.Manager) *projectHandler {
	return &projectHandler{
		projectManager: projectManager,
		syncManager:    syncManager,
	}
}

func (h *projectHandler) create(c echo.Context) error {
	rP := new(projectRequest)
	if err := c.Bind(rP); err != nil {
		c.JSON(http.StatusBadRequest, "invalid payload")
		return nil
	}
	p, err := h.projectManager.Create(rP.Name, velocity.GitRepository{
		Address:    strings.TrimSpace(rP.Address),
		PrivateKey: strings.TrimSpace(rP.PrivateKey),
	})
	if err != nil {
		c.JSON(http.StatusBadRequest, err.ErrorMap)
		return nil
	}

	c.JSON(http.StatusCreated, newProjectResponse(p))
	return nil
}

func (h *projectHandler) getAll(c echo.Context) error {
	pQ := getPagingQueryParams(c)
	if pQ == nil {
		return nil
	}

	ps, total := h.projectManager.GetAll(pQ)
	rProjects := []*projectResponse{}
	for _, p := range ps {
		rProjects = append(rProjects, newProjectResponse(p))
	}

	c.JSON(http.StatusOK, projectList{
		Total: total,
		Data:  rProjects,
	})
	return nil
}

func (h *projectHandler) get(c echo.Context) error {

	if p := getProjectBySlug(c, h.projectManager); p != nil {
		c.JSON(http.StatusOK, newProjectResponse(p))
	}

	return nil
}

func (h *projectHandler) sync(c echo.Context) error {
	p := getProjectBySlug(c, h.projectManager)
	if p == nil {
		return nil
	}

	p, _ = h.syncManager.Sync(p)

	c.JSON(http.StatusOK, newProjectResponse(p))
	return nil
}

func getProjectBySlug(c echo.Context, pM *project.Manager) *project.Project {
	slug := c.Param("slug")

	p, err := pM.GetBySlug(slug)
	if err != nil {
		c.JSON(http.StatusNotFound, "not found")
		return nil
	}

	return p
}
<commit_msg>[backend] Added extra output to Project config<commit_after>package rest

import (
	"net/http"
	"strings"
	"time"

	"github.com/labstack/echo"
	"github.com/velocity-ci/velocity/backend/pkg/domain/project"
	"github.com/velocity-ci/velocity/backend/pkg/domain/sync"
	"github.com/velocity-ci/velocity/backend/pkg/velocity"
)

type projectRequest struct {
	Name       string `json:"name"`
	Address    string `json:"address"`
	PrivateKey string `json:"key"`
}

type projectResponse struct {
	ID         string    `json:"id"`
	Slug       string    `json:"slug"`
	Name       string    `json:"name"`
	Repository string    `json:"repository"`
	CreatedAt  time.Time `json:"createdAt"`
	UpdatedAt  time.Time `json:"updatedAt"`

	Synchronising bool `json:"synchronising"`

	Logo      string `json:"logo"`
	TasksPath string `json:"tasksPath"`
	GitDepth  int    `json:"gitDepth"`

	Parameters []parameterResp `json:"parameters"`
	Plugins    []pluginResp    `json:"plugins"`
	Stages     []stageResp     `json:"stages"`
}

type parameterResp struct {
	Info string `json:"info"`
}

type pluginResp struct {
	Use       string            `json:"use"`
	Arguments map[string]string `json:"arguments"`
	Events    []string          `json:"events"`
}

type stageResp struct {
	Name  string   `json:"name"`
	Tasks []string `json:"tasks"`
}

type projectList struct {
	Total int                `json:"total"`
	Data  []*projectResponse `json:"data"`
}

func newProjectResponse(p *project.Project) *projectResponse {
	params := []parameterResp{}
	for _, p := range p.Parameters {
		params = append(params, parameterResp{Info: p.GetInfo()})
	}

	plugins := []pluginResp{}
	for _, p := range p.Plugins {
		plugins = append(plugins, pluginResp{
			Use:       p.Use,
			Arguments: p.Arguments,
			Events:    p.Events,
		})
	}

	stages := []stageResp{}
	for _, s := range p.Stages {
		stages = append(stages, stageResp{
			Name:  s.Name,
			Tasks: s.Tasks,
		})
	}
	return &projectResponse{
		ID:            p.ID,
		Slug:          p.Slug,
		Name:          p.Name,
		Repository:    p.Config.Address,
		CreatedAt:     p.CreatedAt,
		UpdatedAt:     p.UpdatedAt,
		Synchronising: p.Synchronising,
		Logo:          p.Project.Logo,
		TasksPath:     p.Project.TasksPath,
		GitDepth:      p.Git.Depth,
		Parameters:    params,
		Plugins:       plugins,
		Stages:        stages,
	}
}

type projectHandler struct {
	projectManager *project.Manager
	syncManager    *sync.Manager
}

func newProjectHandler(projectManager *project.Manager, syncManager *sync.Manager) *projectHandler {
	return &projectHandler{
		projectManager: projectManager,
		syncManager:    syncManager,
	}
}

func (h *projectHandler) create(c echo.Context) error {
	rP := new(projectRequest)
	if err := c.Bind(rP); err != nil {
		c.JSON(http.StatusBadRequest, "invalid payload")
		return nil
	}
	p, err := h.projectManager.Create(rP.Name, velocity.GitRepository{
		Address:    strings.TrimSpace(rP.Address),
		PrivateKey: strings.TrimSpace(rP.PrivateKey),
	})
	if err != nil {
		c.JSON(http.StatusBadRequest, err.ErrorMap)
		return nil
	}

	c.JSON(http.StatusCreated, newProjectResponse(p))
	return nil
}

func (h *projectHandler) getAll(c echo.Context) error {
	pQ := getPagingQueryParams(c)
	if pQ == nil {
		return nil
	}

	ps, total := h.projectManager.GetAll(pQ)
	rProjects := []*projectResponse{}
	for _, p := range ps {
		rProjects = append(rProjects, newProjectResponse(p))
	}

	c.JSON(http.StatusOK, projectList{
		Total: total,
		Data:  rProjects,
	})
	return nil
}

func (h *projectHandler) get(c echo.Context) error {

	if p := getProjectBySlug(c, h.projectManager); p != nil {
		c.JSON(http.StatusOK, newProjectResponse(p))
	}

	return nil
}

func (h *projectHandler) sync(c echo.Context) error {
	p := getProjectBySlug(c, h.projectManager)
	if p == nil {
		return nil
	}

	p, _ = h.syncManager.Sync(p)

	c.JSON(http.StatusOK, newProjectResponse(p))
	return nil
}

func getProjectBySlug(c echo.Context, pM *project.Manager) *project.Project {
	slug := c.Param("slug")

	p, err := pM.GetBySlug(slug)
	if err != nil {
		c.JSON(http.StatusNotFound, "not found")
		return nil
	}

	return p
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/opencontainers/runtime-tools/validation/util"
)

func main() {
	g := util.GetDefaultGenerator()
	g.AddProcessRlimits("RLIMIT_NOFILE", 1024, 1024)
	err := util.RuntimeInsideValidate(g, nil)
	if err != nil {
		util.Fatal(err)
	}
}
<commit_msg>validation: Add system validation<commit_after>package main

import (
	"os"
	"runtime"

	"github.com/opencontainers/runtime-tools/validation/util"
)

func main() {
	if "linux" != runtime.GOOS && "solaris" != runtime.GOOS {
		util.Skip("POSIX-specific process.rlimits test", map[string]string{"OS": runtime.GOOS})
		os.Exit(0)
	}

	g := util.GetDefaultGenerator()
	g.AddProcessRlimits("RLIMIT_NOFILE", 1024, 1024)
	err := util.RuntimeInsideValidate(g, nil)
	if err != nil {
		util.Fatal(err)
	}
}
<|endoftext|>