<commit_before>// Copyright 2015 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package sa

import (
	"encoding/json"
	"errors"
	jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose"
	gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1"
	"github.com/letsencrypt/boulder/core"
)

// BoulderTypeConverter is used by Gorp for storing objects in DB.
type BoulderTypeConverter struct{}

// ToDb converts a Boulder object to one suitable for the DB representation.
func (tc BoulderTypeConverter) ToDb(val interface{}) (interface{}, error) {
	switch t := val.(type) {
	case core.AcmeIdentifier, []core.Challenge, []core.AcmeURL, [][]int:
		jsonBytes, err := json.Marshal(t)
		if err != nil {
			return nil, err
		}
		return string(jsonBytes), nil
	case jose.JsonWebKey:
		jsonBytes, err := t.MarshalJSON()
		if err != nil {
			return "", err
		}
		return string(jsonBytes), nil
	case core.AcmeStatus:
		return string(t), nil
	case core.OCSPStatus:
		return string(t), nil
	default:
		return val, nil
	}
}

// FromDb converts a DB representation back into a Boulder object.
func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
	switch target.(type) {
	case *core.AcmeIdentifier, *[]core.Challenge, *[]core.AcmeURL, *[][]int, core.JsonBuffer:
		binder := func(holder, target interface{}) error {
			s, ok := holder.(*string)
			if !ok {
				return errors.New("FromDb: Unable to convert *string")
			}
			b := []byte(*s)
			return json.Unmarshal(b, target)
		}
		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
	case *jose.JsonWebKey:
		binder := func(holder, target interface{}) error {
			s, ok := holder.(*string)
			if !ok {
				return errors.New("FromDb: Unable to convert *string")
			}
			b := []byte(*s)
			k := target.(*jose.JsonWebKey)
			if *s != "" {
				return k.UnmarshalJSON(b)
			} else {
				// HACK: Sometimes we can have an empty string the in the DB where a
				// key should be. We should fix that (see HACK above). In the meantime,
				// return the default JsonWebKey in such situations.
				// https://github.com/letsencrypt/boulder/issues/181
				return nil
			}
		}
		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
	case *core.AcmeStatus:
		binder := func(holder, target interface{}) error {
			s := holder.(*string)
			st := target.(*core.AcmeStatus)
			*st = core.AcmeStatus(*s)
			return nil
		}
		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
	case *core.OCSPStatus:
		binder := func(holder, target interface{}) error {
			s := holder.(*string)
			st := target.(*core.OCSPStatus)
			*st = core.OCSPStatus(*s)
			return nil
		}
		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
	default:
		return gorp.CustomScanner{}, false
	}
}
<commit_msg>Issue #252: Remove the hack from Type Converter<commit_after>// Copyright 2015 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package sa

import (
	"encoding/json"
	"errors"
	jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose"
	gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1"
	"github.com/letsencrypt/boulder/core"
)

// BoulderTypeConverter is used by Gorp for storing objects in DB.
type BoulderTypeConverter struct{}

// ToDb converts a Boulder object to one suitable for the DB representation.
func (tc BoulderTypeConverter) ToDb(val interface{}) (interface{}, error) {
	switch t := val.(type) {
	case core.AcmeIdentifier, []core.Challenge, []core.AcmeURL, [][]int:
		jsonBytes, err := json.Marshal(t)
		if err != nil {
			return nil, err
		}
		return string(jsonBytes), nil
	case jose.JsonWebKey:
		jsonBytes, err := t.MarshalJSON()
		if err != nil {
			return "", err
		}
		return string(jsonBytes), nil
	case core.AcmeStatus:
		return string(t), nil
	case core.OCSPStatus:
		return string(t), nil
	default:
		return val, nil
	}
}

// FromDb converts a DB representation back into a Boulder object.
func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
	switch target.(type) {
	case *core.AcmeIdentifier, *[]core.Challenge, *[]core.AcmeURL, *[][]int, core.JsonBuffer:
		binder := func(holder, target interface{}) error {
			s, ok := holder.(*string)
			if !ok {
				return errors.New("FromDb: Unable to convert *string")
			}
			b := []byte(*s)
			return json.Unmarshal(b, target)
		}
		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
	case *jose.JsonWebKey:
		binder := func(holder, target interface{}) error {
			s, ok := holder.(*string)
			if !ok {
				return errors.New("FromDb: Unable to convert *string")
			}
			if *s == "" {
				return errors.New("FromDb: Empty JWK field.")
			}
			b := []byte(*s)
			k := target.(*jose.JsonWebKey)
			return k.UnmarshalJSON(b)
		}
		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
	case *core.AcmeStatus:
		binder := func(holder, target interface{}) error {
			s := holder.(*string)
			st := target.(*core.AcmeStatus)
			*st = core.AcmeStatus(*s)
			return nil
		}
		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
	case *core.OCSPStatus:
		binder := func(holder, target interface{}) error {
			s := holder.(*string)
			st := target.(*core.OCSPStatus)
			*st = core.OCSPStatus(*s)
			return nil
		}
		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
	default:
		return gorp.CustomScanner{}, false
	}
}
<|endoftext|>


<commit_before>// Copyright Istio 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 tag

import (
	"context"
	"fmt"
	"strings"

	"github.com/hashicorp/go-multierror"
	admit_v1 "k8s.io/api/admissionregistration/v1"
	kerrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"

	"istio.io/api/label"
)

func GetTagWebhooks(ctx context.Context, client kubernetes.Interface) ([]admit_v1.MutatingWebhookConfiguration, error) {
	webhooks, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{
		LabelSelector: IstioTagLabel,
	})
	if err != nil {
		return nil, err
	}
	return webhooks.Items, nil
}

// GetWebhooksWithTag returns webhooks tagged with istio.io/tag=<tag>.
func GetWebhooksWithTag(ctx context.Context, client kubernetes.Interface, tag string) ([]admit_v1.MutatingWebhookConfiguration, error) {
	webhooks, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", IstioTagLabel, tag),
	})
	if err != nil {
		return nil, err
	}
	return webhooks.Items, nil
}

// GetWebhooksWithRevision returns webhooks tagged with istio.io/rev=<rev> and NOT TAGGED with istio.io/tag.
// this retrieves the webhook created at revision installation rather than tag webhooks
func GetWebhooksWithRevision(ctx context.Context, client kubernetes.Interface, rev string) ([]admit_v1.MutatingWebhookConfiguration, error) {
	webhooks, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s,!%s", label.IoIstioRev.Name, rev, IstioTagLabel),
	})
	if err != nil {
		return nil, err
	}
	return webhooks.Items, nil
}

// GetValidatingWebhooksWithRevision returns validating webhooks tagged with istio.io/rev=<rev> and NOT TAGGED with istio.io/tag.
// this retrieves the webhook created at revision installation rather than tags.
func GetValidatingWebhooksWithRevision(ctx context.Context, client kubernetes.Interface, rev string) ([]admit_v1.ValidatingWebhookConfiguration, error) {
	webhooks, err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().List(ctx, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s,!%s", label.IoIstioRev.Name, rev, IstioTagLabel),
	})
	if err != nil {
		return nil, err
	}
	return webhooks.Items, nil
}

// GetNamespacesWithTag retrieves all namespaces pointed at the given tag.
func GetNamespacesWithTag(ctx context.Context, client kubernetes.Interface, tag string) ([]string, error) {
	namespaces, err := client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", label.IoIstioRev.Name, tag),
	})
	if err != nil {
		return nil, err
	}

	nsNames := make([]string, len(namespaces.Items))
	for i, ns := range namespaces.Items {
		nsNames[i] = ns.Name
	}
	return nsNames, nil
}

// GetWebhookTagName extracts tag name from webhook object.
func GetWebhookTagName(wh admit_v1.MutatingWebhookConfiguration) (string, error) {
	if tagName, ok := wh.ObjectMeta.Labels[IstioTagLabel]; ok {
		return tagName, nil
	}
	return "", fmt.Errorf("could not extract tag name from webhook")
}

// GetWebhookRevision extracts tag target revision from webhook object.
func GetWebhookRevision(wh admit_v1.MutatingWebhookConfiguration) (string, error) {
	if tagName, ok := wh.ObjectMeta.Labels[label.IoIstioRev.Name]; ok {
		return tagName, nil
	}
	return "", fmt.Errorf("could not extract tag revision from webhook")
}

// DeleteTagWebhooks deletes the given webhooks.
func DeleteTagWebhooks(ctx context.Context, client kubernetes.Interface, tag string) error {
	webhooks, err := GetWebhooksWithTag(ctx, client, tag)
	if err != nil {
		return err
	}
	var result error
	for _, wh := range webhooks {
		result = multierror.Append(client.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(ctx, wh.Name, metav1.DeleteOptions{})).ErrorOrNil()
	}
	return result
}

// DeleteDeprecatedValidator deletes the deprecated validating webhook configuration. This is used after a user explicitly
// sets a new default revision.
func DeleteDeprecatedValidator(ctx context.Context, client kubernetes.Interface) error {
	vwhs, err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().List(ctx, metav1.ListOptions{
		LabelSelector: "app=istiod",
	})
	if err != nil {
		return err
	}
	var errs *multierror.Error
	for _, vwh := range vwhs.Items {
		// hacky but we want to remove the validators that used to be in base, not the per-revision webhooks.
		if !strings.Contains(vwh.Name, "validator") {
			errs = multierror.Append(errs,
				client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(ctx, vwh.Name, metav1.DeleteOptions{}))
		}
	}
	if kerrors.IsNotFound(err) {
		return nil
	}
	return errs.ErrorOrNil()
}

var neverMatch = &metav1.LabelSelector{
	MatchLabels: map[string]string{
		"istio.io/deactivated": "never-match",
	},
}

// PreviousInstallExists checks whether there is an existing Istio installation. Should be used in installer when deciding
// whether to make an installation the default.
func PreviousInstallExists(ctx context.Context, client kubernetes.Interface) bool {
	mwhs, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{
		LabelSelector: "app=sidecar-injector",
	})
	if err != nil {
		return false
	}
	return len(mwhs.Items) > 0
}

// DeactivateIstioInjectionWebhook deactivates the istio-injection webhook from the given MutatingWebhookConfiguration if exists.
// used rather than just deleting the webhook since we want to keep it around after changing the default so user can later
// switch back to it. This is a hack but it is meant to cover a corner case where a user wants to migrate from a non-revisioned
// old version and then later decides to switch back to the old revision again.
func DeactivateIstioInjectionWebhook(ctx context.Context, client kubernetes.Interface) error {
	whs, err := GetWebhooksWithRevision(ctx, client, DefaultRevisionName)
	if err != nil {
		return err
	}
	if len(whs) == 0 {
		// no revision with default, no action required.
		return nil
	}
	if len(whs) > 1 {
		return fmt.Errorf("expected a single webhook for default revision")
	}
	webhook := whs[0]
	for i := range webhook.Webhooks {
		wh := webhook.Webhooks[i]
		// this is an abomination, but if this isn't a per-revision webhook, we want to make it ineffectual
		// without deleting it. Add a nonsense match.
		wh.NamespaceSelector = neverMatch
		wh.ObjectSelector = neverMatch
		webhook.Webhooks[i] = wh
	}
	admit := client.AdmissionregistrationV1().MutatingWebhookConfigurations()
	_, err = admit.Update(ctx, &webhook, metav1.UpdateOptions{})
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Delete GetValidatingWebhooksWithRevision func (#35918)<commit_after>// Copyright Istio 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 tag

import (
	"context"
	"fmt"
	"strings"

	"github.com/hashicorp/go-multierror"
	admit_v1 "k8s.io/api/admissionregistration/v1"
	kerrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"

	"istio.io/api/label"
)

func GetTagWebhooks(ctx context.Context, client kubernetes.Interface) ([]admit_v1.MutatingWebhookConfiguration, error) {
	webhooks, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{
		LabelSelector: IstioTagLabel,
	})
	if err != nil {
		return nil, err
	}
	return webhooks.Items, nil
}

// GetWebhooksWithTag returns webhooks tagged with istio.io/tag=<tag>.
func GetWebhooksWithTag(ctx context.Context, client kubernetes.Interface, tag string) ([]admit_v1.MutatingWebhookConfiguration, error) {
	webhooks, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", IstioTagLabel, tag),
	})
	if err != nil {
		return nil, err
	}
	return webhooks.Items, nil
}

// GetWebhooksWithRevision returns webhooks tagged with istio.io/rev=<rev> and NOT TAGGED with istio.io/tag.
// this retrieves the webhook created at revision installation rather than tag webhooks
func GetWebhooksWithRevision(ctx context.Context, client kubernetes.Interface, rev string) ([]admit_v1.MutatingWebhookConfiguration, error) {
	webhooks, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s,!%s", label.IoIstioRev.Name, rev, IstioTagLabel),
	})
	if err != nil {
		return nil, err
	}
	return webhooks.Items, nil
}

// GetNamespacesWithTag retrieves all namespaces pointed at the given tag.
func GetNamespacesWithTag(ctx context.Context, client kubernetes.Interface, tag string) ([]string, error) {
	namespaces, err := client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", label.IoIstioRev.Name, tag),
	})
	if err != nil {
		return nil, err
	}

	nsNames := make([]string, len(namespaces.Items))
	for i, ns := range namespaces.Items {
		nsNames[i] = ns.Name
	}
	return nsNames, nil
}

// GetWebhookTagName extracts tag name from webhook object.
func GetWebhookTagName(wh admit_v1.MutatingWebhookConfiguration) (string, error) {
	if tagName, ok := wh.ObjectMeta.Labels[IstioTagLabel]; ok {
		return tagName, nil
	}
	return "", fmt.Errorf("could not extract tag name from webhook")
}

// GetWebhookRevision extracts tag target revision from webhook object.
func GetWebhookRevision(wh admit_v1.MutatingWebhookConfiguration) (string, error) {
	if tagName, ok := wh.ObjectMeta.Labels[label.IoIstioRev.Name]; ok {
		return tagName, nil
	}
	return "", fmt.Errorf("could not extract tag revision from webhook")
}

// DeleteTagWebhooks deletes the given webhooks.
func DeleteTagWebhooks(ctx context.Context, client kubernetes.Interface, tag string) error {
	webhooks, err := GetWebhooksWithTag(ctx, client, tag)
	if err != nil {
		return err
	}
	var result error
	for _, wh := range webhooks {
		result = multierror.Append(client.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(ctx, wh.Name, metav1.DeleteOptions{})).ErrorOrNil()
	}
	return result
}

// DeleteDeprecatedValidator deletes the deprecated validating webhook configuration. This is used after a user explicitly
// sets a new default revision.
func DeleteDeprecatedValidator(ctx context.Context, client kubernetes.Interface) error {
	vwhs, err := client.AdmissionregistrationV1().ValidatingWebhookConfigurations().List(ctx, metav1.ListOptions{
		LabelSelector: "app=istiod",
	})
	if err != nil {
		return err
	}
	var errs *multierror.Error
	for _, vwh := range vwhs.Items {
		// hacky but we want to remove the validators that used to be in base, not the per-revision webhooks.
		if !strings.Contains(vwh.Name, "validator") {
			errs = multierror.Append(errs,
				client.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(ctx, vwh.Name, metav1.DeleteOptions{}))
		}
	}
	if kerrors.IsNotFound(err) {
		return nil
	}
	return errs.ErrorOrNil()
}

var neverMatch = &metav1.LabelSelector{
	MatchLabels: map[string]string{
		"istio.io/deactivated": "never-match",
	},
}

// PreviousInstallExists checks whether there is an existing Istio installation. Should be used in installer when deciding
// whether to make an installation the default.
func PreviousInstallExists(ctx context.Context, client kubernetes.Interface) bool {
	mwhs, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{
		LabelSelector: "app=sidecar-injector",
	})
	if err != nil {
		return false
	}
	return len(mwhs.Items) > 0
}

// DeactivateIstioInjectionWebhook deactivates the istio-injection webhook from the given MutatingWebhookConfiguration if exists.
// used rather than just deleting the webhook since we want to keep it around after changing the default so user can later
// switch back to it. This is a hack but it is meant to cover a corner case where a user wants to migrate from a non-revisioned
// old version and then later decides to switch back to the old revision again.
func DeactivateIstioInjectionWebhook(ctx context.Context, client kubernetes.Interface) error {
	whs, err := GetWebhooksWithRevision(ctx, client, DefaultRevisionName)
	if err != nil {
		return err
	}
	if len(whs) == 0 {
		// no revision with default, no action required.
		return nil
	}
	if len(whs) > 1 {
		return fmt.Errorf("expected a single webhook for default revision")
	}
	webhook := whs[0]
	for i := range webhook.Webhooks {
		wh := webhook.Webhooks[i]
		// this is an abomination, but if this isn't a per-revision webhook, we want to make it ineffectual
		// without deleting it. Add a nonsense match.
		wh.NamespaceSelector = neverMatch
		wh.ObjectSelector = neverMatch
		webhook.Webhooks[i] = wh
	}
	admit := client.AdmissionregistrationV1().MutatingWebhookConfigurations()
	_, err = admit.Update(ctx, &webhook, metav1.UpdateOptions{})
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/urfave/cli"
	"github.com/keltia/ripe-atlas"
	"log"
	"os"
)

// init injects our "ip" related commands/options.
func init() {
	// Fill-in the various commands
	cliCommands = append(cliCommands, cli.Command{
		Name:        "ping",
		Usage:       "ping selected address",
		Description: "send echo/reply to an IP",
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:        "ipv6",
				Usage:       "displays only IPv6",
				Destination: &fWant6,
			},
			cli.BoolFlag{
				Name:        "ipv4",
				Usage:       "displays only IPv4",
				Destination: &fWant4,
			},
		},
		Action: cmdPing,
	})
}

// shortcuts

// cmdIP is a short for displaying the IPs for one probe
func cmdPing(c *cli.Context) error {
	// By default we want both
	if !fWant4 && !fWant6 {
		fWant6, fWant4 = true, true
	}
	args := c.Args()
	if args[0] == "" {
		log.Fatalf("Error: you must specify a hostname/IP")
	}

	addr := args[0]

	def := atlas.Definition{
		Description: "My ping",
		Type: "ping",
		Target: addr,
	}
	defs := []atlas.Definition{}
	defs = append(defs, def)

	req := atlas.MeasurementRequest{
		Definitions: defs,
	}
	// Default set of probes
	probes := atlas.ProbeSet{
		{
			Requested: 10,
			Type: "area",
			Value: "WW",
			Tags: nil,
		},
	}

	req.Probes = probes
	log.Printf("req=%v", req)
	m, err := atlas.Ping(req)
	if err != nil {
		fmt.Printf("err: %v", err)
		os.Exit(1)
	}

	//str := res.Result.Display()
	fmt.Printf("m: %v\n", m)
	return nil
}

<commit_msg>Fix parameter checking.<commit_after>package main

import (
	"fmt"
	"github.com/urfave/cli"
	"github.com/keltia/ripe-atlas"
	"log"
	"os"
)

// init injects our "ip" related commands/options.
func init() {
	// Fill-in the various commands
	cliCommands = append(cliCommands, cli.Command{
		Name:        "ping",
		Usage:       "ping selected address",
		Description: "send echo/reply to an IP",
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:        "ipv6",
				Usage:       "displays only IPv6",
				Destination: &fWant6,
			},
			cli.BoolFlag{
				Name:        "ipv4",
				Usage:       "displays only IPv4",
				Destination: &fWant4,
			},
		},
		Action: cmdPing,
	})
}

// shortcuts

// cmdIP is a short for displaying the IPs for one probe
func cmdPing(c *cli.Context) error {
	// By default we want both
	if !fWant4 && !fWant6 {
		fWant6, fWant4 = true, true
	}
	args := c.Args()
	if args == nil || len(args) != 1 {
		log.Fatalf("Error: you must specify a hostname/IP")
	}

	addr := args[0]

	def := atlas.Definition{
		Description: "My ping",
		Type: "ping",
		Target: addr,
	}
	defs := []atlas.Definition{}
	defs = append(defs, def)

	req := atlas.MeasurementRequest{
		Definitions: defs,
	}
	// Default set of probes
	probes := atlas.ProbeSet{
		{
			Requested: 10,
			Type: "area",
			Value: "WW",
			Tags: nil,
		},
	}

	req.Probes = probes
	log.Printf("req=%v", req)
	m, err := atlas.Ping(req)
	if err != nil {
		fmt.Printf("err: %v", err)
		os.Exit(1)
	}

	//str := res.Result.Display()
	fmt.Printf("m: %v\n", m)
	return nil
}

<|endoftext|>


<commit_before>package cmd

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"github.com/spf13/cobra"
)

// Internal types to allow to provide methods.
type nameValue string
type shorthandValue string
type usageValue string
type typeValue string
type defValueValue string
type noOptDefValValue string
type annotationsValue map[string][]string

// Flag is the structure for the flags, the json from the annotation is
// unmarshaled into this structure. For more information see also
// github.com/spf13/pflag/flag.
type Flag struct {
	Name        nameValue        // name as it appears on command line
	Shorthand   shorthandValue   // one-letter abbreviated flag
	Usage       usageValue       // help message
	Type        typeValue        // type, defaults to bool
	DefValue    defValueValue    // default value (as text); for usage message
	NoOptDefVal noOptDefValValue // default value (as text); if the flag is on the command line without any options
	Annotations annotationsValue // used by cobra.Command bash autocomple code
}

// FlagsDefinition is an array of Flag holding all defined flags of a command.
type FlagsDefinition []Flag

// Defines the constants for the valid types which always should used in the
// source code.
const (
	ftTest1          = "_test1_" // used for testing only
	ftTest2          = "_test2_" // used for testing only
	ftTest3          = "_test3_" // used for testing only
	FtBool           = "bool"
	FtBoolSlice      = "boolSlice"
	FtBytesHex       = "bytesHex"
	FtBytesBase64    = "bytesBase64"
	FtCount          = "count"
	FtDuration       = "duration"
	FtDurationSlice  = "durationSlice"
	FtFloat32        = "float32"
	FtFloat32Slice   = "float32Slice"
	FtFloat64        = "float64"
	FtFloat64Slice   = "float64Slice"
	FtInt            = "int"
	FtIntSlice       = "intSlice"
	FtInt8           = "int8"
	FtInt16          = "int16"
	FtInt32          = "int32"
	FtInt32Slice     = "int32Slice"
	FtInt64          = "int64"
	FtInt64Slice     = "int64Slice"
	FtIP             = "ip"
	FtIPSlice        = "ipSlice"
	FtIPMask         = "ipMask"
	FtIPNet          = "ipNet"
	FtString         = "string"
	FtStringArray    = "stringArray"
	FtStringSlice    = "stringSlice"
	FtStringToInt    = "stringToInt"
	FtStringToInt64  = "stringToInt64"
	FtStringToString = "stringToString"
	FtUint           = "uint"
	FtUintSlice      = "uintSlice"
	FtUint8          = "uint8"
	FtUint16         = "uint16"
	FtUint32         = "uint32"
	FtUint64         = "uint64"
)

// ValidTypes defines the valid types, a value of true indicates it's
// implemented.
// To implement a new type add the required line to the switch statement in
// AssignToCommand and set it here to true, that's all. If a new type is
// added which is not defined here just add a new constant above and here.
var ValidTypes = map[typeValue]bool{
	ftTest1:          false, // used for testing only
	ftTest2:          true,  // used for testing only
	ftTest3:          true,  // used for testing only
	FtBool:           true,
	FtBoolSlice:      false,
	FtBytesHex:       false,
	FtBytesBase64:    false,
	FtCount:          false,
	FtDuration:       false,
	FtDurationSlice:  false,
	FtFloat32:        false,
	FtFloat32Slice:   false,
	FtFloat64:        false,
	FtFloat64Slice:   false,
	FtInt:            true,
	FtIntSlice:       false,
	FtInt8:           false,
	FtInt16:          false,
	FtInt32:          false,
	FtInt32Slice:     false,
	FtInt64:          false,
	FtInt64Slice:     false,
	FtIP:             false,
	FtIPSlice:        false,
	FtIPMask:         false,
	FtIPNet:          false,
	FtString:         true,
	FtStringArray:    false,
	FtStringSlice:    false,
	FtStringToInt:    false,
	FtStringToInt64:  false,
	FtStringToString: false,
	FtUint:           true,
	FtUintSlice:      false,
	FtUint8:          false,
	FtUint16:         false,
	FtUint32:         false,
	FtUint64:         false,
}

// Flags is the main type used to access flags and methods.
type Flags struct {
	CommandName string
	Script      string
	Definition  FlagsDefinition
}

// formatErrorItem returns a formatted message indented by the number of
// `level` and prefixed by a `prefix`.
func formatErrorItem(level int, prefix string, format string, a ...interface{}) error {
	return fmt.Errorf("%s %s %s", strings.Repeat(" ", (level-1)*2), prefix, fmt.Sprintf(format, a...))
}

// extractError returns the error message of an error `err` prefixed by a new
// line or an empty string if no error was given.
func extractError(err error) string {
	if err != nil {
		return "\n" + strings.Trim(err.Error(), "\n")
	}

	return ""
}

// validate checks a nameValue for uniqueness.
func (v *nameValue) validate(longOptions *map[nameValue]bool) error {
	// Check flag does not already exist
	if _, found := (*longOptions)[*v]; found {
		return formatErrorItem(2, "-", "flag '%s' already defined", *v)
	}

	(*longOptions)[*v] = true

	return nil
}

// validate checks a shorthandValue for uniqueness and containing one ASCII
// character only.
func (v *shorthandValue) validate(shortOptions *map[shorthandValue]nameValue, name nameValue) error {
	var errors string = ""

	// Check shorthand does not already exist
	if flagOfShorthand, found := (*shortOptions)[*v]; found {
		errors += extractError(formatErrorItem(2, "-", "shorthand '%s' is already defined for flag '%s'", *v, flagOfShorthand))
	} else if *v != "" {
		(*shortOptions)[*v] = name
	}

	// Check shorthand is one letter only
	if len(*v) > 1 {
		errors += extractError(formatErrorItem(2, "-", "shorthand '%s' is more than one ASCII character", *v))
	}

	if errors != "" {
		return fmt.Errorf("%s", errors)
	}

	return nil
}

// validate checks a usageValue to be defined.
func (v *usageValue) validate() error {
	// Check usage is defined
	if *v == "" {
		return formatErrorItem(2, "-", "no usage defined")
	}

	return nil
}

// validate checks a typeValue and sets a default value if empty.
func (v *typeValue) validate() error {
	// Check type and set default if empty
	if *v == "" {
		*v = FtBool
	}

	// Check type is valid
	implemented, found := ValidTypes[*v]

	if !found {
		return formatErrorItem(2, "-", "type '%s' is not known", *v)
	} else if !implemented {
		return formatErrorItem(2, "-", "type '%s' is not implemented", *v)
	}

	return nil
}

// validate checks a defValueValue and sets a default value if empty.
func (v *defValueValue) validate(aType typeValue) error {
	if *v != "" {
		return nil
	}

	// Init the DefValue
	switch aType {
	case FtBool:
		*v = defValueValue(strconv.FormatBool(false))
	case FtCount, FtDuration, FtFloat32, FtFloat64, FtInt, FtInt8, FtInt16, FtInt32, FtUint, FtUint8, FtUint16, FtUint32:
		*v = defValueValue(strconv.FormatInt(0, 10))
	case ftTest3: // used for testing only
		*v = ""
	default:
		if implemented := ValidTypes[aType]; implemented {
			// Mandatory implementation missing -> panic
			panic(fmt.Sprintf("Error implementation of DefValue validation missing for type '%s'", aType))
		}
	}

	return nil
}

// validate checks a noOptDefValValue and sets a default value if needed.
func (v *noOptDefValValue) validate(aType typeValue) error {
	if *v != "" {
		return nil
	}

	// Init the NoOptDefValValue
	switch aType {
	case FtBool:
		*v = noOptDefValValue(strconv.FormatBool(true))
	}

	return nil
}

// validate checks a annotationsValue.
func (v *annotationsValue) validate() error {
	return nil
}

// validateFlag checks all fields by calling the corresponding validate method.
func (f *Flag) validateFlag(longOptions *map[nameValue]bool, shortOptions *map[shorthandValue]nameValue) error {
	errors := ""

	// Chech all fields
	errors += extractError(f.Name.validate(longOptions))
	errors += extractError(f.Shorthand.validate(shortOptions, f.Name))
	errors += extractError(f.Usage.validate())
	errors += extractError(f.Type.validate())
	errors += extractError(f.DefValue.validate(f.Type))
	errors += extractError(f.NoOptDefVal.validate(f.Type))
	errors += extractError(f.Annotations.validate())

	if errors != "" {
		return fmt.Errorf("%s", errors)
	}

	return nil
}

// Init initializes the Flags structure.
func (f *Flags) Init(commandName, script string) {
	f.CommandName = commandName
	f.Script = script
}

// validateFlags checks all Flags by calling the validateFlag method.
func (f *Flags) validateFlags(flags *FlagsDefinition) error {
	var errors string

	// Temporay vars to precheck for duplicated flags. It's still possible
	// other commands will introduce the same flags which is tested
	// afterwards by cobra.
	long := map[nameValue]bool{"help": true}
	short := map[shorthandValue]nameValue{"h": "help"}

	for i := range *flags {
		flag := &(*flags)[i]

		// Additional validations of the flag fields
		flagErrors := extractError(flag.validateFlag(&long, &short))

		if flagErrors != "" {
			errors += extractError(formatErrorItem(1, "*", "for flag '%s':%s", flag.Name, flagErrors))
		}
	}

	if errors != "" {
		return fmt.Errorf("The following problems were found in the flags definition of the command '%s' in '%s':%s", f.CommandName, f.Script, errors)
	}

	return nil
}

// LoadFromJSON imports the defs provided by the custom command as json into
// the flags structure.
func (f *Flags) LoadFromJSON(data string) error {
	if data == "" {
		return nil
	}

	var err error
	var defs = FlagsDefinition{}

	// Import the JSON to the FlagsDefinition structure and return in case of
	// error
	if err = json.Unmarshal([]byte(data), &defs); err != nil {
		return err
	}

	// Validate the user provided flags and return in case of error
	if err = f.validateFlags(&defs); err != nil {
		return err
	}

	// Assign the data to the field
	f.Definition = defs
	return nil
}

// AssignToCommand iterates the flags and assigns it to the provided command.
func (f *Flags) AssignToCommand(command *cobra.Command) error {
	var errors string

	for _, flag := range f.Definition {
		// Create the flag at the command
		switch flag.Type {
		case FtBool /*, ""*/ : // empty type defaults to bool
			command.Flags().BoolP(string(flag.Name), string(flag.Shorthand), false, string(flag.Usage))
		case FtInt:
			command.Flags().IntP(string(flag.Name), string(flag.Shorthand), 0, string(flag.Usage))
		case FtString:
			command.Flags().StringP(string(flag.Name), string(flag.Shorthand), "", string(flag.Usage))
		case FtUint:
			command.Flags().UintP(string(flag.Name), string(flag.Shorthand), 0, string(flag.Usage))
		default:
			if implemented := ValidTypes[flag.Type]; implemented {
				// Mandatory implementation missing -> panic
				panic(fmt.Sprintf("Error implementation missing for type '%s'", flag.Type))
			}

			continue // continue here, nothing to set for this flag
		}

		// Update default values and annotations
		newFlag := command.Flags().Lookup(string(flag.Name))

		if err := newFlag.Value.Set(string(flag.DefValue)); err != nil {
			// Invalid default value was defined by the user, hide the flag
			// and save the error
			newFlag.Hidden = true
			errors += extractError(formatErrorItem(1, "-", "error '%s' while set value of flag '%s'", err.Error(), flag.Name))
		}

		newFlag.DefValue = string(flag.DefValue)
		newFlag.NoOptDefVal = string(flag.NoOptDefVal)
		newFlag.Annotations = flag.Annotations
	}

	if errors != "" {
		return fmt.Errorf("The following problems were found while assigning the flags to the command '%s' in '%s':%s", f.CommandName, f.Script, errors)
	}

	return nil
}
<commit_msg>Fix validation error for string flag in shell command, fixes #2631 (#2636)<commit_after>package cmd

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"github.com/spf13/cobra"
)

// Internal types to allow to provide methods.
type nameValue string
type shorthandValue string
type usageValue string
type typeValue string
type defValueValue string
type noOptDefValValue string
type annotationsValue map[string][]string

// Flag is the structure for the flags, the json from the annotation is
// unmarshaled into this structure. For more information see also
// github.com/spf13/pflag/flag.
type Flag struct {
	Name        nameValue        // name as it appears on command line
	Shorthand   shorthandValue   // one-letter abbreviated flag
	Usage       usageValue       // help message
	Type        typeValue        // type, defaults to bool
	DefValue    defValueValue    // default value (as text); for usage message
	NoOptDefVal noOptDefValValue // default value (as text); if the flag is on the command line without any options
	Annotations annotationsValue // used by cobra.Command bash autocomple code
}

// FlagsDefinition is an array of Flag holding all defined flags of a command.
type FlagsDefinition []Flag

// Defines the constants for the valid types which always should used in the
// source code.
const (
	ftTest1          = "_test1_" // used for testing only
	ftTest2          = "_test2_" // used for testing only
	ftTest3          = "_test3_" // used for testing only
	FtBool           = "bool"
	FtBoolSlice      = "boolSlice"
	FtBytesHex       = "bytesHex"
	FtBytesBase64    = "bytesBase64"
	FtCount          = "count"
	FtDuration       = "duration"
	FtDurationSlice  = "durationSlice"
	FtFloat32        = "float32"
	FtFloat32Slice   = "float32Slice"
	FtFloat64        = "float64"
	FtFloat64Slice   = "float64Slice"
	FtInt            = "int"
	FtIntSlice       = "intSlice"
	FtInt8           = "int8"
	FtInt16          = "int16"
	FtInt32          = "int32"
	FtInt32Slice     = "int32Slice"
	FtInt64          = "int64"
	FtInt64Slice     = "int64Slice"
	FtIP             = "ip"
	FtIPSlice        = "ipSlice"
	FtIPMask         = "ipMask"
	FtIPNet          = "ipNet"
	FtString         = "string"
	FtStringArray    = "stringArray"
	FtStringSlice    = "stringSlice"
	FtStringToInt    = "stringToInt"
	FtStringToInt64  = "stringToInt64"
	FtStringToString = "stringToString"
	FtUint           = "uint"
	FtUintSlice      = "uintSlice"
	FtUint8          = "uint8"
	FtUint16         = "uint16"
	FtUint32         = "uint32"
	FtUint64         = "uint64"
)

// ValidTypes defines the valid types, a value of true indicates it's
// implemented.
// To implement a new type add the required line to the switch statement in
// AssignToCommand and set it here to true, that's all. If a new type is
// added which is not defined here just add a new constant above and here.
var ValidTypes = map[typeValue]bool{
	ftTest1:          false, // used for testing only
	ftTest2:          true,  // used for testing only
	ftTest3:          true,  // used for testing only
	FtBool:           true,
	FtBoolSlice:      false,
	FtBytesHex:       false,
	FtBytesBase64:    false,
	FtCount:          false,
	FtDuration:       false,
	FtDurationSlice:  false,
	FtFloat32:        false,
	FtFloat32Slice:   false,
	FtFloat64:        false,
	FtFloat64Slice:   false,
	FtInt:            true,
	FtIntSlice:       false,
	FtInt8:           false,
	FtInt16:          false,
	FtInt32:          false,
	FtInt32Slice:     false,
	FtInt64:          false,
	FtInt64Slice:     false,
	FtIP:             false,
	FtIPSlice:        false,
	FtIPMask:         false,
	FtIPNet:          false,
	FtString:         true,
	FtStringArray:    false,
	FtStringSlice:    false,
	FtStringToInt:    false,
	FtStringToInt64:  false,
	FtStringToString: false,
	FtUint:           true,
	FtUintSlice:      false,
	FtUint8:          false,
	FtUint16:         false,
	FtUint32:         false,
	FtUint64:         false,
}

// Flags is the main type used to access flags and methods.
type Flags struct {
	CommandName string
	Script      string
	Definition  FlagsDefinition
}

// formatErrorItem returns a formatted message indented by the number of
// `level` and prefixed by a `prefix`.
func formatErrorItem(level int, prefix string, format string, a ...interface{}) error {
	return fmt.Errorf("%s %s %s", strings.Repeat(" ", (level-1)*2), prefix, fmt.Sprintf(format, a...))
}

// extractError returns the error message of an error `err` prefixed by a new
// line or an empty string if no error was given.
func extractError(err error) string {
	if err != nil {
		return "\n" + strings.Trim(err.Error(), "\n")
	}

	return ""
}

// validate checks a nameValue for uniqueness.
func (v *nameValue) validate(longOptions *map[nameValue]bool) error {
	// Check flag does not already exist
	if _, found := (*longOptions)[*v]; found {
		return formatErrorItem(2, "-", "flag '%s' already defined", *v)
	}

	(*longOptions)[*v] = true

	return nil
}

// validate checks a shorthandValue for uniqueness and containing one ASCII
// character only.
func (v *shorthandValue) validate(shortOptions *map[shorthandValue]nameValue, name nameValue) error {
	var errors string = ""

	// Check shorthand does not already exist
	if flagOfShorthand, found := (*shortOptions)[*v]; found {
		errors += extractError(formatErrorItem(2, "-", "shorthand '%s' is already defined for flag '%s'", *v, flagOfShorthand))
	} else if *v != "" {
		(*shortOptions)[*v] = name
	}

	// Check shorthand is one letter only
	if len(*v) > 1 {
		errors += extractError(formatErrorItem(2, "-", "shorthand '%s' is more than one ASCII character", *v))
	}

	if errors != "" {
		return fmt.Errorf("%s", errors)
	}

	return nil
}

// validate checks a usageValue to be defined.
func (v *usageValue) validate() error {
	// Check usage is defined
	if *v == "" {
		return formatErrorItem(2, "-", "no usage defined")
	}

	return nil
}

// validate checks a typeValue and sets a default value if empty.
func (v *typeValue) validate() error {
	// Check type and set default if empty
	if *v == "" {
		*v = FtBool
	}

	// Check type is valid
	implemented, found := ValidTypes[*v]

	if !found {
		return formatErrorItem(2, "-", "type '%s' is not known", *v)
	} else if !implemented {
		return formatErrorItem(2, "-", "type '%s' is not implemented", *v)
	}

	return nil
}

// validate checks a defValueValue and sets a default value if empty.
func (v *defValueValue) validate(aType typeValue) error {
	if *v != "" {
		return nil
	}

	// Init the DefValue
	switch aType {
	case FtBool:
		*v = defValueValue(strconv.FormatBool(false))
	case FtCount, FtDuration, FtFloat32, FtFloat64, FtInt, FtInt8, FtInt16, FtInt32, FtUint, FtUint8, FtUint16, FtUint32:
		*v = defValueValue(strconv.FormatInt(0, 10))
	case FtString:
		*v = ""
	case ftTest3: // used for testing only
		*v = ""
	default:
		if implemented := ValidTypes[aType]; implemented {
			// Mandatory implementation missing -> panic
			panic(fmt.Sprintf("Error implementation of DefValue validation missing for type '%s'", aType))
		}
	}

	return nil
}

// validate checks a noOptDefValValue and sets a default value if needed.
func (v *noOptDefValValue) validate(aType typeValue) error {
	if *v != "" {
		return nil
	}

	// Init the NoOptDefValValue
	switch aType {
	case FtBool:
		*v = noOptDefValValue(strconv.FormatBool(true))
	}

	return nil
}

// validate checks a annotationsValue.
func (v *annotationsValue) validate() error {
	return nil
}

// validateFlag checks all fields by calling the corresponding validate method.
func (f *Flag) validateFlag(longOptions *map[nameValue]bool, shortOptions *map[shorthandValue]nameValue) error {
	errors := ""

	// Chech all fields
	errors += extractError(f.Name.validate(longOptions))
	errors += extractError(f.Shorthand.validate(shortOptions, f.Name))
	errors += extractError(f.Usage.validate())
	errors += extractError(f.Type.validate())
	errors += extractError(f.DefValue.validate(f.Type))
	errors += extractError(f.NoOptDefVal.validate(f.Type))
	errors += extractError(f.Annotations.validate())

	if errors != "" {
		return fmt.Errorf("%s", errors)
	}

	return nil
}

// Init initializes the Flags structure.
func (f *Flags) Init(commandName, script string) {
	f.CommandName = commandName
	f.Script = script
}

// validateFlags checks all Flags by calling the validateFlag method.
func (f *Flags) validateFlags(flags *FlagsDefinition) error {
	var errors string

	// Temporay vars to precheck for duplicated flags. It's still possible
	// other commands will introduce the same flags which is tested
	// afterwards by cobra.
	long := map[nameValue]bool{"help": true}
	short := map[shorthandValue]nameValue{"h": "help"}

	for i := range *flags {
		flag := &(*flags)[i]

		// Additional validations of the flag fields
		flagErrors := extractError(flag.validateFlag(&long, &short))

		if flagErrors != "" {
			errors += extractError(formatErrorItem(1, "*", "for flag '%s':%s", flag.Name, flagErrors))
		}
	}

	if errors != "" {
		return fmt.Errorf("The following problems were found in the flags definition of the command '%s' in '%s':%s", f.CommandName, f.Script, errors)
	}

	return nil
}

// LoadFromJSON imports the defs provided by the custom command as json into
// the flags structure.
func (f *Flags) LoadFromJSON(data string) error {
	if data == "" {
		return nil
	}

	var err error
	var defs = FlagsDefinition{}

	// Import the JSON to the FlagsDefinition structure and return in case of
	// error
	if err = json.Unmarshal([]byte(data), &defs); err != nil {
		return err
	}

	// Validate the user provided flags and return in case of error
	if err = f.validateFlags(&defs); err != nil {
		return err
	}

	// Assign the data to the field
	f.Definition = defs
	return nil
}

// AssignToCommand iterates the flags and assigns it to the provided command.
func (f *Flags) AssignToCommand(command *cobra.Command) error {
	var errors string

	for _, flag := range f.Definition {
		// Create the flag at the command
		switch flag.Type {
		case FtBool /*, ""*/ : // empty type defaults to bool
			command.Flags().BoolP(string(flag.Name), string(flag.Shorthand), false, string(flag.Usage))
		case FtInt:
			command.Flags().IntP(string(flag.Name), string(flag.Shorthand), 0, string(flag.Usage))
		case FtString:
			command.Flags().StringP(string(flag.Name), string(flag.Shorthand), "", string(flag.Usage))
		case FtUint:
			command.Flags().UintP(string(flag.Name), string(flag.Shorthand), 0, string(flag.Usage))
		default:
			if implemented := ValidTypes[flag.Type]; implemented {
				// Mandatory implementation missing -> panic
				panic(fmt.Sprintf("Error implementation missing for type '%s'", flag.Type))
			}

			continue // continue here, nothing to set for this flag
		}

		// Update default values and annotations
		newFlag := command.Flags().Lookup(string(flag.Name))

		if err := newFlag.Value.Set(string(flag.DefValue)); err != nil {
			// Invalid default value was defined by the user, hide the flag
			// and save the error
			newFlag.Hidden = true
			errors += extractError(formatErrorItem(1, "-", "error '%s' while set value of flag '%s'", err.Error(), flag.Name))
		}

		newFlag.DefValue = string(flag.DefValue)
		newFlag.NoOptDefVal = string(flag.NoOptDefVal)
		newFlag.Annotations = flag.Annotations
	}

	if errors != "" {
		return fmt.Errorf("The following problems were found while assigning the flags to the command '%s' in '%s':%s", f.CommandName, f.Script, errors)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import "text/template"

type header struct {
	Pkg      string
	Function string
	Tags     string
	Fallback bool
}

type file struct {
	Name    string
	Data    string
	Size    int64
	Mode    uint32
	ModTime int64
}

var (
	headerTmpl      = template.Must(template.New("gen-header").Parse(headerData))
	emptyHeaderTmpl = template.Must(template.New("gen-empty-header").Parse(emptyHeaderData))
	fileTmpl        = template.Must(template.New("gen-file").Parse(fileData))
	footerTmpl      = template.Must(template.New("gen-footer").Parse(footerData))
)

const (
	headerData = `
{{- if .Tags }}// +build {{ .Tags }}
{{- end }}

// DO NOT EDIT ** This file was generated with github.com/urandom/embed ** DO NOT EDIT //

package {{ .Pkg }}

import (
	"fmt"
	"os"
	"time"

	"github.com/pkg/errors"
	"github.com/urandom/embed/filesystem"
)

func {{ .Function }}() (*filesystem.FileSystem, error) {
	fs := filesystem.New()
{{ if .Fallback }}
	fs.Fallback = true
{{ end -}}
`

	emptyHeaderData = `
{{- if .Tags }}// +build {{ .Tags }}
{{- end }}

// DO NOT EDIT ** This file was generated with github.com/urandom/embed ** DO NOT EDIT //

package {{ .Pkg }}

import (
	"github.com/urandom/embed/filesystem"
)

func {{ .Function }}() (*filesystem.FileSystem, error) {
	fs := filesystem.New()
{{ if .Fallback }}
	fs.Fallback = true
{{ end -}}
`

	fileData = `
	if err := fs.Add("{{ .Name }}", {{ .Size }}, os.FileMode({{ .Mode }}), time.Unix({{ .ModTime }}, 0), {{ .Data }}); err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("packing file {{ .Name }}"))
	}
`

	footerData = `
	return fs, nil
}
`
)
<commit_msg>Change the template to return an http.FileSystem interface<commit_after>package main

import "text/template"

type header struct {
	Pkg      string
	Function string
	Tags     string
	Fallback bool
}

type file struct {
	Name    string
	Data    string
	Size    int64
	Mode    uint32
	ModTime int64
}

var (
	headerTmpl      = template.Must(template.New("gen-header").Parse(headerData))
	emptyHeaderTmpl = template.Must(template.New("gen-empty-header").Parse(emptyHeaderData))
	fileTmpl        = template.Must(template.New("gen-file").Parse(fileData))
	footerTmpl      = template.Must(template.New("gen-footer").Parse(footerData))
)

const (
	headerData = `
{{- if .Tags }}// +build {{ .Tags }}
{{- end }}

// DO NOT EDIT ** This file was generated with github.com/urandom/embed ** DO NOT EDIT //

package {{ .Pkg }}

import (
	"fmt"
	"net/http"
	"os"
	"time"

	"github.com/pkg/errors"
	"github.com/urandom/embed/filesystem"
)

// {{ .Function }} creates a new filesystem with pre-filled binary data.
func {{ .Function }}() (http.FileSystem, error) {
	fs := filesystem.New()
{{ if .Fallback }}
	fs.Fallback = true
{{ end -}}
`

	emptyHeaderData = `
{{- if .Tags }}// +build {{ .Tags }}
{{- end }}

// DO NOT EDIT ** This file was generated with github.com/urandom/embed ** DO NOT EDIT //

package {{ .Pkg }}

import (
	"net/http"

	"github.com/urandom/embed/filesystem"
)

// {{ .Function }} creates a new empty filesystem.
func {{ .Function }}() (http.FileSystem, error) {
	fs := filesystem.New()
{{ if .Fallback }}
	fs.Fallback = true
{{ end -}}
`

	fileData = `
	if err := fs.Add("{{ .Name }}", {{ .Size }}, os.FileMode({{ .Mode }}), time.Unix({{ .ModTime }}, 0), {{ .Data }}); err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("packing file {{ .Name }}"))
	}
`

	footerData = `
	return fs, nil
}
`
)
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/brutella/hc/accessory"
	"github.com/hemtjanst/hemtjanst/device"
	"github.com/hemtjanst/hemtjanst/homekit"
	"github.com/hemtjanst/hemtjanst/homekit/bridge"
	"github.com/hemtjanst/hemtjanst/messaging"
	"github.com/hemtjanst/hemtjanst/messaging/flagmqtt"
	"github.com/hemtjanst/hemtjanst/web"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

var (
	addr     = flag.String("address", "127.0.0.1", "IP or hostname for Hemtjänst to bind on")
	port     = flag.String("port", "12345", "Port for Hemtjänst to bind on")
	pin      = flag.String("pin", "01020304", "Pairing pin for the HomeKit bridge")
	startWeb = flag.Bool("web.ui", false, "Start the built-in web UI")
	wAddr    = flag.String("web.addr", ":8080", "IP/host:port to bind the webinterface to")
	dbPath   = flag.String("db.path", "./db", "Path to store the database with HomeKit key pairs etc.")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Parameters:\n\n")
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\n")
	}
	flag.Parse()

	log.Print("Initialing Hemtjänst")
	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	announce := make(chan []byte)
	leave := make(chan []byte)

	bridgeConfig := bridge.Config{
		Pin:         *pin,
		Port:        *port,
		StoragePath: *dbPath,
	}
	bridgeInfo := accessory.Info{
		Name:         "Hemtjänst",
		SerialNumber: "12345",
		Manufacturer: "BEDS Inc.",
		Model:        "v0.1",
	}

	log.Print("Attempting to connect to MQTT broker")
	handler := &messaging.Handler{
		Ann:   announce,
		Leave: leave,
	}
	cID := flagmqtt.NewUniqueIdentifier()
	conf := flagmqtt.ClientConfig{
		ClientID:                fmt.Sprintf("hemtjanst-%s", cID),
		OnConnectHandler:        handler.OnConnect,
		OnConnectionLostHandler: handler.OnConnectionLost,
		WillTopic:               "leave",
		WillPayload:             fmt.Sprintf("hemtjanst-%s", cID),
		WillRetain:              false,
		WillQoS:                 0,
	}

	c, err := flagmqtt.NewPersistentMqtt(conf)
	if err != nil {
		log.Fatal("Could not configure the MQTT client: ", err)
	}

	go func() {
		if token := c.Connect(); token.Wait() && token.Error() != nil {
			log.Fatal("Failed to establish connection with broker: ", token.Error())
		}
	}()

	hkBridge, err := bridge.NewBridge(bridgeConfig, bridgeInfo)
	if err != nil {
		log.Fatal("Could not start HomeKit bridge: ", err)
	}

	manager := device.NewManager(messaging.NewMQTTMessenger(c))
	log.Print("Started device manager")

	hk := homekit.NewHomekit(hkBridge, manager)
	manager.AddHandler(hk)

	go func() {
		<-time.After(2 * time.Second)
		hkBridge.Start()
	}()
	log.Print("Started HomeKit bridge")

	if *startWeb {
		go func() {
			<-time.After(5 * time.Second)
			web.Serve(manager, *wAddr)
		}()
		log.Print("Started web interface")
	}

loop:
	for {
		select {
		case sig := <-quit:
			log.Printf("Received signal: %s, proceeding to shutdown", sig)
			break loop
		case msg := <-announce:
			newReg := string(msg)
			log.Print("New announcement: ", newReg)
			if !strings.Contains(newReg, "/") {
				// We expect topics we care about to contain at least 1 /
				break
			}
			go manager.Add(newReg)
		case msg := <-leave:
			go manager.Remove(string(msg))
		}
	}

	// When the MQTT lib is connecting but hasn't establish a conneciton yet
	// the IsConnected() method returns true. As a consequence, b/c it believes
	// it is connected the call to .Disconnect() will panic if we terminate
	// before we've managed to establish a connection to the broker, as it
	// tries to close one of its own channels that are currently still nil.
	//
	// To avoid getting an ugly panic printed for what is arguably a bug in the
	// library defer a recover that does nothing and exit normally.
	defer func() {
		recover()
	}()

	c.Disconnect(250)
	hkBridge.Stop()
	log.Print("Disconnected from broker. Bye!")
	os.Exit(0)
}
<commit_msg>:pencil: Pass down address to bind on<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/brutella/hc/accessory"
	"github.com/hemtjanst/hemtjanst/device"
	"github.com/hemtjanst/hemtjanst/homekit"
	"github.com/hemtjanst/hemtjanst/homekit/bridge"
	"github.com/hemtjanst/hemtjanst/messaging"
	"github.com/hemtjanst/hemtjanst/messaging/flagmqtt"
	"github.com/hemtjanst/hemtjanst/web"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

var (
	addr     = flag.String("address", "127.0.0.1", "IP or hostname for Hemtjänst to bind on")
	port     = flag.String("port", "12345", "Port for Hemtjänst to bind on")
	pin      = flag.String("pin", "01020304", "Pairing pin for the HomeKit bridge")
	startWeb = flag.Bool("web.ui", false, "Start the built-in web UI")
	wAddr    = flag.String("web.addr", ":8080", "IP/host:port to bind the webinterface to")
	dbPath   = flag.String("db.path", "./db", "Path to store the database with HomeKit key pairs etc.")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Parameters:\n\n")
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\n")
	}
	flag.Parse()

	log.Print("Initialing Hemtjänst")
	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	announce := make(chan []byte)
	leave := make(chan []byte)

	bridgeConfig := bridge.Config{
		Pin:         *pin,
		Port:        *port,
		IP:          *addr,
		StoragePath: *dbPath,
	}
	bridgeInfo := accessory.Info{
		Name:         "Hemtjänst",
		SerialNumber: "12345",
		Manufacturer: "BEDS Inc.",
		Model:        "v0.1",
	}

	log.Print("Attempting to connect to MQTT broker")
	handler := &messaging.Handler{
		Ann:   announce,
		Leave: leave,
	}
	cID := flagmqtt.NewUniqueIdentifier()
	conf := flagmqtt.ClientConfig{
		ClientID:                fmt.Sprintf("hemtjanst-%s", cID),
		OnConnectHandler:        handler.OnConnect,
		OnConnectionLostHandler: handler.OnConnectionLost,
		WillTopic:               "leave",
		WillPayload:             fmt.Sprintf("hemtjanst-%s", cID),
		WillRetain:              false,
		WillQoS:                 0,
	}

	c, err := flagmqtt.NewPersistentMqtt(conf)
	if err != nil {
		log.Fatal("Could not configure the MQTT client: ", err)
	}

	go func() {
		if token := c.Connect(); token.Wait() && token.Error() != nil {
			log.Fatal("Failed to establish connection with broker: ", token.Error())
		}
	}()

	hkBridge, err := bridge.NewBridge(bridgeConfig, bridgeInfo)
	if err != nil {
		log.Fatal("Could not start HomeKit bridge: ", err)
	}

	manager := device.NewManager(messaging.NewMQTTMessenger(c))
	log.Print("Started device manager")

	hk := homekit.NewHomekit(hkBridge, manager)
	manager.AddHandler(hk)

	go func() {
		<-time.After(2 * time.Second)
		hkBridge.Start()
	}()
	log.Print("Started HomeKit bridge")

	if *startWeb {
		go func() {
			<-time.After(5 * time.Second)
			web.Serve(manager, *wAddr)
		}()
		log.Print("Started web interface")
	}

loop:
	for {
		select {
		case sig := <-quit:
			log.Printf("Received signal: %s, proceeding to shutdown", sig)
			break loop
		case msg := <-announce:
			newReg := string(msg)
			log.Print("New announcement: ", newReg)
			if !strings.Contains(newReg, "/") {
				// We expect topics we care about to contain at least 1 /
				break
			}
			go manager.Add(newReg)
		case msg := <-leave:
			go manager.Remove(string(msg))
		}
	}

	// When the MQTT lib is connecting but hasn't establish a conneciton yet
	// the IsConnected() method returns true. As a consequence, b/c it believes
	// it is connected the call to .Disconnect() will panic if we terminate
	// before we've managed to establish a connection to the broker, as it
	// tries to close one of its own channels that are currently still nil.
	//
	// To avoid getting an ugly panic printed for what is arguably a bug in the
	// library defer a recover that does nothing and exit normally.
	defer func() {
		recover()
	}()

	c.Disconnect(250)
	hkBridge.Stop()
	log.Print("Disconnected from broker. Bye!")
	os.Exit(0)
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"fmt"
	"os"

	"launchpad.net/gnuflag"

	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/cmd/envcmd"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/bootstrap"
	"launchpad.net/juju-core/environs/imagemetadata"
	"launchpad.net/juju-core/environs/tools"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/provider"
)

const bootstrapDoc = `
bootstrap starts a new environment of the current type (it will return an error
if the environment has already been bootstrapped).  Bootstrapping an environment
will provision a new machine in the environment and run the juju state server on
that machine.

If constraints are specified in the bootstrap command, they will apply to the
machine provisioned for the juju state server.  They will also be set as default
constraints on the environment for all future machines, exactly as if the
constraints were set with juju set-constraints.

Bootstrap initializes the cloud environment synchronously and displays information
about the current installation steps.  The time for bootstrap to complete varies
across cloud providers from a few seconds to several minutes.  Once bootstrap has
completed, you can run other juju commands against your environment. You can change
the default timeout and retry delays used during the bootstrap by changing the
following settings in your environments.yaml (all values represent number of seconds):

    # How long to wait for a connection to the state server.
    bootstrap-timeout: 600 # default: 10 minutes
    # How long to wait between connection attempts to a state server address.
    bootstrap-retry-delay: 5 # default: 5 seconds
    # How often to refresh state server addresses from the API server.
    bootstrap-addresses-delay: 10 # default: 10 seconds

Private clouds may need to specify their own custom image metadata, and possibly upload
Juju tools to cloud storage if no outgoing Internet access is available. In this case,
use the --metadata-source paramater to tell bootstrap a local directory from which to
upload tools and/or image metadata.

See Also:
   juju help switch
   juju help constraints
   juju help set-constraints
`

// BootstrapCommand is responsible for launching the first machine in a juju
// environment, and setting up everything necessary to continue working.
type BootstrapCommand struct {
	envcmd.EnvCommandBase
	Constraints    constraints.Value
	UploadTools    bool
	Series         []string
	MetadataSource string
	Placement      string
}

func (c *BootstrapCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "bootstrap",
		Purpose: "start up an environment from scratch",
		Doc:     bootstrapDoc,
	}
}

func (c *BootstrapCommand) SetFlags(f *gnuflag.FlagSet) {
	c.EnvCommandBase.SetFlags(f)
	f.Var(constraints.ConstraintsValue{Target: &c.Constraints}, "constraints", "set environment constraints")
	f.BoolVar(&c.UploadTools, "upload-tools", false, "upload local version of tools before bootstrapping")
	f.Var(newSeriesValue(nil, &c.Series), "series", "upload tools for supplied comma-separated series list")
	f.StringVar(&c.MetadataSource, "metadata-source", "", "local path to use as tools and/or metadata source")
	f.StringVar(&c.Placement, "to", "", "a placement directive indicating an instance to bootstrap")
}

func (c *BootstrapCommand) Init(args []string) (err error) {
	err = c.EnvCommandBase.Init()
	if err != nil {
		return
	}
	if len(c.Series) > 0 && !c.UploadTools {
		return fmt.Errorf("--series requires --upload-tools")
	}
	// Parse the placement directive. Bootstrap currently only
	// supports provider-specific placement directives.
	if c.Placement != "" {
		_, err = instance.ParsePlacement(c.Placement)
		if err != instance.ErrPlacementScopeMissing {
			// We only support unscoped placement directives for bootstrap.
			return fmt.Errorf("unsupported bootstrap placement directive %q", c.Placement)
		}
	}
	return cmd.CheckEmpty(args)
}

type seriesValue struct {
	*cmd.StringsValue
}

// newSeriesValue is used to create the type passed into the gnuflag.FlagSet Var function.
func newSeriesValue(defaultValue []string, target *[]string) *seriesValue {
	v := seriesValue{(*cmd.StringsValue)(target)}
	*(v.StringsValue) = defaultValue
	return &v
}

// Implements gnuflag.Value Set.
func (v *seriesValue) Set(s string) error {
	if err := v.StringsValue.Set(s); err != nil {
		return err
	}
	for _, name := range *(v.StringsValue) {
		if !charm.IsValidSeries(name) {
			return fmt.Errorf("invalid series name %q", name)
		}
	}
	return nil
}

// Run connects to the environment specified on the command line and bootstraps
// a juju in that environment if none already exists. If there is as yet no environments.yaml file,
// the user is informed how to create one.
func (c *BootstrapCommand) Run(ctx *cmd.Context) (resultErr error) {
	environ, cleanup, err := environFromName(ctx, c.EnvName, &resultErr, "Bootstrap")
	if err != nil {
		return err
	}
	validator, err := environ.ConstraintsValidator()
	if err != nil {
		return err
	}
	unsupported, err := validator.Validate(c.Constraints)
	if len(unsupported) > 0 {
		logger.Warningf("unsupported constraints: %v", err)
	} else if err != nil {
		return err
	}

	defer cleanup()
	if err := bootstrap.EnsureNotBootstrapped(environ); err != nil {
		return err
	}

	// Block interruption during bootstrap. Providers may also
	// register for interrupt notification so they can exit early.
	interrupted := make(chan os.Signal, 1)
	defer close(interrupted)
	ctx.InterruptNotify(interrupted)
	defer ctx.StopInterruptNotify(interrupted)
	go func() {
		for _ = range interrupted {
			ctx.Infof("Interrupt signalled: waiting for bootstrap to exit")
		}
	}()

	// If --metadata-source is specified, override the default tools metadata source so
	// SyncTools can use it, and also upload any image metadata.
	if c.MetadataSource != "" {
		metadataDir := ctx.AbsPath(c.MetadataSource)
		logger.Infof("Setting default tools and image metadata sources: %s", metadataDir)
		tools.DefaultBaseURL = metadataDir
		if err := imagemetadata.UploadImageMetadata(environ.Storage(), metadataDir); err != nil {
			// Do not error if image metadata directory doesn't exist.
			if !os.IsNotExist(err) {
				return fmt.Errorf("uploading image metadata: %v", err)
			}
		} else {
			logger.Infof("custom image metadata uploaded")
		}
	}
	// TODO (wallyworld): 2013-09-20 bug 1227931
	// We can set a custom tools data source instead of doing an
	// unnecessary upload.
	if environ.Config().Type() == provider.Local {
		c.UploadTools = true
	}
	if c.UploadTools {
		err = bootstrap.UploadTools(ctx, environ, c.Constraints.Arch, true, c.Series...)
		if err != nil {
			return err
		}
	}
	return bootstrap.Bootstrap(ctx, environ, environs.BootstrapParams{
		Constraints: c.Constraints,
		Placement:   c.Placement,
	})
}
<commit_msg>[r=menno.smits],[bug=1302005] cmd/juju: refactor --series to use StringsValue<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"fmt"
	"os"

	"launchpad.net/gnuflag"

	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/cmd/envcmd"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/bootstrap"
	"launchpad.net/juju-core/environs/imagemetadata"
	"launchpad.net/juju-core/environs/tools"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/provider"
)

const bootstrapDoc = `
bootstrap starts a new environment of the current type (it will return an error
if the environment has already been bootstrapped).  Bootstrapping an environment
will provision a new machine in the environment and run the juju state server on
that machine.

If constraints are specified in the bootstrap command, they will apply to the
machine provisioned for the juju state server.  They will also be set as default
constraints on the environment for all future machines, exactly as if the
constraints were set with juju set-constraints.

Bootstrap initializes the cloud environment synchronously and displays information
about the current installation steps.  The time for bootstrap to complete varies
across cloud providers from a few seconds to several minutes.  Once bootstrap has
completed, you can run other juju commands against your environment. You can change
the default timeout and retry delays used during the bootstrap by changing the
following settings in your environments.yaml (all values represent number of seconds):

    # How long to wait for a connection to the state server.
    bootstrap-timeout: 600 # default: 10 minutes
    # How long to wait between connection attempts to a state server address.
    bootstrap-retry-delay: 5 # default: 5 seconds
    # How often to refresh state server addresses from the API server.
    bootstrap-addresses-delay: 10 # default: 10 seconds

Private clouds may need to specify their own custom image metadata, and possibly upload
Juju tools to cloud storage if no outgoing Internet access is available. In this case,
use the --metadata-source paramater to tell bootstrap a local directory from which to
upload tools and/or image metadata.

See Also:
   juju help switch
   juju help constraints
   juju help set-constraints
`

// BootstrapCommand is responsible for launching the first machine in a juju
// environment, and setting up everything necessary to continue working.
type BootstrapCommand struct {
	envcmd.EnvCommandBase
	Constraints    constraints.Value
	UploadTools    bool
	Series         []string
	MetadataSource string
	Placement      string
}

func (c *BootstrapCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "bootstrap",
		Purpose: "start up an environment from scratch",
		Doc:     bootstrapDoc,
	}
}

func (c *BootstrapCommand) SetFlags(f *gnuflag.FlagSet) {
	c.EnvCommandBase.SetFlags(f)
	f.Var(constraints.ConstraintsValue{Target: &c.Constraints}, "constraints", "set environment constraints")
	f.BoolVar(&c.UploadTools, "upload-tools", false, "upload local version of tools before bootstrapping")
	f.Var(newSeriesValue(nil, &c.Series), "series", "upload tools for supplied comma-separated series list")
	f.StringVar(&c.MetadataSource, "metadata-source", "", "local path to use as tools and/or metadata source")
	f.StringVar(&c.Placement, "to", "", "a placement directive indicating an instance to bootstrap")
}

func (c *BootstrapCommand) Init(args []string) (err error) {
	err = c.EnvCommandBase.Init()
	if err != nil {
		return
	}
	if len(c.Series) > 0 && !c.UploadTools {
		return fmt.Errorf("--series requires --upload-tools")
	}
	// Parse the placement directive. Bootstrap currently only
	// supports provider-specific placement directives.
	if c.Placement != "" {
		_, err = instance.ParsePlacement(c.Placement)
		if err != instance.ErrPlacementScopeMissing {
			// We only support unscoped placement directives for bootstrap.
			return fmt.Errorf("unsupported bootstrap placement directive %q", c.Placement)
		}
	}
	return cmd.CheckEmpty(args)
}

type seriesValue struct {
	*cmd.StringsValue
}

// newSeriesValue is used to create the type passed into the gnuflag.FlagSet Var function.
func newSeriesValue(defaultValue []string, target *[]string) *seriesValue {
	v := seriesValue{(*cmd.StringsValue)(target)}
	*(v.StringsValue) = defaultValue
	return &v
}

// Implements gnuflag.Value Set.
func (v *seriesValue) Set(s string) error {
	if err := v.StringsValue.Set(s); err != nil {
		return err
	}
	for _, name := range *(v.StringsValue) {
		if !charm.IsValidSeries(name) {
			v.StringsValue = nil
			return fmt.Errorf("invalid series name %q", name)
		}
	}
	return nil
}

// Run connects to the environment specified on the command line and bootstraps
// a juju in that environment if none already exists. If there is as yet no environments.yaml file,
// the user is informed how to create one.
func (c *BootstrapCommand) Run(ctx *cmd.Context) (resultErr error) {
	environ, cleanup, err := environFromName(ctx, c.EnvName, &resultErr, "Bootstrap")
	if err != nil {
		return err
	}
	validator, err := environ.ConstraintsValidator()
	if err != nil {
		return err
	}
	unsupported, err := validator.Validate(c.Constraints)
	if len(unsupported) > 0 {
		logger.Warningf("unsupported constraints: %v", err)
	} else if err != nil {
		return err
	}

	defer cleanup()
	if err := bootstrap.EnsureNotBootstrapped(environ); err != nil {
		return err
	}

	// Block interruption during bootstrap. Providers may also
	// register for interrupt notification so they can exit early.
	interrupted := make(chan os.Signal, 1)
	defer close(interrupted)
	ctx.InterruptNotify(interrupted)
	defer ctx.StopInterruptNotify(interrupted)
	go func() {
		for _ = range interrupted {
			ctx.Infof("Interrupt signalled: waiting for bootstrap to exit")
		}
	}()

	// If --metadata-source is specified, override the default tools metadata source so
	// SyncTools can use it, and also upload any image metadata.
	if c.MetadataSource != "" {
		metadataDir := ctx.AbsPath(c.MetadataSource)
		logger.Infof("Setting default tools and image metadata sources: %s", metadataDir)
		tools.DefaultBaseURL = metadataDir
		if err := imagemetadata.UploadImageMetadata(environ.Storage(), metadataDir); err != nil {
			// Do not error if image metadata directory doesn't exist.
			if !os.IsNotExist(err) {
				return fmt.Errorf("uploading image metadata: %v", err)
			}
		} else {
			logger.Infof("custom image metadata uploaded")
		}
	}
	// TODO (wallyworld): 2013-09-20 bug 1227931
	// We can set a custom tools data source instead of doing an
	// unnecessary upload.
	if environ.Config().Type() == provider.Local {
		c.UploadTools = true
	}
	if c.UploadTools {
		err = bootstrap.UploadTools(ctx, environ, c.Constraints.Arch, true, c.Series...)
		if err != nil {
			return err
		}
	}
	return bootstrap.Bootstrap(ctx, environ, environs.BootstrapParams{
		Constraints: c.Constraints,
		Placement:   c.Placement,
	})
}
<|endoftext|>


<commit_before>// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	json "github.com/minio/colorjson"
	"github.com/minio/mc/pkg/probe"
	"github.com/minio/minio-go/v7/pkg/replication"
	"github.com/minio/pkg/console"
)

var replicateEditFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "id",
		Usage: "id for the rule, should be a unique value",
	},
	cli.StringFlag{
		Name:  "tags",
		Usage: "format '<key1>=<value1>&<key2>=<value2>&<key3>=<value3>', multiple values allowed for multiple key/value pairs",
	},
	cli.StringFlag{
		Name:  "storage-class",
		Usage: `storage class for destination, valid values are either "STANDARD" or "REDUCED_REDUNDANCY"`,
	},
	cli.StringFlag{
		Name:  "state",
		Usage: "change rule status. Valid values are [enable|disable]",
	},
	cli.IntFlag{
		Name:  "priority",
		Usage: "priority of the rule, should be unique and is a required field",
	},
	cli.StringFlag{
		Name:  "remote-bucket",
		Usage: "destination bucket, should be a unique value for the configuration",
	},
	cli.StringFlag{
		Name:  "replicate",
		Usage: `comma separated list to enable replication of soft deletes, permanent deletes, existing objects and metadata sync. Valid options are "delete-marker","delete","existing-objects","metadata-sync" and ""'`,
	},
}

var replicateEditCmd = cli.Command{
	Name:         "edit",
	Usage:        "modify an existing server side replication configuration rule",
	Action:       mainReplicateEdit,
	OnUsageError: onUsageError,
	Before:       setGlobalsFromContext,
	Flags:        append(globalFlags, replicateEditFlags...),
	CustomHelpTemplate: `NAME:
  {{.HelpName}} - {{.Usage}}

USAGE:
  {{.HelpName}} TARGET

FLAGS:
  {{range .VisibleFlags}}{{.}}
  {{end}}
EXAMPLES:
  1. Change priority of rule with rule ID "bsibgh8t874dnjst8hkg" on bucket "mybucket" for alias "myminio".
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "bsibgh8t874dnjst8hkg"  --priority 3

  2. Disable a replication configuration rule with rule ID "bsibgh8t874dnjst8hkg" on target myminio/bucket
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "bsibgh8t874dnjst8hkg" --state disable

  3. Set tags and storage class on a replication configuration with rule ID "kMYD.491" on target myminio/bucket/prefix.
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "kMYD.491" --tags "key1=value1&key2=value2" \
				  --storage-class "STANDARD" --priority 2
  4. Clear tags for replication configuration rule with ID "kMYD.491" on a target myminio/bucket.
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "kMYD.491" --tags ""

  5. Enable delete marker replication on a replication configuration rule with ID "kxYD.491" on a target myminio/bucket.
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "kxYD.491" --replicate "delete-marker"

  6. Disable delete marker and versioned delete replication on a replication configuration rule with ID "kxYD.491" on a target myminio/bucket.
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "kxYD.491" --replicate ""

  7. Enable existing object replication on a configuration rule with ID "kxYD.491" on a target myminio/bucket. Rule previously had enabled delete marker and versioned delete replication.
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "kxYD.491" --replicate "existing-objects,delete-marker,delete"
`,
}

// checkReplicateEditSyntax - validate all the passed arguments
func checkReplicateEditSyntax(ctx *cli.Context) {
	if len(ctx.Args()) != 1 {
		showCommandHelpAndExit(ctx, "edit", 1) // last argument is exit code
	}
}

type replicateEditMessage struct {
	Op     string `json:"op"`
	Status string `json:"status"`
	URL    string `json:"url"`
	ID     string `json:"id"`
}

func (l replicateEditMessage) JSON() string {
	l.Status = "success"
	jsonMessageBytes, e := json.MarshalIndent(l, "", " ")
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
	return string(jsonMessageBytes)
}

func (l replicateEditMessage) String() string {
	if l.ID != "" {
		return console.Colorize("replicateEditMessage", "Replication configuration rule with ID `"+l.ID+"` applied to "+l.URL+".")
	}
	return console.Colorize("replicateEditMessage", "Replication configuration rule applied to "+l.URL+" successfully.")
}

func mainReplicateEdit(cliCtx *cli.Context) error {
	ctx, cancelReplicateEdit := context.WithCancel(globalContext)
	defer cancelReplicateEdit()

	console.SetColor("replicateEditMessage", color.New(color.FgGreen))

	checkReplicateEditSyntax(cliCtx)

	// Get the alias parameter from cli
	args := cliCtx.Args()
	aliasedURL := args.Get(0)
	// Create a new Client
	client, err := newClient(aliasedURL)
	fatalIf(err, "Unable to initialize connection.")
	rcfg, err := client.GetReplication(ctx)
	fatalIf(err.Trace(args...), "Unable to get replication configuration")

	if !cliCtx.IsSet("id") {
		fatalIf(errInvalidArgument(), "--id is a required flag")
	}
	var state string
	if cliCtx.IsSet("state") {
		state = strings.ToLower(cliCtx.String("state"))
		if state != "enable" && state != "disable" {
			fatalIf(err.Trace(args...), "--state can be either `enable` or `disable`")
		}
	}
	var vDeleteReplicate, dmReplicate, replicasync, existingReplState string
	if cliCtx.IsSet("replicate") {
		replSlice := strings.Split(cliCtx.String("replicate"), ",")
		vDeleteReplicate = disableStatus
		dmReplicate = disableStatus
		replicasync = disableStatus
		existingReplState = disableStatus

		for _, opt := range replSlice {
			switch strings.TrimSpace(strings.ToLower(opt)) {
			case "delete-marker":
				dmReplicate = enableStatus
			case "delete":
				vDeleteReplicate = enableStatus
			case "metadata-sync", "replica-metadata-sync":
				replicasync = enableStatus
			case "existing-objects":
				existingReplState = enableStatus
			default:
				if opt != "" {
					fatalIf(probe.NewError(fmt.Errorf("invalid value for --replicate flag %s", cliCtx.String("replicate"))), `--replicate flag takes one or more comma separated string with values "delete", "delete-marker", "metadata-sync", "existing-objects" or "" to disable these settings`)
				}
			}
		}
	}

	opts := replication.Options{
		TagString:    cliCtx.String("tags"),
		RoleArn:      cliCtx.String("arn"),
		StorageClass: cliCtx.String("storage-class"),
		RuleStatus:   state,
		ID:           cliCtx.String("id"),
		Op:           replication.SetOption,
		DestBucket:   cliCtx.String("remote-bucket"),
		IsSCSet:      cliCtx.IsSet("storage-class"),
		IsTagSet:     cliCtx.IsSet("tags"),
	}

	if cliCtx.IsSet("priority") {
		opts.Priority = strconv.Itoa(cliCtx.Int("priority"))
	}
	if cliCtx.IsSet("replicate") {
		opts.ReplicateDeletes = vDeleteReplicate
		opts.ReplicateDeleteMarkers = dmReplicate
		opts.ReplicaSync = replicasync
		opts.ExistingObjectReplicate = existingReplState
	}

	fatalIf(client.SetReplication(ctx, &rcfg, opts), "Could not modify replication rule")
	printMsg(replicateEditMessage{
		Op:  "set",
		URL: aliasedURL,
		ID:  opts.ID,
	})
	return nil
}
<commit_msg>Updating usage for mc replicate edit (#4328)<commit_after>// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	json "github.com/minio/colorjson"
	"github.com/minio/mc/pkg/probe"
	"github.com/minio/minio-go/v7/pkg/replication"
	"github.com/minio/pkg/console"
)

var replicateEditFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "id",
		Usage: "id for the rule, should be a unique value",
	},
	cli.StringFlag{
		Name:  "tags",
		Usage: "format '<key1>=<value1>&<key2>=<value2>&<key3>=<value3>', multiple values allowed for multiple key/value pairs",
	},
	cli.StringFlag{
		Name:  "storage-class",
		Usage: `storage class for destination, valid values are either "STANDARD" or "REDUCED_REDUNDANCY"`,
	},
	cli.StringFlag{
		Name:  "state",
		Usage: "change rule status. Valid values are [enable|disable]",
	},
	cli.IntFlag{
		Name:  "priority",
		Usage: "priority of the rule, should be unique and is a required field",
	},
	cli.StringFlag{
		Name:  "remote-bucket",
		Usage: "destination bucket, should be a unique value for the configuration",
	},
	cli.StringFlag{
		Name:  "replicate",
		Usage: `comma separated list to enable replication of soft deletes, permanent deletes, existing objects and metadata sync. Valid options are "delete-marker","delete","existing-objects","metadata-sync" and ""'`,
	},
}

var replicateEditCmd = cli.Command{
	Name:         "edit",
	Usage:        "modify an existing server side replication configuration rule",
	Action:       mainReplicateEdit,
	OnUsageError: onUsageError,
	Before:       setGlobalsFromContext,
	Flags:        append(globalFlags, replicateEditFlags...),
	CustomHelpTemplate: `NAME:
  {{.HelpName}} - {{.Usage}}

USAGE:
  {{.HelpName}} TARGET --id=RULE-ID [FLAGS]

FLAGS:
  {{range .VisibleFlags}}{{.}}
  {{end}}
EXAMPLES:
  1. Change priority of rule with rule ID "bsibgh8t874dnjst8hkg" on bucket "mybucket" for alias "myminio".
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "bsibgh8t874dnjst8hkg"  --priority 3

  2. Disable a replication configuration rule with rule ID "bsibgh8t874dnjst8hkg" on target myminio/bucket
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "bsibgh8t874dnjst8hkg" --state disable

  3. Set tags and storage class on a replication configuration with rule ID "kMYD.491" on target myminio/bucket/prefix.
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "kMYD.491" --tags "key1=value1&key2=value2" \
				  --storage-class "STANDARD" --priority 2
  4. Clear tags for replication configuration rule with ID "kMYD.491" on a target myminio/bucket.
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "kMYD.491" --tags ""

  5. Enable delete marker replication on a replication configuration rule with ID "kxYD.491" on a target myminio/bucket.
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "kxYD.491" --replicate "delete-marker"

  6. Disable delete marker and versioned delete replication on a replication configuration rule with ID "kxYD.491" on a target myminio/bucket.
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "kxYD.491" --replicate ""

  7. Enable existing object replication on a configuration rule with ID "kxYD.491" on a target myminio/bucket. Rule previously had enabled delete marker and versioned delete replication.
     {{.Prompt}} {{.HelpName}} myminio/mybucket --id "kxYD.491" --replicate "existing-objects,delete-marker,delete"
`,
}

// checkReplicateEditSyntax - validate all the passed arguments
func checkReplicateEditSyntax(ctx *cli.Context) {
	if len(ctx.Args()) != 1 {
		showCommandHelpAndExit(ctx, "edit", 1) // last argument is exit code
	}
}

type replicateEditMessage struct {
	Op     string `json:"op"`
	Status string `json:"status"`
	URL    string `json:"url"`
	ID     string `json:"id"`
}

func (l replicateEditMessage) JSON() string {
	l.Status = "success"
	jsonMessageBytes, e := json.MarshalIndent(l, "", " ")
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
	return string(jsonMessageBytes)
}

func (l replicateEditMessage) String() string {
	if l.ID != "" {
		return console.Colorize("replicateEditMessage", "Replication configuration rule with ID `"+l.ID+"` applied to "+l.URL+".")
	}
	return console.Colorize("replicateEditMessage", "Replication configuration rule applied to "+l.URL+" successfully.")
}

func mainReplicateEdit(cliCtx *cli.Context) error {
	ctx, cancelReplicateEdit := context.WithCancel(globalContext)
	defer cancelReplicateEdit()

	console.SetColor("replicateEditMessage", color.New(color.FgGreen))

	checkReplicateEditSyntax(cliCtx)

	// Get the alias parameter from cli
	args := cliCtx.Args()
	aliasedURL := args.Get(0)
	// Create a new Client
	client, err := newClient(aliasedURL)
	fatalIf(err, "Unable to initialize connection.")
	rcfg, err := client.GetReplication(ctx)
	fatalIf(err.Trace(args...), "Unable to get replication configuration")

	if !cliCtx.IsSet("id") {
		fatalIf(errInvalidArgument(), "--id is a required flag")
	}
	var state string
	if cliCtx.IsSet("state") {
		state = strings.ToLower(cliCtx.String("state"))
		if state != "enable" && state != "disable" {
			fatalIf(err.Trace(args...), "--state can be either `enable` or `disable`")
		}
	}
	var vDeleteReplicate, dmReplicate, replicasync, existingReplState string
	if cliCtx.IsSet("replicate") {
		replSlice := strings.Split(cliCtx.String("replicate"), ",")
		vDeleteReplicate = disableStatus
		dmReplicate = disableStatus
		replicasync = disableStatus
		existingReplState = disableStatus

		for _, opt := range replSlice {
			switch strings.TrimSpace(strings.ToLower(opt)) {
			case "delete-marker":
				dmReplicate = enableStatus
			case "delete":
				vDeleteReplicate = enableStatus
			case "metadata-sync", "replica-metadata-sync":
				replicasync = enableStatus
			case "existing-objects":
				existingReplState = enableStatus
			default:
				if opt != "" {
					fatalIf(probe.NewError(fmt.Errorf("invalid value for --replicate flag %s", cliCtx.String("replicate"))), `--replicate flag takes one or more comma separated string with values "delete", "delete-marker", "metadata-sync", "existing-objects" or "" to disable these settings`)
				}
			}
		}
	}

	opts := replication.Options{
		TagString:    cliCtx.String("tags"),
		RoleArn:      cliCtx.String("arn"),
		StorageClass: cliCtx.String("storage-class"),
		RuleStatus:   state,
		ID:           cliCtx.String("id"),
		Op:           replication.SetOption,
		DestBucket:   cliCtx.String("remote-bucket"),
		IsSCSet:      cliCtx.IsSet("storage-class"),
		IsTagSet:     cliCtx.IsSet("tags"),
	}

	if cliCtx.IsSet("priority") {
		opts.Priority = strconv.Itoa(cliCtx.Int("priority"))
	}
	if cliCtx.IsSet("replicate") {
		opts.ReplicateDeletes = vDeleteReplicate
		opts.ReplicateDeleteMarkers = dmReplicate
		opts.ReplicaSync = replicasync
		opts.ExistingObjectReplicate = existingReplState
	}

	fatalIf(client.SetReplication(ctx, &rcfg, opts), "Could not modify replication rule")
	printMsg(replicateEditMessage{
		Op:  "set",
		URL: aliasedURL,
		ID:  opts.ID,
	})
	return nil
}
<|endoftext|>


<commit_before>package halgo

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

func Navigator(uri string) navigator {
	return navigator{
		rootUri:         uri,
		linksToNavigate: []link{},
		HttpClient:      http.DefaultClient,
	}
}

type link struct {
	rel    string
	params Params
}

type navigator struct {
	HttpClient      HttpClient
	linksToNavigate []link
	rootUri         string
}

func (n navigator) Link(rel string) navigator {
	return n.LinkExpand(rel, nil)
}

func (n navigator) LinkExpand(rel string, params Params) navigator {
	links := make([]link, 0, len(n.linksToNavigate)+1)
	copy(n.linksToNavigate, links)
	links = append(links, link{rel: rel})

	return navigator{
		HttpClient:      n.HttpClient,
		linksToNavigate: links,
		rootUri:         n.rootUri,
	}
}

func (n navigator) url() (string, error) {
	url := n.rootUri

	for _, link := range n.linksToNavigate {
		links, err := n.getLinks(url)
		if err != nil {
			return "", err
		}

		if _, ok := links.Items[link.rel]; !ok {
			return "", LinkNotFoundError{link.rel}
		}

		url, err = links.HrefParams(link.rel, link.params)
		if err != nil {
			return "", err
		}
	}

	return url, nil
}

func (n navigator) Get() (*http.Response, error) {
	url, err := n.url()
	if err != nil {
		return nil, err
	}

	return n.HttpClient.Get(url)
}

func (n navigator) PostForm(data url.Values) (*http.Response, error) {
	url, err := n.url()
	if err != nil {
		return nil, err
	}

	return n.HttpClient.PostForm(url, data)
}

func (n navigator) Patch(bodyType string, body io.Reader) (*http.Response, error) {
	url, err := n.url()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("PATCH", url, body)
	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-Type", bodyType)

	return n.HttpClient.Do(req)
}

func (n navigator) Post(bodyType string, body io.Reader) (*http.Response, error) {
	url, err := n.url()
	if err != nil {
		return nil, err
	}

	return n.HttpClient.Post(url, bodyType, body)
}

func (n navigator) Unmarshal(v interface{}) error {
	res, err := n.Get()
	if err != nil {
		return err
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}

	return json.Unmarshal(body, &v)
}

func (n navigator) getLinks(uri string) (Links, error) {
	res, err := n.HttpClient.Get(uri)
	if err != nil {
		return Links{}, err
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return Links{}, err
	}

	var m Links

	if err := json.Unmarshal(body, &m); err != nil {
		return Links{}, err
	}

	return m, nil
}
<commit_msg>Relations and path, dont overload the link term<commit_after>package halgo

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

func Navigator(uri string) navigator {
	return navigator{
		rootUri:    uri,
		path:       []relation{},
		HttpClient: http.DefaultClient,
	}
}

type relation struct {
	rel    string
	params Params
}

type navigator struct {
	HttpClient HttpClient
	path       []relation
	rootUri    string
}

func (n navigator) Link(rel string) navigator {
	return n.LinkExpand(rel, nil)
}

func (n navigator) LinkExpand(rel string, params Params) navigator {
	relations := make([]relation, 0, len(n.path)+1)
	copy(n.path, relations)
	relations = append(relations, relation{rel: rel})

	return navigator{
		HttpClient: n.HttpClient,
		path:       relations,
		rootUri:    n.rootUri,
	}
}

func (n navigator) url() (string, error) {
	url := n.rootUri

	for _, link := range n.path {
		links, err := n.getLinks(url)
		if err != nil {
			return "", err
		}

		if _, ok := links.Items[link.rel]; !ok {
			return "", LinkNotFoundError{link.rel}
		}

		url, err = links.HrefParams(link.rel, link.params)
		if err != nil {
			return "", err
		}
	}

	return url, nil
}

func (n navigator) Get() (*http.Response, error) {
	url, err := n.url()
	if err != nil {
		return nil, err
	}

	return n.HttpClient.Get(url)
}

func (n navigator) PostForm(data url.Values) (*http.Response, error) {
	url, err := n.url()
	if err != nil {
		return nil, err
	}

	return n.HttpClient.PostForm(url, data)
}

func (n navigator) Patch(bodyType string, body io.Reader) (*http.Response, error) {
	url, err := n.url()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("PATCH", url, body)
	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-Type", bodyType)

	return n.HttpClient.Do(req)
}

func (n navigator) Post(bodyType string, body io.Reader) (*http.Response, error) {
	url, err := n.url()
	if err != nil {
		return nil, err
	}

	return n.HttpClient.Post(url, bodyType, body)
}

func (n navigator) Unmarshal(v interface{}) error {
	res, err := n.Get()
	if err != nil {
		return err
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}

	return json.Unmarshal(body, &v)
}

func (n navigator) getLinks(uri string) (Links, error) {
	res, err := n.HttpClient.Get(uri)
	if err != nil {
		return Links{}, err
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return Links{}, err
	}

	var m Links

	if err := json.Unmarshal(body, &m); err != nil {
		return Links{}, err
	}

	return m, nil
}
<|endoftext|>


<commit_before>package runner

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"path/filepath"
	"strings"
	"syscall"
)

func cleanSharedLibs(lgopath string, sessID *SessionID) error {
	pkg := path.Join(lgopath, "pkg")
	files, err := ioutil.ReadDir(pkg)
	if err != nil {
		return err
	}
	prefix := "libgithub.com-yunabe-lgo-" + sessID.Marshal()
	for _, f := range files {
		if strings.HasPrefix(f.Name(), prefix) && strings.HasSuffix(f.Name(), ".so") {
			if rerr := os.Remove(path.Join(pkg, f.Name())); rerr != nil && err == nil {
				err = rerr
			}
		}
	}
	if rerr := os.RemoveAll(path.Join(pkg, "github.com/yunabe/lgo", sessID.Marshal())); rerr != nil {
		err = rerr
	}
	return err
}

// CleanSession cleans up files for a session specified by sessNum.
// CleanSession returns nil when no target file exists.
func CleanSession(gopath, lgopath string, sessID *SessionID) error {
	srcErr := os.RemoveAll(path.Join(gopath, "src/github.com/yunabe/lgo", sessID.Marshal()))
	soErr := cleanSharedLibs(lgopath, sessID)
	if srcErr != nil {
		return srcErr
	}
	return soErr
}

// MainWithCleanup is a main routine called from wrapper binaries (lgo, lgo-kernel).
// If killchild is passed, the child process is killed when killchild is closed.
func MainWithCleanup(lgobin string, killchild <-chan struct{}) {
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		log.Fatal("GOPATH is not set")
	}
	lgopath := os.Getenv("LGOPATH")
	if lgopath == "" {
		log.Fatal("LGOPATH is empty")
	}
	lgopath, err := filepath.Abs(lgopath)
	if err != nil {
		log.Fatalf("Failed to get the absolute path of LGOPATH: %v", err)
	}
	root := os.Getenv("LGOPATH")
	if root == "" {
		log.Fatalf("LGOPATH environ variable is not set")
		return
	}
	// SIGINT: Ctrl-C, SIGTERM: kill, SIGQUIT: Ctrl-\, SIGTSTP: Ctrl-z
	signal.Notify(make(chan os.Signal), syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGTSTP, syscall.SIGHUP)

	sessID := NewSessionID()
	binary := path.Join(lgopath, "bin", lgobin)
	args := []string{fmt.Sprintf("--sess_id=%s", sessID.Marshal())}
	args = append(args, os.Args[1:]...)
	cmd := exec.Command(binary, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin

	failed := false
	if err := cmd.Start(); err != nil {
		log.Printf("Failed to start %s with %v: %v", binary, args, err)
		failed = true
	} else {
		if killchild != nil {
			// This goroutine leaks! It's okay because this function must be called only once.
			go func() {
				<-killchild
				cmd.Process.Kill()
			}()
		}
		if err := cmd.Wait(); err != nil {
			log.Printf("%s failed: %v", binary, err)
			failed = true
		}
	}
	if err := CleanSession(gopath, lgopath, sessID); err != nil {
		log.Fatalf("Clean-up failure: %v", err)
	}
	if failed {
		os.Exit(1)
	}
}
<commit_msg>Delete an unused func: "cmd/runner".MainWithCleanup<commit_after>package runner

import (
	"io/ioutil"
	"os"
	"path"
	"strings"
)

func cleanSharedLibs(lgopath string, sessID *SessionID) error {
	pkg := path.Join(lgopath, "pkg")
	files, err := ioutil.ReadDir(pkg)
	if err != nil {
		return err
	}
	prefix := "libgithub.com-yunabe-lgo-" + sessID.Marshal()
	for _, f := range files {
		if strings.HasPrefix(f.Name(), prefix) && strings.HasSuffix(f.Name(), ".so") {
			if rerr := os.Remove(path.Join(pkg, f.Name())); rerr != nil && err == nil {
				err = rerr
			}
		}
	}
	if rerr := os.RemoveAll(path.Join(pkg, "github.com/yunabe/lgo", sessID.Marshal())); rerr != nil {
		err = rerr
	}
	return err
}

// CleanSession cleans up files for a session specified by sessNum.
// CleanSession returns nil when no target file exists.
func CleanSession(gopath, lgopath string, sessID *SessionID) error {
	srcErr := os.RemoveAll(path.Join(gopath, "src/github.com/yunabe/lgo", sessID.Marshal()))
	soErr := cleanSharedLibs(lgopath, sessID)
	if srcErr != nil {
		return srcErr
	}
	return soErr
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"path/filepath"

	"github.com/carlmjohnson/scattered"
)

func link(paths map[string]string) (err error) {
	for src, dst := range paths {
		_, err := os.Stat(dst)
		if !os.IsNotExist(err) {
			return err
		} else if err == nil {
			if err = os.Remove(dst); err != nil {
				return err
			}
		}

		if err = os.Link(src, dst); err != nil {
			return err
		}
	}

	return nil
}

func getPaths(globs []string) (paths []string, err error) {
	var fileset = map[string]bool{}

	for _, glob := range globs {
		globpaths, err := filepath.Glob(glob)
		if err != nil {
			return nil, err
		}

		for _, path := range globpaths {
			if seen := fileset[path]; seen {
				continue
			}

			fileset[path] = true
			if !scattered.IsHashedPath(path) {
				paths = append(paths, path)
			}
		}
	}

	return paths, err
}

func die(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
}

func main() {
	flag.Parse()
	paths, err := getPaths(flag.Args())
	die(err)

	var pathsMap = map[string]string{}

	for _, src := range paths {
		dst, err := scattered.HashPath(src)
		die(err)
		pathsMap[src] = dst
	}

	die(link(pathsMap))

	b, err := json.MarshalIndent(&pathsMap, "", "\t")
	die(err)

	os.Stdout.Write(b)
	// Trailing newline
	os.Stdout.WriteString("\n")
}
<commit_msg>CMD: Use one-liner main func style<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"path/filepath"

	"github.com/carlmjohnson/scattered"
)

func link(paths map[string]string) (err error) {
	for src, dst := range paths {
		_, err := os.Stat(dst)
		if !os.IsNotExist(err) {
			return err
		} else if err == nil {
			if err = os.Remove(dst); err != nil {
				return err
			}
		}

		if err = os.Link(src, dst); err != nil {
			return err
		}
	}

	return nil
}

func getPaths(globs []string) (paths []string, err error) {
	var fileset = map[string]bool{}

	for _, glob := range globs {
		globpaths, err := filepath.Glob(glob)
		if err != nil {
			return nil, err
		}

		for _, path := range globpaths {
			if seen := fileset[path]; seen {
				continue
			}

			fileset[path] = true
			if !scattered.IsHashedPath(path) {
				paths = append(paths, path)
			}
		}
	}

	return paths, err
}

func main() {
	if err := run(); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
}

func run() error {
	flag.Parse()
	paths, err := getPaths(flag.Args())
	if err != nil {
		return err
	}

	var pathsMap = map[string]string{}

	for _, src := range paths {
		dst, err := scattered.HashPath(src)
		if err != nil {
			return err
		}
		pathsMap[src] = dst
	}

	if err = link(pathsMap); err != nil {
		return err
	}

	b, err := json.MarshalIndent(&pathsMap, "", "\t")
	if err != nil {
		return err
	}

	os.Stdout.Write(b)
	// Trailing newline
	_, err = os.Stdout.WriteString("\n")
	return err
}
<|endoftext|>