<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package main

import (
	"context"
	"testing"

	"github.com/kr/pretty"
	"k8s.io/api/extensions/v1beta1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/ingress-gce/pkg/e2e"
	"k8s.io/ingress-gce/pkg/fuzz"
	"k8s.io/ingress-gce/pkg/fuzz/features"
)

func TestBasic(t *testing.T) {
	t.Parallel()

	port80 := intstr.FromInt(80)

	for _, tc := range []struct {
		desc string
		ing  *v1beta1.Ingress

		numForwardingRules int
		numBackendServices int
	}{
		{
			desc: "http default backend",
			ing: fuzz.NewIngressBuilder("", "ingress-1", "").
				DefaultBackend("service-1", port80).
				Build(),
			numForwardingRules: 1,
			numBackendServices: 1,
		},
		{
			desc: "http one path",
			ing: fuzz.NewIngressBuilder("", "ingress-1", "").
				AddPath("test.com", "/", "service-1", port80).
				Build(),
			numForwardingRules: 1,
			numBackendServices: 2,
		},
		{
			desc: "http multiple paths",
			ing: fuzz.NewIngressBuilder("", "ingress-1", "").
				AddPath("test.com", "/foo", "service-1", port80).
				AddPath("test.com", "/bar", "service-1", port80).
				Build(),
			numForwardingRules: 1,
			numBackendServices: 2,
		},
	} {
		tc := tc // Capture tc as we are running this in parallel.
		Framework.RunWithSandbox(tc.desc, t, func(t *testing.T, s *e2e.Sandbox) {
			t.Parallel()

			ctx := context.Background()

			_, _, err := e2e.CreateEchoService(s, "service-1", nil)
			if err != nil {
				t.Fatalf("error creating echo service: %v", err)
			}
			t.Logf("Echo service created (%s/%s)", s.Namespace, "service-1")

			if _, err := Framework.Clientset.Extensions().Ingresses(s.Namespace).Create(tc.ing); err != nil {
				t.Fatalf("error creating Ingress spec: %v", err)
			}
			t.Logf("Ingress created (%s/%s)", s.Namespace, tc.ing.Name)

			ing, err := e2e.WaitForIngress(s, tc.ing, nil)
			if err != nil {
				t.Fatalf("error waiting for Ingress to stabilize: %v", err)
			}
			t.Logf("GCLB resources createdd (%s/%s)", s.Namespace, tc.ing.Name)

			// Perform whitebox testing.
			if len(ing.Status.LoadBalancer.Ingress) < 1 {
				t.Fatalf("Ingress does not have an IP: %+v", ing.Status)
			}

			vip := ing.Status.LoadBalancer.Ingress[0].IP
			t.Logf("Ingress %s/%s VIP = %s", s.Namespace, tc.ing.Name, vip)
			gclb, err := fuzz.GCLBForVIP(context.Background(), Framework.Cloud, vip, fuzz.FeatureValidators(features.All))
			if err != nil {
				t.Fatalf("Error getting GCP resources for LB with IP = %q: %v", vip, err)
			}

			// Do some cursory checks on the GCP objects.
			if len(gclb.ForwardingRule) != tc.numForwardingRules {
				t.Errorf("got %d fowarding rules, want %d;\n%s", len(gclb.ForwardingRule), tc.numForwardingRules, pretty.Sprint(gclb.ForwardingRule))
			}
			if len(gclb.BackendService) != tc.numBackendServices {
				t.Errorf("got %d backend services, want %d;\n%s", len(gclb.BackendService), tc.numBackendServices, pretty.Sprint(gclb.BackendService))
			}

			deleteOptions := &fuzz.GCLBDeleteOptions{
				SkipDefaultBackend: true,
			}
			if err := e2e.WaitForIngressDeletion(ctx, gclb, s, ing, deleteOptions); err != nil {
				t.Errorf("e2e.WaitForIngressDeletion(..., %q, nil) = %v, want nil", ing.Name, err)
			}
		})
	}
}
<commit_msg>Add e2e test for Ingress that has long namespace/name<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package main

import (
	"context"
	"testing"

	"github.com/kr/pretty"
	"k8s.io/api/extensions/v1beta1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/ingress-gce/pkg/e2e"
	"k8s.io/ingress-gce/pkg/fuzz"
	"k8s.io/ingress-gce/pkg/fuzz/features"
)

func TestBasic(t *testing.T) {
	t.Parallel()

	port80 := intstr.FromInt(80)

	for _, tc := range []struct {
		desc string
		ing  *v1beta1.Ingress

		numForwardingRules int
		numBackendServices int
	}{
		{
			desc: "http default backend",
			ing: fuzz.NewIngressBuilder("", "ingress-1", "").
				DefaultBackend("service-1", port80).
				Build(),
			numForwardingRules: 1,
			numBackendServices: 1,
		},
		{
			desc: "http one path",
			ing: fuzz.NewIngressBuilder("", "ingress-1", "").
				AddPath("test.com", "/", "service-1", port80).
				Build(),
			numForwardingRules: 1,
			numBackendServices: 2,
		},
		{
			desc: "http multiple paths",
			ing: fuzz.NewIngressBuilder("", "ingress-1", "").
				AddPath("test.com", "/foo", "service-1", port80).
				AddPath("test.com", "/bar", "service-1", port80).
				Build(),
			numForwardingRules: 1,
			numBackendServices: 2,
		},
	} {
		tc := tc // Capture tc as we are running this in parallel.
		Framework.RunWithSandbox(tc.desc, t, func(t *testing.T, s *e2e.Sandbox) {
			t.Parallel()

			ctx := context.Background()

			_, _, err := e2e.CreateEchoService(s, "service-1", nil)
			if err != nil {
				t.Fatalf("error creating echo service: %v", err)
			}
			t.Logf("Echo service created (%s/%s)", s.Namespace, "service-1")

			if _, err := Framework.Clientset.Extensions().Ingresses(s.Namespace).Create(tc.ing); err != nil {
				t.Fatalf("error creating Ingress spec: %v", err)
			}
			t.Logf("Ingress created (%s/%s)", s.Namespace, tc.ing.Name)

			ing, err := e2e.WaitForIngress(s, tc.ing, nil)
			if err != nil {
				t.Fatalf("error waiting for Ingress to stabilize: %v", err)
			}
			t.Logf("GCLB resources createdd (%s/%s)", s.Namespace, tc.ing.Name)

			// Perform whitebox testing.
			if len(ing.Status.LoadBalancer.Ingress) < 1 {
				t.Fatalf("Ingress does not have an IP: %+v", ing.Status)
			}

			vip := ing.Status.LoadBalancer.Ingress[0].IP
			t.Logf("Ingress %s/%s VIP = %s", s.Namespace, tc.ing.Name, vip)
			gclb, err := fuzz.GCLBForVIP(context.Background(), Framework.Cloud, vip, fuzz.FeatureValidators(features.All))
			if err != nil {
				t.Fatalf("Error getting GCP resources for LB with IP = %q: %v", vip, err)
			}

			// Do some cursory checks on the GCP objects.
			if len(gclb.ForwardingRule) != tc.numForwardingRules {
				t.Errorf("got %d fowarding rules, want %d;\n%s", len(gclb.ForwardingRule), tc.numForwardingRules, pretty.Sprint(gclb.ForwardingRule))
			}
			if len(gclb.BackendService) != tc.numBackendServices {
				t.Errorf("got %d backend services, want %d;\n%s", len(gclb.BackendService), tc.numBackendServices, pretty.Sprint(gclb.BackendService))
			}

			deleteOptions := &fuzz.GCLBDeleteOptions{
				SkipDefaultBackend: true,
			}
			if err := e2e.WaitForIngressDeletion(ctx, gclb, s, ing, deleteOptions); err != nil {
				t.Errorf("e2e.WaitForIngressDeletion(..., %q, nil) = %v, want nil", ing.Name, err)
			}
		})
	}
}

// TestBasicEdge exercises some basic edge cases that previously have caused bugs.
func TestBasicEdge(t *testing.T) {
	t.Parallel()

	port80 := intstr.FromInt(80)

	for _, tc := range []struct {
		desc string
		ing  *v1beta1.Ingress

		numForwardingRules int
		numBackendServices int
	}{
		{
			desc: "long ingress name",
			ing: fuzz.NewIngressBuilder("", "long-ingress-name", "").
				DefaultBackend("service-1", port80).
				Build(),
			numForwardingRules: 1,
			numBackendServices: 1,
		},
	} {
		tc := tc // Capture tc as we are running this in parallel.
		Framework.RunWithSandbox(tc.desc, t, func(t *testing.T, s *e2e.Sandbox) {
			t.Parallel()

			ctx := context.Background()

			_, _, err := e2e.CreateEchoService(s, "service-1", nil)
			if err != nil {
				t.Fatalf("error creating echo service: %v", err)
			}
			t.Logf("Echo service created (%s/%s)", s.Namespace, "service-1")

			if _, err := Framework.Clientset.Extensions().Ingresses(s.Namespace).Create(tc.ing); err != nil {
				t.Fatalf("error creating Ingress spec: %v", err)
			}
			t.Logf("Ingress created (%s/%s)", s.Namespace, tc.ing.Name)

			ing, err := e2e.WaitForIngress(s, tc.ing, nil)
			if err != nil {
				t.Fatalf("error waiting for Ingress to stabilize: %v", err)
			}
			t.Logf("GCLB resources createdd (%s/%s)", s.Namespace, tc.ing.Name)

			// Perform whitebox testing.
			if len(ing.Status.LoadBalancer.Ingress) < 1 {
				t.Fatalf("Ingress does not have an IP: %+v", ing.Status)
			}

			vip := ing.Status.LoadBalancer.Ingress[0].IP
			t.Logf("Ingress %s/%s VIP = %s", s.Namespace, tc.ing.Name, vip)
			gclb, err := fuzz.GCLBForVIP(context.Background(), Framework.Cloud, vip, fuzz.FeatureValidators(features.All))
			if err != nil {
				t.Fatalf("Error getting GCP resources for LB with IP = %q: %v", vip, err)
			}

			// Do some cursory checks on the GCP objects.
			if len(gclb.ForwardingRule) != tc.numForwardingRules {
				t.Errorf("got %d fowarding rules, want %d;\n%s", len(gclb.ForwardingRule), tc.numForwardingRules, pretty.Sprint(gclb.ForwardingRule))
			}
			if len(gclb.BackendService) != tc.numBackendServices {
				t.Errorf("got %d backend services, want %d;\n%s", len(gclb.BackendService), tc.numBackendServices, pretty.Sprint(gclb.BackendService))
			}

			deleteOptions := &fuzz.GCLBDeleteOptions{
				SkipDefaultBackend: true,
			}
			if err := e2e.WaitForIngressDeletion(ctx, gclb, s, ing, deleteOptions); err != nil {
				t.Errorf("e2e.WaitForIngressDeletion(..., %q, nil) = %v, want nil", ing.Name, err)
			}
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"io"
	"time"

	"github.com/spf13/cobra"

	"github.com/weaveworks/flux"
	"github.com/weaveworks/flux/api/v11"
	"github.com/weaveworks/flux/api/v6"
	"github.com/weaveworks/flux/cluster"
	"github.com/weaveworks/flux/job"
	"github.com/weaveworks/flux/update"
)

type workloadReleaseOpts struct {
	*rootOpts
	namespace    string
	workloads    []string
	allWorkloads bool
	image        string
	allImages    bool
	exclude      []string
	dryRun       bool
	interactive  bool
	force        bool
	watch        bool
	outputOpts
	cause update.Cause

	// Deprecated
	controllers []string
}

func newWorkloadRelease(parent *rootOpts) *workloadReleaseOpts {
	return &workloadReleaseOpts{rootOpts: parent}
}

func (opts *workloadReleaseOpts) Command() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "release",
		Short: "Release a new version of a controller.",
		Example: makeExample(
			"fluxctl release -n default --workload=deployment/foo --update-image=library/hello:v2",
			"fluxctl release --all --update-image=library/hello:v2",
			"fluxctl release --workload=default:deployment/foo --update-all-images",
		),
		RunE: opts.RunE,
	}

	AddOutputFlags(cmd, &opts.outputOpts)
	AddCauseFlags(cmd, &opts.cause)
	cmd.Flags().StringVarP(&opts.namespace, "namespace", "n", "default", "Workload namespace")
	// Note: we cannot define a shorthand for --workload since it clashes with the shorthand of --watch
	cmd.Flags().StringSliceVarP(&opts.workloads, "workload", "", []string{}, "List of workloads to release <namespace>:<kind>/<name>")
	cmd.Flags().BoolVar(&opts.allWorkloads, "all", false, "Release all controllers")
	cmd.Flags().StringVarP(&opts.image, "update-image", "i", "", "Update a specific image")
	cmd.Flags().BoolVar(&opts.allImages, "update-all-images", false, "Update all images to latest versions")
	cmd.Flags().StringSliceVar(&opts.exclude, "exclude", []string{}, "List of controllers to exclude")
	cmd.Flags().BoolVar(&opts.dryRun, "dry-run", false, "Do not release anything; just report back what would have been done")
	cmd.Flags().BoolVar(&opts.interactive, "interactive", false, "Select interactively which containers to update")
	cmd.Flags().BoolVarP(&opts.force, "force", "f", false, "Disregard locks and container image filters (has no effect when used with --all or --update-all-images)")
	cmd.Flags().BoolVarP(&opts.watch, "watch", "w", false, "Watch rollout progress during release")

	// Deprecated
	cmd.Flags().StringSliceVarP(&opts.controllers, "controller", "c", []string{}, "List of controllers to release <namespace>:<kind>/<name>")
	cmd.Flags().MarkDeprecated("controller", "changed to --workload, use that instead")

	return cmd
}

func (opts *workloadReleaseOpts) RunE(cmd *cobra.Command, args []string) error {
	if len(args) != 0 {
		return errorWantedNoArgs
	}

	if err := checkExactlyOne("--update-image=<image> or --update-all-images", opts.image != "", opts.allImages); err != nil {
		return err
	}

	// Backwards compatibility with --controller until we remove it
	opts.workloads = append(opts.workloads, opts.controllers...)

	switch {
	case len(opts.workloads) <= 0 && !opts.allWorkloads:
		return newUsageError("please supply either --all, or at least one --workload=<workload>")
	case opts.watch && opts.dryRun:
		return newUsageError("cannot use --watch with --dry-run")
	case opts.force && opts.allWorkloads && opts.allImages:
		return newUsageError("--force has no effect when used with --all and --update-all-images")
	case opts.force && opts.allWorkloads:
		fmt.Fprintf(cmd.OutOrStderr(), "Warning: --force will not ignore locked workloads when used with --all\n")
	case opts.force && opts.allImages:
		fmt.Fprintf(cmd.OutOrStderr(), "Warning: --force will not ignore container image tags when used with --update-all-images\n")
	}

	var workloads []update.ResourceSpec

	if opts.allWorkloads {
		workloads = []update.ResourceSpec{update.ResourceSpecAll}
	} else {
		for _, workload := range opts.workloads {
			id, err := flux.ParseResourceIDOptionalNamespace(opts.namespace, workload)
			if err != nil {
				return err
			}
			workloads = append(workloads, update.MakeResourceSpec(id))
		}
	}

	var (
		image update.ImageSpec
		err   error
	)
	switch {
	case opts.image != "":
		image, err = update.ParseImageSpec(opts.image)
		if err != nil {
			return err
		}
	case opts.allImages:
		image = update.ImageSpecLatest
	}

	var kind update.ReleaseKind = update.ReleaseKindExecute
	if opts.dryRun || opts.interactive {
		kind = update.ReleaseKindPlan
	}

	var excludes []flux.ResourceID
	for _, exclude := range opts.exclude {
		s, err := flux.ParseResourceIDOptionalNamespace(opts.namespace, exclude)
		if err != nil {
			return err
		}
		excludes = append(excludes, s)
	}

	if kind == update.ReleaseKindPlan {
		fmt.Fprintf(cmd.OutOrStderr(), "Submitting dry-run release ...\n")
	} else {
		fmt.Fprintf(cmd.OutOrStderr(), "Submitting release ...\n")
	}

	ctx := context.Background()
	spec := update.ReleaseImageSpec{
		ServiceSpecs: workloads,
		ImageSpec:    image,
		Kind:         kind,
		Excludes:     excludes,
		Force:        opts.force,
	}
	jobID, err := opts.API.UpdateManifests(ctx, update.Spec{
		Type:  update.Images,
		Cause: opts.cause,
		Spec:  spec,
	})
	if err != nil {
		return err
	}

	result, err := awaitJob(ctx, opts.API, jobID)
	if err != nil {
		return err
	}
	if opts.interactive {
		spec, err := promptSpec(cmd.OutOrStdout(), result, opts.verbosity)
		spec.Force = opts.force
		if err != nil {
			fmt.Fprintln(cmd.OutOrStderr(), err.Error())
			return nil
		}

		fmt.Fprintf(cmd.OutOrStderr(), "Submitting selected release ...\n")
		jobID, err = opts.API.UpdateManifests(ctx, update.Spec{
			Type:  update.Containers,
			Cause: opts.cause,
			Spec:  spec,
		})
		if err != nil {
			fmt.Fprintln(cmd.OutOrStderr(), err.Error())
			return nil
		}

		opts.dryRun = false
	}

	err = await(ctx, cmd.OutOrStdout(), cmd.OutOrStderr(), opts.API, jobID, !opts.dryRun, opts.verbosity)
	if !opts.watch || err != nil {
		return err
	}

	fmt.Fprintf(cmd.OutOrStderr(), "Monitoring rollout ...\n")
	for {
		completed := 0
		workloads, err := opts.API.ListServicesWithOptions(ctx, v11.ListServicesOptions{Services: result.Result.AffectedResources()})
		if err != nil {
			return err
		}

		for _, workload := range workloads {
			writeRolloutStatus(workload, opts.verbosity)

			if workload.Status == cluster.StatusReady {
				completed++
			}

			if workload.Rollout.Messages != nil {
				fmt.Fprintf(cmd.OutOrStderr(), "There was a problem releasing %s:\n", workload.ID)
				for _, msg := range workload.Rollout.Messages {
					fmt.Fprintf(cmd.OutOrStderr(), "%s\n", msg)
				}
				return nil
			}
		}

		if completed == len(workloads) {
			fmt.Fprintf(cmd.OutOrStderr(), "All workloads ready.\n")
			return nil
		}

		time.Sleep(2000 * time.Millisecond)
	}
}

func writeRolloutStatus(workload v6.ControllerStatus, verbosity int) {
	w := newTabwriter()
	fmt.Fprintf(w, "WORKLOAD\tCONTAINER\tIMAGE\tRELEASE\tREPLICAS\n")

	if len(workload.Containers) > 0 {
		c := workload.Containers[0]
		fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d/%d", workload.ID, c.Name, c.Current.ID, workload.Status, workload.Rollout.Updated, workload.Rollout.Desired)
		if verbosity > 0 {
			fmt.Fprintf(w, " (%d outdated, %d ready)", workload.Rollout.Outdated, workload.Rollout.Ready)
		}
		fmt.Fprintf(w, "\n")
		for _, c := range workload.Containers[1:] {
			fmt.Fprintf(w, "\t%s\t%s\t\t\n", c.Name, c.Current.ID)
		}
	} else {
		fmt.Fprintf(w, "%s\t\t\t%s\t%d/%d", workload.ID, workload.Status, workload.Rollout.Updated, workload.Rollout.Desired)
		if verbosity > 0 {
			fmt.Fprintf(w, " (%d outdated, %d ready)", workload.Rollout.Outdated, workload.Rollout.Ready)
		}
		fmt.Fprintf(w, "\n")
	}
	fmt.Fprintln(w)
	w.Flush()
}

func promptSpec(out io.Writer, result job.Result, verbosity int) (update.ReleaseContainersSpec, error) {
	menu := update.NewMenu(out, result.Result, verbosity)
	containerSpecs, err := menu.Run()
	return update.ReleaseContainersSpec{
		Kind:           update.ReleaseKindExecute,
		ContainerSpecs: containerSpecs,
		SkipMismatches: false,
	}, err
}
<commit_msg>Rename controller to workload in `fluxctl release`<commit_after>package main

import (
	"context"
	"fmt"
	"io"
	"time"

	"github.com/spf13/cobra"

	"github.com/weaveworks/flux"
	"github.com/weaveworks/flux/api/v11"
	"github.com/weaveworks/flux/api/v6"
	"github.com/weaveworks/flux/cluster"
	"github.com/weaveworks/flux/job"
	"github.com/weaveworks/flux/update"
)

type workloadReleaseOpts struct {
	*rootOpts
	namespace    string
	workloads    []string
	allWorkloads bool
	image        string
	allImages    bool
	exclude      []string
	dryRun       bool
	interactive  bool
	force        bool
	watch        bool
	outputOpts
	cause update.Cause

	// Deprecated
	controllers []string
}

func newWorkloadRelease(parent *rootOpts) *workloadReleaseOpts {
	return &workloadReleaseOpts{rootOpts: parent}
}

func (opts *workloadReleaseOpts) Command() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "release",
		Short: "Release a new version of a workload.",
		Example: makeExample(
			"fluxctl release -n default --workload=deployment/foo --update-image=library/hello:v2",
			"fluxctl release --all --update-image=library/hello:v2",
			"fluxctl release --workload=default:deployment/foo --update-all-images",
		),
		RunE: opts.RunE,
	}

	AddOutputFlags(cmd, &opts.outputOpts)
	AddCauseFlags(cmd, &opts.cause)
	cmd.Flags().StringVarP(&opts.namespace, "namespace", "n", "default", "Workload namespace")
	// Note: we cannot define a shorthand for --workload since it clashes with the shorthand of --watch
	cmd.Flags().StringSliceVarP(&opts.workloads, "workload", "", []string{}, "List of workloads to release <namespace>:<kind>/<name>")
	cmd.Flags().BoolVar(&opts.allWorkloads, "all", false, "Release all workloads")
	cmd.Flags().StringVarP(&opts.image, "update-image", "i", "", "Update a specific image")
	cmd.Flags().BoolVar(&opts.allImages, "update-all-images", false, "Update all images to latest versions")
	cmd.Flags().StringSliceVar(&opts.exclude, "exclude", []string{}, "List of workloads to exclude")
	cmd.Flags().BoolVar(&opts.dryRun, "dry-run", false, "Do not release anything; just report back what would have been done")
	cmd.Flags().BoolVar(&opts.interactive, "interactive", false, "Select interactively which containers to update")
	cmd.Flags().BoolVarP(&opts.force, "force", "f", false, "Disregard locks and container image filters (has no effect when used with --all or --update-all-images)")
	cmd.Flags().BoolVarP(&opts.watch, "watch", "w", false, "Watch rollout progress during release")

	// Deprecated
	cmd.Flags().StringSliceVarP(&opts.controllers, "controller", "c", []string{}, "List of controllers to release <namespace>:<kind>/<name>")
	cmd.Flags().MarkDeprecated("controller", "changed to --workload, use that instead")

	return cmd
}

func (opts *workloadReleaseOpts) RunE(cmd *cobra.Command, args []string) error {
	if len(args) != 0 {
		return errorWantedNoArgs
	}

	if err := checkExactlyOne("--update-image=<image> or --update-all-images", opts.image != "", opts.allImages); err != nil {
		return err
	}

	// Backwards compatibility with --controller until we remove it
	opts.workloads = append(opts.workloads, opts.controllers...)

	switch {
	case len(opts.workloads) <= 0 && !opts.allWorkloads:
		return newUsageError("please supply either --all, or at least one --workload=<workload>")
	case opts.watch && opts.dryRun:
		return newUsageError("cannot use --watch with --dry-run")
	case opts.force && opts.allWorkloads && opts.allImages:
		return newUsageError("--force has no effect when used with --all and --update-all-images")
	case opts.force && opts.allWorkloads:
		fmt.Fprintf(cmd.OutOrStderr(), "Warning: --force will not ignore locked workloads when used with --all\n")
	case opts.force && opts.allImages:
		fmt.Fprintf(cmd.OutOrStderr(), "Warning: --force will not ignore container image tags when used with --update-all-images\n")
	}

	var workloads []update.ResourceSpec

	if opts.allWorkloads {
		workloads = []update.ResourceSpec{update.ResourceSpecAll}
	} else {
		for _, workload := range opts.workloads {
			id, err := flux.ParseResourceIDOptionalNamespace(opts.namespace, workload)
			if err != nil {
				return err
			}
			workloads = append(workloads, update.MakeResourceSpec(id))
		}
	}

	var (
		image update.ImageSpec
		err   error
	)
	switch {
	case opts.image != "":
		image, err = update.ParseImageSpec(opts.image)
		if err != nil {
			return err
		}
	case opts.allImages:
		image = update.ImageSpecLatest
	}

	var kind update.ReleaseKind = update.ReleaseKindExecute
	if opts.dryRun || opts.interactive {
		kind = update.ReleaseKindPlan
	}

	var excludes []flux.ResourceID
	for _, exclude := range opts.exclude {
		s, err := flux.ParseResourceIDOptionalNamespace(opts.namespace, exclude)
		if err != nil {
			return err
		}
		excludes = append(excludes, s)
	}

	if kind == update.ReleaseKindPlan {
		fmt.Fprintf(cmd.OutOrStderr(), "Submitting dry-run release ...\n")
	} else {
		fmt.Fprintf(cmd.OutOrStderr(), "Submitting release ...\n")
	}

	ctx := context.Background()
	spec := update.ReleaseImageSpec{
		ServiceSpecs: workloads,
		ImageSpec:    image,
		Kind:         kind,
		Excludes:     excludes,
		Force:        opts.force,
	}
	jobID, err := opts.API.UpdateManifests(ctx, update.Spec{
		Type:  update.Images,
		Cause: opts.cause,
		Spec:  spec,
	})
	if err != nil {
		return err
	}

	result, err := awaitJob(ctx, opts.API, jobID)
	if err != nil {
		return err
	}
	if opts.interactive {
		spec, err := promptSpec(cmd.OutOrStdout(), result, opts.verbosity)
		spec.Force = opts.force
		if err != nil {
			fmt.Fprintln(cmd.OutOrStderr(), err.Error())
			return nil
		}

		fmt.Fprintf(cmd.OutOrStderr(), "Submitting selected release ...\n")
		jobID, err = opts.API.UpdateManifests(ctx, update.Spec{
			Type:  update.Containers,
			Cause: opts.cause,
			Spec:  spec,
		})
		if err != nil {
			fmt.Fprintln(cmd.OutOrStderr(), err.Error())
			return nil
		}

		opts.dryRun = false
	}

	err = await(ctx, cmd.OutOrStdout(), cmd.OutOrStderr(), opts.API, jobID, !opts.dryRun, opts.verbosity)
	if !opts.watch || err != nil {
		return err
	}

	fmt.Fprintf(cmd.OutOrStderr(), "Monitoring rollout ...\n")
	for {
		completed := 0
		workloads, err := opts.API.ListServicesWithOptions(ctx, v11.ListServicesOptions{Services: result.Result.AffectedResources()})
		if err != nil {
			return err
		}

		for _, workload := range workloads {
			writeRolloutStatus(workload, opts.verbosity)

			if workload.Status == cluster.StatusReady {
				completed++
			}

			if workload.Rollout.Messages != nil {
				fmt.Fprintf(cmd.OutOrStderr(), "There was a problem releasing %s:\n", workload.ID)
				for _, msg := range workload.Rollout.Messages {
					fmt.Fprintf(cmd.OutOrStderr(), "%s\n", msg)
				}
				return nil
			}
		}

		if completed == len(workloads) {
			fmt.Fprintf(cmd.OutOrStderr(), "All workloads ready.\n")
			return nil
		}

		time.Sleep(2000 * time.Millisecond)
	}
}

func writeRolloutStatus(workload v6.ControllerStatus, verbosity int) {
	w := newTabwriter()
	fmt.Fprintf(w, "WORKLOAD\tCONTAINER\tIMAGE\tRELEASE\tREPLICAS\n")

	if len(workload.Containers) > 0 {
		c := workload.Containers[0]
		fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d/%d", workload.ID, c.Name, c.Current.ID, workload.Status, workload.Rollout.Updated, workload.Rollout.Desired)
		if verbosity > 0 {
			fmt.Fprintf(w, " (%d outdated, %d ready)", workload.Rollout.Outdated, workload.Rollout.Ready)
		}
		fmt.Fprintf(w, "\n")
		for _, c := range workload.Containers[1:] {
			fmt.Fprintf(w, "\t%s\t%s\t\t\n", c.Name, c.Current.ID)
		}
	} else {
		fmt.Fprintf(w, "%s\t\t\t%s\t%d/%d", workload.ID, workload.Status, workload.Rollout.Updated, workload.Rollout.Desired)
		if verbosity > 0 {
			fmt.Fprintf(w, " (%d outdated, %d ready)", workload.Rollout.Outdated, workload.Rollout.Ready)
		}
		fmt.Fprintf(w, "\n")
	}
	fmt.Fprintln(w)
	w.Flush()
}

func promptSpec(out io.Writer, result job.Result, verbosity int) (update.ReleaseContainersSpec, error) {
	menu := update.NewMenu(out, result.Result, verbosity)
	containerSpecs, err := menu.Run()
	return update.ReleaseContainersSpec{
		Kind:           update.ReleaseKindExecute,
		ContainerSpecs: containerSpecs,
		SkipMismatches: false,
	}, err
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"

	"github.com/alexyer/ghost/server"
)

var (
	host string
	port int
)

func init() {
	flag.StringVar(&host, "host", "localhost", "host")
	flag.IntVar(&port, "port", 6869, "port")
}

func main() {
	flag.Parse()
	server.GhostRun(&server.Options{Addr: fmt.Sprintf("%s:%d", host, port)})
}
<commit_msg>Refactor ghost-server command<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"runtime/pprof"
	"syscall"

	"github.com/alexyer/ghost/server"
)

var (
	cpuprofile string
	host       string
	port       int
)

func init() {
	flag.StringVar(&cpuprofile, "cpuprofile", "", "enable cpu profiling and write profile to file")
	flag.StringVar(&host, "host", "localhost", "host")
	flag.IntVar(&port, "port", 6869, "port")
	flag.Parse()
}

func handleShutdown() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)

	go func() {
		<-c
		cleanup()
		os.Exit(0)
	}()
}

func cleanup() {
	if cpuprofile != "" {
		pprof.StopCPUProfile()
		log.Print("Stopped CPU profiling")
	}

	log.Print("Bye!")
}

func initCPUProfile() {
	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("Starting CPU profiling. Output: %s", cpuprofile)

		pprof.StartCPUProfile(f)
	}
}

func initServer() {
	server.GhostRun(&server.Options{Addr: fmt.Sprintf("%s:%d", host, port)})
}

func main() {
	initCPUProfile()
	go handleShutdown()
	initServer()
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"strconv"
	"strings"

	"github.com/Shopify/sarama"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/color"
)

type Clusters struct {
	Ui                cli.Ui
	Cmd               string
	verbose           bool
	registeredBrokers bool
}

// TODO cluster info will contain desciption,owner,etc.
func (this *Clusters) Run(args []string) (exitCode int) {
	var (
		addCluster  string
		setMode     bool
		verifyMode  bool
		clusterName string
		clusterPath string
		zone        string
		priority    int
		replicas    int
		addBroker   string
		nickname    string
		delBroker   int
	)
	cmdFlags := flag.NewFlagSet("clusters", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&zone, "z", "", "")
	cmdFlags.StringVar(&addCluster, "add", "", "")
	cmdFlags.BoolVar(&setMode, "s", false, "")
	cmdFlags.StringVar(&clusterName, "c", "", "")
	cmdFlags.StringVar(&clusterPath, "p", "", "")
	cmdFlags.BoolVar(&this.verbose, "l", false, "")
	cmdFlags.IntVar(&replicas, "replicas", -1, "")
	cmdFlags.IntVar(&priority, "priority", -1, "")
	cmdFlags.StringVar(&addBroker, "addbroker", "", "")
	cmdFlags.StringVar(&nickname, "nickname", "", "")
	cmdFlags.IntVar(&delBroker, "delbroker", -1, "")
	cmdFlags.BoolVar(&this.registeredBrokers, "r", false, "")
	cmdFlags.BoolVar(&verifyMode, "verify", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	if validateArgs(this, this.Ui).
		on("-add", "-z", "-c", "-p").
		on("-s", "-z", "-c").
		requireAdminRights("-s", "-add").
		invalid(args) {
		return 2
	}

	if addCluster != "" {
		// add cluster
		zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
		defer printSwallowedErrors(this.Ui, zkzone)

		if err := zkzone.RegisterCluster(addCluster, clusterPath); err != nil {
			this.Ui.Error(err.Error())
			return 1
		}

		this.Ui.Info(fmt.Sprintf("%s: %s created", addCluster, clusterPath))

		return
	}

	if setMode {
		// setup a cluser meta info
		zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
		zkcluster := zkzone.NewCluster(clusterName)
		switch {
		case priority != -1:
			zkcluster.SetPriority(priority)

		case replicas != -1:
			zkcluster.SetReplicas(replicas)

		case nickname != "":
			zkcluster.SetNickname(nickname)

		case addBroker != "":
			parts := strings.Split(addBroker, ":")
			if len(parts) != 3 {
				this.Ui.Output(this.Help())
				return
			}

			brokerId, err := strconv.Atoi(parts[0])
			swallow(err)
			port, err := strconv.Atoi(parts[2])
			swallow(err)
			err = zkcluster.RegisterBroker(brokerId, parts[1], port)
			swallow(err)

		case delBroker != -1:
			this.Ui.Error("not implemented yet")

		default:
			this.Ui.Error("command not recognized")
		}

		return
	}

	if verifyMode {
		if zone != "" {
			zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
			this.verifyBrokers(zkzone)

			printSwallowedErrors(this.Ui, zkzone)
		} else {
			// print all zones all clusters
			forSortedZones(func(zkzone *zk.ZkZone) {
				this.verifyBrokers(zkzone)

				printSwallowedErrors(this.Ui, zkzone)
			})
		}
		return
	}

	// display mode
	if zone != "" {
		zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
		this.printClusters(zkzone)

		printSwallowedErrors(this.Ui, zkzone)
	} else {
		// print all zones all clusters
		forSortedZones(func(zkzone *zk.ZkZone) {
			this.printClusters(zkzone)

			printSwallowedErrors(this.Ui, zkzone)
		})
	}

	return
}

func (this *Clusters) printRegisteredBrokers(zkzone *zk.ZkZone) {
	this.Ui.Output(zkzone.Name())
	zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) {
		info := zkcluster.RegisteredInfo()
		this.Ui.Output(fmt.Sprintf("    %s(%s)", info.Name(), info.Nickname))
		registeredBrokers := info.Roster
		if len(registeredBrokers) == 0 {
			this.Ui.Warn("        brokers not defined")
		} else {
			for _, b := range registeredBrokers {
				this.Ui.Output(fmt.Sprintf("        %2d %s", b.Id, b.Addr()))
			}
		}

	})
}

func (this *Clusters) verifyBrokers(zkzone *zk.ZkZone) {
	this.Ui.Output(zkzone.Name())
	zkzone.ForSortedBrokers(func(cluster string, liveBrokers map[string]*zk.BrokerZnode) {
		zkcluster := zkzone.NewCluster(cluster)
		registeredBrokers := zkcluster.RegisteredInfo().Roster

		// find diff between registeredBrokers and liveBrokers
		// loop1 find liveBrokers>registeredBrokers
		for _, broker := range liveBrokers {
			foundInRoster := false
			for _, b := range registeredBrokers {
				bid := strconv.Itoa(b.Id)
				if bid == broker.Id && broker.Addr() == b.Addr() {
					foundInRoster = true
					break
				}
			}

			if !foundInRoster {
				// should manually register the broker
				this.Ui.Output(strings.Repeat(" ", 4) +
					color.Green("+ gk clusters -z %s -s -c %s -addbroker %s:%s",
						zkzone.Name(), cluster, broker.Id, broker.Addr()))
			}
		}

		// loop2 find liveBrokers<registeredBrokers
		for _, b := range registeredBrokers {
			foundInLive := false
			for _, broker := range liveBrokers {
				bid := strconv.Itoa(b.Id)
				if bid == broker.Id && broker.Addr() == b.Addr() {
					foundInLive = true
					break
				}
			}

			if !foundInLive {
				// the broker is dead
				this.Ui.Output(strings.Repeat(" ", 4) +
					color.Red("broker %d %s is dead", b.Id, b.Addr()))
			}
		}
	})
}

func (this *Clusters) printClusters(zkzone *zk.ZkZone) {
	if this.registeredBrokers {
		this.printRegisteredBrokers(zkzone)
		return
	}

	type clusterInfo struct {
		name, path         string
		topicN, partitionN int
		err                string
		priority           int
		replicas           int
		brokerInfos        []zk.BrokerInfo
	}
	clusters := make([]clusterInfo, 0)
	zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) {
		ci := clusterInfo{
			name: zkcluster.Name(),
			path: zkcluster.Chroot(),
		}
		if !this.verbose {
			clusters = append(clusters, ci)
			return
		}

		// verbose mode, will calculate topics and partition count
		brokerList := zkcluster.BrokerList()
		if len(brokerList) == 0 {
			ci.err = "no live brokers"
			clusters = append(clusters, ci)
			return
		}

		kfk, err := sarama.NewClient(brokerList, sarama.NewConfig())
		if err != nil {
			ci.err = err.Error()
			clusters = append(clusters, ci)
			return
		}
		topics, err := kfk.Topics()
		if err != nil {
			ci.err = err.Error()
			clusters = append(clusters, ci)
			return
		}
		partitionN := 0
		for _, topic := range topics {
			partitions, err := kfk.Partitions(topic)
			if err != nil {
				ci.err = err.Error()
				clusters = append(clusters, ci)
				continue
			}

			partitionN += len(partitions)
		}

		info := zkcluster.RegisteredInfo()
		clusters = append(clusters, clusterInfo{
			name:        zkcluster.Name(),
			path:        zkcluster.Chroot(),
			topicN:      len(topics),
			partitionN:  partitionN,
			replicas:    info.Replicas,
			priority:    info.Priority,
			brokerInfos: info.Roster,
		})
	})

	this.Ui.Output(fmt.Sprintf("%s: %d", zkzone.Name(), len(clusters)))
	if this.verbose {
		// 2 loop: 1. print the err clusters 2. print the good clusters
		for _, c := range clusters {
			if c.err == "" {
				continue
			}

			this.Ui.Output(fmt.Sprintf("%30s: %s %s", c.name, c.path,
				color.Red(c.err)))
		}

		// loop2
		for _, c := range clusters {
			if c.err != "" {
				continue
			}

			this.Ui.Output(fmt.Sprintf("%30s: %s",
				c.name, c.path))
			this.Ui.Output(strings.Repeat(" ", 4) +
				color.Blue("topics:%d partitions:%d replicas:%d priority:%d brokers:%+v",
					c.topicN, c.partitionN, c.replicas, c.priority, c.brokerInfos))
		}

		return
	}

	// not verbose mode
	for _, c := range clusters {
		this.Ui.Output(fmt.Sprintf("%30s: %s", c.name, c.path))
	}

}

func (*Clusters) Synopsis() string {
	return "Register kafka clusters to a zone"
}

func (this *Clusters) Help() string {
	help := fmt.Sprintf(`
Usage: %s clusters [options]

    Register kafka clusters to a zone

Options:

    -z zone
      Only print kafka clusters within this zone.

    -c cluster name
      The new kafka cluster name.

    -l
      Use a long listing format.

    -add cluster name
      Add a new kafka cluster into a zone.

    -p cluster zk path
      The new kafka cluster chroot path in Zookeeper.
      e,g. gk clusters -z prod -add foo -p /kafka

    -s
      Setup a cluster info.

    -priority n
      Set the priority of a cluster.

    -nickname name
      Set nickname of a cluster.
      e,g. gk clusters -z prod -c foo -s -nickname bar

    -replicas n
      Set the default replicas of a cluster.

    -addbroker id:host:port
      Register a permanent broker to a cluster.

    -delbroker id TODO
      Delete a broker from a cluster.

    -r
      Display registered permanent brokers info.

    -verify
      Verify that the online brokers are consistent with the registered brokers.

`, this.Cmd)
	return strings.TrimSpace(help)
}
<commit_msg>fix arg validator bug<commit_after>package command

import (
	"flag"
	"fmt"
	"strconv"
	"strings"

	"github.com/Shopify/sarama"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/color"
)

type Clusters struct {
	Ui                cli.Ui
	Cmd               string
	verbose           bool
	registeredBrokers bool
}

// TODO cluster info will contain desciption,owner,etc.
func (this *Clusters) Run(args []string) (exitCode int) {
	var (
		addCluster  string
		setMode     bool
		verifyMode  bool
		clusterName string
		clusterPath string
		zone        string
		priority    int
		replicas    int
		addBroker   string
		nickname    string
		delBroker   int
	)
	cmdFlags := flag.NewFlagSet("clusters", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&zone, "z", "", "")
	cmdFlags.StringVar(&addCluster, "add", "", "")
	cmdFlags.BoolVar(&setMode, "s", false, "")
	cmdFlags.StringVar(&clusterName, "c", "", "")
	cmdFlags.StringVar(&clusterPath, "p", "", "")
	cmdFlags.BoolVar(&this.verbose, "l", false, "")
	cmdFlags.IntVar(&replicas, "replicas", -1, "")
	cmdFlags.IntVar(&priority, "priority", -1, "")
	cmdFlags.StringVar(&addBroker, "addbroker", "", "")
	cmdFlags.StringVar(&nickname, "nickname", "", "")
	cmdFlags.IntVar(&delBroker, "delbroker", -1, "")
	cmdFlags.BoolVar(&this.registeredBrokers, "r", false, "")
	cmdFlags.BoolVar(&verifyMode, "verify", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	if validateArgs(this, this.Ui).
		on("-add", "-z", "-p").
		on("-s", "-z", "-c").
		requireAdminRights("-s", "-add").
		invalid(args) {
		return 2
	}

	if addCluster != "" {
		// add cluster
		zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
		defer printSwallowedErrors(this.Ui, zkzone)

		if err := zkzone.RegisterCluster(addCluster, clusterPath); err != nil {
			this.Ui.Error(err.Error())
			return 1
		}

		this.Ui.Info(fmt.Sprintf("%s: %s created", addCluster, clusterPath))

		return
	}

	if setMode {
		// setup a cluser meta info
		zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
		zkcluster := zkzone.NewCluster(clusterName)
		switch {
		case priority != -1:
			zkcluster.SetPriority(priority)

		case replicas != -1:
			zkcluster.SetReplicas(replicas)

		case nickname != "":
			zkcluster.SetNickname(nickname)

		case addBroker != "":
			parts := strings.Split(addBroker, ":")
			if len(parts) != 3 {
				this.Ui.Output(this.Help())
				return
			}

			brokerId, err := strconv.Atoi(parts[0])
			swallow(err)
			port, err := strconv.Atoi(parts[2])
			swallow(err)
			err = zkcluster.RegisterBroker(brokerId, parts[1], port)
			swallow(err)

		case delBroker != -1:
			this.Ui.Error("not implemented yet")

		default:
			this.Ui.Error("command not recognized")
		}

		return
	}

	if verifyMode {
		if zone != "" {
			zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
			this.verifyBrokers(zkzone)

			printSwallowedErrors(this.Ui, zkzone)
		} else {
			// print all zones all clusters
			forSortedZones(func(zkzone *zk.ZkZone) {
				this.verifyBrokers(zkzone)

				printSwallowedErrors(this.Ui, zkzone)
			})
		}
		return
	}

	// display mode
	if zone != "" {
		zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
		this.printClusters(zkzone)

		printSwallowedErrors(this.Ui, zkzone)
	} else {
		// print all zones all clusters
		forSortedZones(func(zkzone *zk.ZkZone) {
			this.printClusters(zkzone)

			printSwallowedErrors(this.Ui, zkzone)
		})
	}

	return
}

func (this *Clusters) printRegisteredBrokers(zkzone *zk.ZkZone) {
	this.Ui.Output(zkzone.Name())
	zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) {
		info := zkcluster.RegisteredInfo()
		this.Ui.Output(fmt.Sprintf("    %s(%s)", info.Name(), info.Nickname))
		registeredBrokers := info.Roster
		if len(registeredBrokers) == 0 {
			this.Ui.Warn("        brokers not defined")
		} else {
			for _, b := range registeredBrokers {
				this.Ui.Output(fmt.Sprintf("        %2d %s", b.Id, b.Addr()))
			}
		}

	})
}

func (this *Clusters) verifyBrokers(zkzone *zk.ZkZone) {
	this.Ui.Output(zkzone.Name())
	zkzone.ForSortedBrokers(func(cluster string, liveBrokers map[string]*zk.BrokerZnode) {
		zkcluster := zkzone.NewCluster(cluster)
		registeredBrokers := zkcluster.RegisteredInfo().Roster

		// find diff between registeredBrokers and liveBrokers
		// loop1 find liveBrokers>registeredBrokers
		for _, broker := range liveBrokers {
			foundInRoster := false
			for _, b := range registeredBrokers {
				bid := strconv.Itoa(b.Id)
				if bid == broker.Id && broker.Addr() == b.Addr() {
					foundInRoster = true
					break
				}
			}

			if !foundInRoster {
				// should manually register the broker
				this.Ui.Output(strings.Repeat(" ", 4) +
					color.Green("+ gk clusters -z %s -s -c %s -addbroker %s:%s",
						zkzone.Name(), cluster, broker.Id, broker.Addr()))
			}
		}

		// loop2 find liveBrokers<registeredBrokers
		for _, b := range registeredBrokers {
			foundInLive := false
			for _, broker := range liveBrokers {
				bid := strconv.Itoa(b.Id)
				if bid == broker.Id && broker.Addr() == b.Addr() {
					foundInLive = true
					break
				}
			}

			if !foundInLive {
				// the broker is dead
				this.Ui.Output(strings.Repeat(" ", 4) +
					color.Red("broker %d %s is dead", b.Id, b.Addr()))
			}
		}
	})
}

func (this *Clusters) printClusters(zkzone *zk.ZkZone) {
	if this.registeredBrokers {
		this.printRegisteredBrokers(zkzone)
		return
	}

	type clusterInfo struct {
		name, path         string
		topicN, partitionN int
		err                string
		priority           int
		replicas           int
		brokerInfos        []zk.BrokerInfo
	}
	clusters := make([]clusterInfo, 0)
	zkzone.ForSortedClusters(func(zkcluster *zk.ZkCluster) {
		ci := clusterInfo{
			name: zkcluster.Name(),
			path: zkcluster.Chroot(),
		}
		if !this.verbose {
			clusters = append(clusters, ci)
			return
		}

		// verbose mode, will calculate topics and partition count
		brokerList := zkcluster.BrokerList()
		if len(brokerList) == 0 {
			ci.err = "no live brokers"
			clusters = append(clusters, ci)
			return
		}

		kfk, err := sarama.NewClient(brokerList, sarama.NewConfig())
		if err != nil {
			ci.err = err.Error()
			clusters = append(clusters, ci)
			return
		}
		topics, err := kfk.Topics()
		if err != nil {
			ci.err = err.Error()
			clusters = append(clusters, ci)
			return
		}
		partitionN := 0
		for _, topic := range topics {
			partitions, err := kfk.Partitions(topic)
			if err != nil {
				ci.err = err.Error()
				clusters = append(clusters, ci)
				continue
			}

			partitionN += len(partitions)
		}

		info := zkcluster.RegisteredInfo()
		clusters = append(clusters, clusterInfo{
			name:        zkcluster.Name(),
			path:        zkcluster.Chroot(),
			topicN:      len(topics),
			partitionN:  partitionN,
			replicas:    info.Replicas,
			priority:    info.Priority,
			brokerInfos: info.Roster,
		})
	})

	this.Ui.Output(fmt.Sprintf("%s: %d", zkzone.Name(), len(clusters)))
	if this.verbose {
		// 2 loop: 1. print the err clusters 2. print the good clusters
		for _, c := range clusters {
			if c.err == "" {
				continue
			}

			this.Ui.Output(fmt.Sprintf("%30s: %s %s", c.name, c.path,
				color.Red(c.err)))
		}

		// loop2
		for _, c := range clusters {
			if c.err != "" {
				continue
			}

			this.Ui.Output(fmt.Sprintf("%30s: %s",
				c.name, c.path))
			this.Ui.Output(strings.Repeat(" ", 4) +
				color.Blue("topics:%d partitions:%d replicas:%d priority:%d brokers:%+v",
					c.topicN, c.partitionN, c.replicas, c.priority, c.brokerInfos))
		}

		return
	}

	// not verbose mode
	for _, c := range clusters {
		this.Ui.Output(fmt.Sprintf("%30s: %s", c.name, c.path))
	}

}

func (*Clusters) Synopsis() string {
	return "Register kafka clusters to a zone"
}

func (this *Clusters) Help() string {
	help := fmt.Sprintf(`
Usage: %s clusters [options]

    Register kafka clusters to a zone

Options:

    -z zone
      Only print kafka clusters within this zone.

    -c cluster name
      The new kafka cluster name.

    -l
      Use a long listing format.

    -add cluster name
      Add a new kafka cluster into a zone.

    -p cluster zk path
      The new kafka cluster chroot path in Zookeeper.
      e,g. gk clusters -z prod -add foo -p /kafka

    -s
      Setup a cluster info.

    -priority n
      Set the priority of a cluster.

    -nickname name
      Set nickname of a cluster.
      e,g. gk clusters -z prod -c foo -s -nickname bar

    -replicas n
      Set the default replicas of a cluster.

    -addbroker id:host:port
      Register a permanent broker to a cluster.

    -delbroker id TODO
      Delete a broker from a cluster.

    -r
      Display registered permanent brokers info.

    -verify
      Verify that the online brokers are consistent with the registered brokers.

`, this.Cmd)
	return strings.TrimSpace(help)
}
<|endoftext|>


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

package user

import (
	"github.com/juju/cmd"
	"github.com/juju/errors"

	"github.com/juju/juju/cmd/juju/block"
	"github.com/juju/juju/cmd/modelcmd"
)

const disableUserDoc = `
Disabling a user stops that user from being able to log in. The user still
exists and can be reenabled using the "juju enable-user" command.  If the user is
already disabled, this command succeeds silently.

Examples:
  juju disable-user foobar

See Also:
  juju help enable-user
`

const enableUserDoc = `
Enabling a user that is disabled allows that user to log in again. The user
still exists and can be reenabled using the "juju enable-user" command.  If the
user is already enabled, this command succeeds silently.

Examples:
  juju enable-user foobar

See Also:
  juju help disable-user
`

// disenableUserBase common code for enable/disable user commands
type disenableUserBase struct {
	modelcmd.ControllerCommandBase
	api  disenableUserAPI
	User string
}

func NewDisableCommand() cmd.Command {
	return modelcmd.WrapController(&disableCommand{})
}

// disableCommand disables users.
type disableCommand struct {
	disenableUserBase
}

func NewEnableCommand() cmd.Command {
	return modelcmd.WrapController(&enableCommand{})
}

// enableCommand enables users.
type enableCommand struct {
	disenableUserBase
}

// Info implements Command.Info.
func (c *disableCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "disable-user",
		Args:    "<username>",
		Purpose: "disable a user to stop the user logging in",
		Doc:     disableUserDoc,
	}
}

// Info implements Command.Info.
func (c *enableCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "enable-user",
		Args:    "<username>",
		Purpose: "reenables a disabled user to allow the user to log in",
		Doc:     enableUserDoc,
	}
}

// Init implements Command.Init.
func (c *disenableUserBase) Init(args []string) error {
	if len(args) == 0 {
		return errors.New("no username supplied")
	}
	// TODO(thumper): support multiple users in one command,
	// and also verify that the values are valid user names.
	c.User = args[0]
	return cmd.CheckEmpty(args[1:])
}

// Username is here entirely for testing purposes to allow both the
// disableCommand and enableCommand to support a common interface that is able
// to ask for the command line supplied username.
func (c *disenableUserBase) Username() string {
	return c.User
}

// disenableUserAPI defines the API methods that the disable and enable
// commands use.
type disenableUserAPI interface {
	EnableUser(username string) error
	DisableUser(username string) error
	Close() error
}

// Run implements Command.Run.
func (c *disableCommand) Run(ctx *cmd.Context) error {
	if c.api == nil {
		api, err := c.NewUserManagerAPIClient()
		if err != nil {
			return errors.Trace(err)
		}
		c.api = api
		defer c.api.Close()
	}

	if err := c.api.DisableUser(c.User); err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}
	ctx.Infof("User %q disabled", c.User)
	return nil
}

// Run implements Command.Run.
func (c *enableCommand) Run(ctx *cmd.Context) error {
	if c.api == nil {
		api, err := c.NewUserManagerAPIClient()
		if err != nil {
			return errors.Trace(err)
		}
		c.api = api
		defer c.api.Close()
	}

	if err := c.api.EnableUser(c.User); err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}
	ctx.Infof("User %q enabled", c.User)
	return nil
}
<commit_msg>Update enable-user helptext<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package user

import (
	"github.com/juju/cmd"
	"github.com/juju/errors"

	"github.com/juju/juju/cmd/juju/block"
	"github.com/juju/juju/cmd/modelcmd"
)

var usageEnableUserSummary = `
Re-enables a previously disabled Juju user.`[1:]

var usageDisableUserDetails = `
Disabling a user stops that user from being able to log in. The user still
exists and can be reenabled using the "juju enable-user" command.  If the user is
already disabled, this command succeeds silently.

Examples:
  juju disable-user foobar

See Also:
  juju help enable-user
`

var usageDisableUserSummary = `
`[1:]

var usageEnableUserDetails = `
An enabled Juju user is one that can log in to a controller.

Examples:
    juju enable-user bob

See also:
    disable-user
    list-users
    login`[1:]

// disenableUserBase common code for enable/disable user commands
type disenableUserBase struct {
	modelcmd.ControllerCommandBase
	api  disenableUserAPI
	User string
}

func NewDisableCommand() cmd.Command {
	return modelcmd.WrapController(&disableCommand{})
}

// disableCommand disables users.
type disableCommand struct {
	disenableUserBase
}

func NewEnableCommand() cmd.Command {
	return modelcmd.WrapController(&enableCommand{})
}

// enableCommand enables users.
type enableCommand struct {
	disenableUserBase
}

// Info implements Command.Info.
func (c *disableCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "disable-user",
		Args:    "<username>",
		Purpose: "disable a user to stop the user logging in",
		Doc:     usageDisableUserDetails,
	}
}

// Info implements Command.Info.
func (c *enableCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "enable-user",
		Args:    "<user name>",
		Purpose: usageEnableUserSummary,
		Doc:     usageEnableUserDetails,
	}
}

// Init implements Command.Init.
func (c *disenableUserBase) Init(args []string) error {
	if len(args) == 0 {
		return errors.New("no username supplied")
	}
	// TODO(thumper): support multiple users in one command,
	// and also verify that the values are valid user names.
	c.User = args[0]
	return cmd.CheckEmpty(args[1:])
}

// Username is here entirely for testing purposes to allow both the
// disableCommand and enableCommand to support a common interface that is able
// to ask for the command line supplied username.
func (c *disenableUserBase) Username() string {
	return c.User
}

// disenableUserAPI defines the API methods that the disable and enable
// commands use.
type disenableUserAPI interface {
	EnableUser(username string) error
	DisableUser(username string) error
	Close() error
}

// Run implements Command.Run.
func (c *disableCommand) Run(ctx *cmd.Context) error {
	if c.api == nil {
		api, err := c.NewUserManagerAPIClient()
		if err != nil {
			return errors.Trace(err)
		}
		c.api = api
		defer c.api.Close()
	}

	if err := c.api.DisableUser(c.User); err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}
	ctx.Infof("User %q disabled", c.User)
	return nil
}

// Run implements Command.Run.
func (c *enableCommand) Run(ctx *cmd.Context) error {
	if c.api == nil {
		api, err := c.NewUserManagerAPIClient()
		if err != nil {
			return errors.Trace(err)
		}
		c.api = api
		defer c.api.Close()
	}

	if err := c.api.EnableUser(c.User); err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}
	ctx.Infof("User %q enabled", c.User)
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors All rights reserved.

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

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

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

package cmd

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

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"

	"k8s.io/minikube/pkg/drivers/kic/oci"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/exit"
	"k8s.io/minikube/pkg/minikube/localpath"
	"k8s.io/minikube/pkg/minikube/machine"
	"k8s.io/minikube/pkg/minikube/service"
	"k8s.io/minikube/pkg/minikube/tunnel"
	"k8s.io/minikube/pkg/minikube/tunnel/kic"
)

var cleanup bool

// tunnelCmd represents the tunnel command
var tunnelCmd = &cobra.Command{
	Use:   "tunnel",
	Short: "tunnel makes services of type LoadBalancer accessible on localhost",
	Long:  `tunnel creates a route to services deployed with type LoadBalancer and sets their Ingress to their ClusterIP. for a detailed example see https://minikube.sigs.k8s.io/docs/tasks/loadbalancer`,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		RootCmd.PersistentPreRun(cmd, args)
	},
	Run: func(cmd *cobra.Command, args []string) {
		manager := tunnel.NewManager()

		if cleanup {
			glog.Info("Checking for tunnels to cleanup...")
			if err := manager.CleanupNotRunningTunnels(); err != nil {
				glog.Errorf("error cleaning up: %s", err)
			}
			return
		}

		glog.Infof("Creating docker machine client...")
		api, err := machine.NewAPIClient()
		if err != nil {
			exit.WithError("error creating machine client", err)
		}
		glog.Infof("Creating k8s client...")

		// Tunnel uses the k8s clientset to query the API server for services in the LoadBalancerEmulator.
		// We define the tunnel and minikube error free if the API server responds within a second.
		// This also contributes to better UX, the tunnel status check can happen every second and
		// doesn't hang on the API server call during startup and shutdown time or if there is a temporary error.
		clientset, err := service.K8s.GetClientset(1 * time.Second)
		if err != nil {
			exit.WithError("error creating clientset", err)
		}

		cfg, err := config.Load(viper.GetString(config.MachineProfile))
		if err != nil {
			exit.WithError("Error getting config", err)
		}

		ctrlC := make(chan os.Signal, 1)
		signal.Notify(ctrlC, os.Interrupt)
		ctx, cancel := context.WithCancel(context.Background())
		go func() {
			<-ctrlC
			cancel()
		}()

		if runtime.GOOS == "darwin" && cfg.VMDriver == "docker" {
			port, err := oci.HostPortBinding("docker", "minikube", 22)
			if err != nil {
				exit.WithError("error getting ssh port", err)
			}
			sshPort := strconv.Itoa(port)
			sshKey := filepath.Join(localpath.MiniPath(), "machines", cfg.Name, "id_rsa")

			kicSSHTunnel := kic.NewSSHTunnel(ctx, sshPort, sshKey, clientset.CoreV1())
			err = kicSSHTunnel.Start()
			if err != nil {
				exit.WithError("error starting tunnel", err)
			}

			return
		}

		done, err := manager.StartTunnel(ctx, cfg.Name, api, config.DefaultLoader, clientset.CoreV1())
		if err != nil {
			exit.WithError("error starting tunnel", err)
		}
		<-done
	},
}

func init() {
	tunnelCmd.Flags().BoolVarP(&cleanup, "cleanup", "c", false, "call with cleanup=true to remove old tunnels")
}
<commit_msg>patch<commit_after>/*
Copyright 2018 The Kubernetes Authors All rights reserved.

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

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

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

package cmd

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

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"

	"k8s.io/minikube/pkg/drivers/kic/oci"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/exit"
	"k8s.io/minikube/pkg/minikube/localpath"
	"k8s.io/minikube/pkg/minikube/machine"
	"k8s.io/minikube/pkg/minikube/service"
	"k8s.io/minikube/pkg/minikube/tunnel"
	"k8s.io/minikube/pkg/minikube/tunnel/kic"
)

var cleanup bool

// tunnelCmd represents the tunnel command
var tunnelCmd = &cobra.Command{
	Use:   "tunnel",
	Short: "tunnel makes services of type LoadBalancer accessible on localhost",
	Long:  `tunnel creates a route to services deployed with type LoadBalancer and sets their Ingress to their ClusterIP. for a detailed example see https://minikube.sigs.k8s.io/docs/tasks/loadbalancer`,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		RootCmd.PersistentPreRun(cmd, args)
	},
	Run: func(cmd *cobra.Command, args []string) {
		manager := tunnel.NewManager()

		if cleanup {
			glog.Info("Checking for tunnels to cleanup...")
			if err := manager.CleanupNotRunningTunnels(); err != nil {
				glog.Errorf("error cleaning up: %s", err)
			}
			return
		}

		glog.Infof("Creating docker machine client...")
		api, err := machine.NewAPIClient()
		if err != nil {
			exit.WithError("error creating machine client", err)
		}
		glog.Infof("Creating k8s client...")

		// Tunnel uses the k8s clientset to query the API server for services in the LoadBalancerEmulator.
		// We define the tunnel and minikube error free if the API server responds within a second.
		// This also contributes to better UX, the tunnel status check can happen every second and
		// doesn't hang on the API server call during startup and shutdown time or if there is a temporary error.
		clientset, err := service.K8s.GetClientset(1 * time.Second)
		if err != nil {
			exit.WithError("error creating clientset", err)
		}

		cfg, err := config.Load(viper.GetString(config.MachineProfile))
		if err != nil {
			exit.WithError("Error getting config", err)
		}

		ctrlC := make(chan os.Signal, 1)
		signal.Notify(ctrlC, os.Interrupt)
		ctx, cancel := context.WithCancel(context.Background())
		go func() {
			<-ctrlC
			cancel()
		}()

		if runtime.GOOS == "darwin" && cfg.Driver == oci.Docker {
			port, err := oci.HostPortBinding(oci.Docker, cfg.Name, 22)
			if err != nil {
				exit.WithError("error getting ssh port", err)
			}
			sshPort := strconv.Itoa(port)
			sshKey := filepath.Join(localpath.MiniPath(), "machines", cfg.Name, "id_rsa")

			kicSSHTunnel := kic.NewSSHTunnel(ctx, sshPort, sshKey, clientset.CoreV1())
			err = kicSSHTunnel.Start()
			if err != nil {
				exit.WithError("error starting tunnel", err)
			}

			return
		}

		done, err := manager.StartTunnel(ctx, cfg.Name, api, config.DefaultLoader, clientset.CoreV1())
		if err != nil {
			exit.WithError("error starting tunnel", err)
		}
		<-done
	},
}

func init() {
	tunnelCmd.Flags().BoolVarP(&cleanup, "cleanup", "c", false, "call with cleanup=true to remove old tunnels")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/johnweldon/nginx-log-parse/nginx"
	"github.com/johnweldon/nginx-log-parse/util"
)

func main() {
	p := util.NewParser(os.Stdin)
	for {
		select {
		case line := <-p.LineCh:
			record, ok := line.(nginx.RequestLine)
			if !ok {
				continue
			}
			fmt.Fprintf(os.Stdout, "%s\n", record)
		case <-p.Dying():
			return
		}
	}
}
<commit_msg>Make cmd line app work too<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/johnweldon/nginx-log-parse/util"
)

func main() {
	p := util.NewParser(os.Stdin)
	for {
		select {
		case line := <-p.LineCh:
			if line != nil {
				fmt.Fprintf(os.Stdout, "%s\n", line)
			}
		case <-p.Dying():
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/timeredbull/tsuru/cmd"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
)

type Service struct{}
type ServiceCreate struct{}

func (c *Service) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "service",
		Usage:   "service (init|list|create|remove|update) [args]",
		Desc:    "manage services.",
		MinArgs: 1,
	}
}

func (c *Service) Subcommands() map[string]interface{} {
	return map[string]interface{}{
		"create": &ServiceCreate{},
	}
}

func (c *ServiceCreate) Info() *cmd.Info {
	desc := "Creates a service based on a passed manifest. The manifest format should be a yaml and follow the standard described in the documentation (should link to it here)"
	return &cmd.Info{
		Name:    "create",
		Usage:   "create path/to/manifesto",
		Desc:    desc,
		MinArgs: 1,
	}
}

func (c *ServiceCreate) Run(context *cmd.Context, client cmd.Doer) error {
	manifest := context.Args[0]
	url := cmd.GetUrl("/services")
	b, err := ioutil.ReadFile(manifest)
	if err != nil {
		return err
	}
	body := strings.NewReader(string(b))
	request, err := http.NewRequest("POST", url, body)
	if err != nil {
		return err
	}
	r, err := client.Do(request)
	if err != nil {
		return err
	}
	b, err = ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, string(b)+"\n")
	return nil
}

type ServiceRemove struct{}

func (c *ServiceRemove) Run(context *cmd.Context, client cmd.Doer) error {
	serviceName := context.Args[0]
	url := cmd.GetUrl("/services/" + serviceName)
	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, "Service successfully removed.\n")
	return nil
}

func (c *ServiceRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "remove",
		Usage:   "remove <servicename>",
		Desc:    "removes a service from catalog",
		MinArgs: 1,
	}
}

type ServiceList struct{}

func (c *ServiceList) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "list",
		Usage: "list",
		Desc:  "list services that belongs to user's team and it's service instances.",
	}
}

type ServiceModel struct {
	Service   string
	Instances []string
}

func (c *ServiceList) Run(ctxt *cmd.Context, client cmd.Doer) error {
	url := cmd.GetUrl("/services")
	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(request)
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	rslt, err := c.show(b)
	if err != nil {
		return err
	}
	ctxt.Stdout.Write(rslt)
	return nil
}

func (c *ServiceList) show(b []byte) ([]byte, error) {
	var services []ServiceModel
	err := json.Unmarshal(b, &services)
	if err != nil {
		return []byte{}, err
	}
	table := cmd.NewTable()
	table.Headers = cmd.Row([]string{"Services", "Instances"})
	for _, s := range services {
		insts := strings.Join(s.Instances, ", ")
		r := cmd.Row([]string{s.Service, insts})
		table.AddRow(r)
	}
	return table.Bytes(), nil
}
<commit_msg>cmd: Returning error (if any) when making request<commit_after>package main

import (
	"encoding/json"
	"github.com/timeredbull/tsuru/cmd"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
)

type Service struct{}
type ServiceCreate struct{}

func (c *Service) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "service",
		Usage:   "service (init|list|create|remove|update) [args]",
		Desc:    "manage services.",
		MinArgs: 1,
	}
}

func (c *Service) Subcommands() map[string]interface{} {
	return map[string]interface{}{
		"create": &ServiceCreate{},
	}
}

func (c *ServiceCreate) Info() *cmd.Info {
	desc := "Creates a service based on a passed manifest. The manifest format should be a yaml and follow the standard described in the documentation (should link to it here)"
	return &cmd.Info{
		Name:    "create",
		Usage:   "create path/to/manifesto",
		Desc:    desc,
		MinArgs: 1,
	}
}

func (c *ServiceCreate) Run(context *cmd.Context, client cmd.Doer) error {
	manifest := context.Args[0]
	url := cmd.GetUrl("/services")
	b, err := ioutil.ReadFile(manifest)
	if err != nil {
		return err
	}
	body := strings.NewReader(string(b))
	request, err := http.NewRequest("POST", url, body)
	if err != nil {
		return err
	}
	r, err := client.Do(request)
	if err != nil {
		return err
	}
	b, err = ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, string(b)+"\n")
	return nil
}

type ServiceRemove struct{}

func (c *ServiceRemove) Run(context *cmd.Context, client cmd.Doer) error {
	serviceName := context.Args[0]
	url := cmd.GetUrl("/services/" + serviceName)
	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	io.WriteString(context.Stdout, "Service successfully removed.\n")
	return nil
}

func (c *ServiceRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "remove",
		Usage:   "remove <servicename>",
		Desc:    "removes a service from catalog",
		MinArgs: 1,
	}
}

type ServiceList struct{}

func (c *ServiceList) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "list",
		Usage: "list",
		Desc:  "list services that belongs to user's team and it's service instances.",
	}
}

type ServiceModel struct {
	Service   string
	Instances []string
}

func (c *ServiceList) Run(ctxt *cmd.Context, client cmd.Doer) error {
	url := cmd.GetUrl("/services")
	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(request)
	if err != nil {
		return err
	}
	b, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return err
	}
	rslt, err := c.show(b)
	if err != nil {
		return err
	}
	ctxt.Stdout.Write(rslt)
	return nil
}

func (c *ServiceList) show(b []byte) ([]byte, error) {
	var services []ServiceModel
	err := json.Unmarshal(b, &services)
	if err != nil {
		return []byte{}, err
	}
	table := cmd.NewTable()
	table.Headers = cmd.Row([]string{"Services", "Instances"})
	for _, s := range services {
		insts := strings.Join(s.Instances, ", ")
		r := cmd.Row([]string{s.Service, insts})
		table.AddRow(r)
	}
	return table.Bytes(), nil
}
<|endoftext|>


<commit_before>package aeon

import (
	"fmt"
	"time"

	"github.com/ninjasphere/go-openzwave"
	"github.com/ninjasphere/go-openzwave/CC"

	"github.com/ninjasphere/go-ninja/channels"

	"github.com/ninjasphere/driver-go-zwave/spi"
	"github.com/ninjasphere/driver-go-zwave/utils"
)

const (
	maxDeviceBrightness = 100             // by experiment, a level of 100 does not work for this device
	maxDelay            = time.Second * 5 // maximum delay for apply calls
)

var (
	level_switch = openzwave.ValueID{CC.SWITCH_MULTILEVEL, 1, 0}
)

type illuminator struct {
	spi.Device

	onOffChannel      *channels.OnOffChannel
	brightnessChannel *channels.BrightnessChannel

	// brightness is a cache of the current brightness when the device is switched off.
	// It is updated from the device on a confirmed attempt to adjust the level to a non-zero value
	brightness uint8

	refresh chan struct{} // used to wait for confirmation of updates after a level change

	emitter utils.Emitter
}

func IlluminatorFactory(driver spi.Driver, node openzwave.Node) openzwave.Device {
	device := &illuminator{}

	device.Init(driver, node)

	var ok bool

	device.brightness, ok = device.Node.GetValueWithId(level_switch).GetUint8()
	if !ok || device.brightness == 0 {
		// we have to reset brightness to 100 since we apply brightness when
		// we switch it on

		//
		// one implication of this is that if the controller is removed
		// then replaced, while the light is off, the original brightness
		// will be lost
		//

		device.brightness = maxDeviceBrightness
	}

	(*device.Info.Signatures)["ninja:thingType"] = "light"

	device.refresh = make(chan struct{}, 0)

	device.emitter = utils.Filter(
		func(level utils.Equatable) {
			device.unconditionalSendLightState(level.(*utils.WrappedUint8).Unwrap())
		},
		30*time.Second)

	return device
}

// ZWave protocols

func (device *illuminator) NodeAdded() {

	node := device.Node
	api := device.Driver.ZWave()
	conn := device.Driver.Connection()

	err := conn.ExportDevice(device)
	if err != nil {
		api.Logger().Infof("failed to export node: %v as device: %s", node, err)
		return
	}

	device.onOffChannel = channels.NewOnOffChannel(device)
	err = conn.ExportChannel(device, device.onOffChannel, "on-off")
	if err != nil {
		api.Logger().Infof("failed to export on-off channel for %v: %s", node, err)
		return
	}

	device.brightnessChannel = channels.NewBrightnessChannel(device)
	err = conn.ExportChannel(device, device.brightnessChannel, "brightness")
	if err != nil {
		api.Logger().Infof("failed to export brightness channel for %v: %s", node, err)
	}

	device.Node.GetValueWithId(level_switch).SetPollingState(true)

}

func (device *illuminator) NodeChanged() {
}

func (device *illuminator) NodeRemoved() {
}

func (device *illuminator) ValueChanged(v openzwave.Value) {
	switch v.Id() {
	case level_switch:
		select {
		case device.refresh <- struct{}{}:
		default:
			device.sendLightState()
		}
	}
}

// Ninja protocols

func (device *illuminator) SetOnOff(state bool) error {
	level := uint8(0)
	if state {
		level = device.brightness
	}
	return device.setDeviceLevel(level)
}

func (device *illuminator) ToggleOnOff() error {
	level, ok := device.Node.GetValueWithId(level_switch).GetUint8()
	if !ok {
		return fmt.Errorf("Unable to determine current state of switch")
	}
	if level == 0 {
		return device.setDeviceLevel(device.brightness)
	} else {
		return device.setDeviceLevel(0)
	}
}

func (device *illuminator) SetBrightness(state float64) error {

	var err error = nil
	if state < 0 {
		state = 0
	} else if state > 1.0 {
		state = 1.0
	}
	level, ok := device.Node.GetValueWithId(level_switch).GetUint8()
	if ok {
		newLevel := uint8(state * maxDeviceBrightness)
		if level > 0 {
			err = device.setDeviceLevel(newLevel)
		} else {
			device.brightness = newLevel // to be applied when device is switched on
			device.emitter.Reset()
		}
	} else {
		err = fmt.Errorf("Unable to apply brightness - get failed.")
	}
	return err
}

//
// Issue a set against the OpenZWave API, then wait until the refreshed
// value matches the requested level or until a timeout, issuing refreshes
// as required.
//
func (device *illuminator) setDeviceLevel(level uint8) error {

	val := device.Node.GetValueWithId(level_switch)

	if level >= maxDeviceBrightness {
		// aeon will reject attempts to set the level to exactly 100
		level = maxDeviceBrightness - 1
	}

	if !val.SetUint8(level) {
		return fmt.Errorf("Failed to set level to %d - set failed", level)
	}
	timer := time.NewTimer(maxDelay)

	// loop until timeout or until refresh yields expected level

	for {
		if !val.Refresh() {
			return fmt.Errorf("Failed to set required level to %d - refresh failed", level)
		}
		select {
		case timeout := <-timer.C:
			_ = timeout
			if level != 0 {
				device.brightness = level
			}
			return fmt.Errorf("Failed to set required level to %d - timeout", level)
		case refreshed := <-device.refresh:
			_ = refreshed
			current, ok := val.GetUint8()
			if ok && current == level {
				if level != 0 {
					device.brightness = level
				}
				device.emitter.Reset()
				return nil
			}
		}
	}
}

//
// This call is used to reflect notifications about the current
// state of the light back to towards the ninja network
//
func (device *illuminator) sendLightState() {
	level, ok := device.Node.GetValueWithId(level_switch).GetUint8()
	if ok {
		//
		// Emit the current state, but filter out levels that don't change
		// within a specified period.
		//
		device.emitter.Emit(utils.WrapUint8(level))
	}
}

func (device *illuminator) unconditionalSendLightState(level uint8) {
	if device.brightness == maxDeviceBrightness-1 {
		device.brightness = 100
	}

	onOff := level != 0
	brightness := float64(device.brightness) / maxDeviceBrightness

	device.onOffChannel.SendState(onOff)
	device.brightnessChannel.SendState(brightness)
}
<commit_msg>Add power and energy channels to smart illuminator.<commit_after>package aeon

import (
	"fmt"
	"time"

	"github.com/ninjasphere/go-openzwave"
	"github.com/ninjasphere/go-openzwave/CC"

	"github.com/ninjasphere/go-ninja/channels"

	"github.com/ninjasphere/driver-go-zwave/spi"
	"github.com/ninjasphere/driver-go-zwave/utils"
)

const (
	maxDeviceBrightness = 100             // by experiment, a level of 100 does not work for this device
	maxDelay            = time.Second * 5 // maximum delay for apply calls
)

var (
	level_switch = openzwave.ValueID{CC.SWITCH_MULTILEVEL, 1, 0}
	energy_meter = openzwave.ValueID{CC.METER, 1, 0}
	power_meter  = openzwave.ValueID{CC.METER, 1, 8}
)

type illuminator struct {
	spi.Device

	onOffChannel      *channels.OnOffChannel
	brightnessChannel *channels.BrightnessChannel
	powerChannel      *channels.PowerChannel
	energyChannel     *channels.EnergyChannel

	// brightness is a cache of the current brightness when the device is switched off.
	// It is updated from the device on a confirmed attempt to adjust the level to a non-zero value
	brightness uint8

	refresh chan struct{} // used to wait for confirmation of updates after a level change

	emitter utils.Emitter
}

func IlluminatorFactory(driver spi.Driver, node openzwave.Node) openzwave.Device {
	device := &illuminator{}

	device.Init(driver, node)

	var ok bool

	device.brightness, ok = device.Node.GetValueWithId(level_switch).GetUint8()
	if !ok || device.brightness == 0 {
		// we have to reset brightness to 100 since we apply brightness when
		// we switch it on

		//
		// one implication of this is that if the controller is removed
		// then replaced, while the light is off, the original brightness
		// will be lost
		//

		device.brightness = maxDeviceBrightness
	}

	(*device.Info.Signatures)["ninja:thingType"] = "light"

	device.refresh = make(chan struct{}, 0)

	device.emitter = utils.Filter(
		func(level utils.Equatable) {
			device.unconditionalSendLightState(level.(*utils.WrappedUint8).Unwrap())
		},
		30*time.Second)

	return device
}

// ZWave protocols

func (device *illuminator) NodeAdded() {

	node := device.Node
	api := device.Driver.ZWave()
	conn := device.Driver.Connection()

	err := conn.ExportDevice(device)
	if err != nil {
		api.Logger().Infof("failed to export node: %v as device: %s", node, err)
		return
	}

	device.onOffChannel = channels.NewOnOffChannel(device)
	err = conn.ExportChannel(device, device.onOffChannel, "on-off")
	if err != nil {
		api.Logger().Infof("failed to export on-off channel for %v: %s", node, err)
		return
	}

	device.brightnessChannel = channels.NewBrightnessChannel(device)
	err = conn.ExportChannel(device, device.brightnessChannel, "brightness")
	if err != nil {
		api.Logger().Infof("failed to export brightness channel for %v: %s", node, err)
	}

	device.powerChannel = channels.NewPowerChannel(device)
	err = conn.ExportChannel(device, device.powerChannel, "power")
	if err != nil {
		api.Logger().Infof("failed to export power channel for %v: %s", node, err)
	}

	device.energyChannel = channels.NewEnergyChannel(device)
	err = conn.ExportChannel(device, device.energyChannel, "energy")
	if err != nil {
		api.Logger().Infof("failed to export energy channel for %v: %s", node, err)
	}

	device.Node.GetValueWithId(level_switch).SetPollingState(true)
	device.Node.GetValueWithId(power_meter).SetPollingState(true)
	device.Node.GetValueWithId(energy_meter).SetPollingState(true)

}

func (device *illuminator) NodeChanged() {
}

func (device *illuminator) NodeRemoved() {
}

func (device *illuminator) ValueChanged(v openzwave.Value) {
	switch v.Id() {
	case level_switch:
		select {
		case device.refresh <- struct{}{}:
		default:
			device.sendLightState()
		}
	case power_meter:
		readingW, ok := v.GetFloat()
		if ok {
			device.powerChannel.SendState(readingW)
		}
	case energy_meter:
		readingKWH, ok := v.GetFloat()
		if ok {
			watts := readingKWH * 1000
			device.energyChannel.SendState(watts)
		}
	}
}

// Ninja protocols

func (device *illuminator) SetOnOff(state bool) error {
	level := uint8(0)
	if state {
		level = device.brightness
	}
	return device.setDeviceLevel(level)
}

func (device *illuminator) ToggleOnOff() error {
	level, ok := device.Node.GetValueWithId(level_switch).GetUint8()
	if !ok {
		return fmt.Errorf("Unable to determine current state of switch")
	}
	if level == 0 {
		return device.setDeviceLevel(device.brightness)
	} else {
		return device.setDeviceLevel(0)
	}
}

func (device *illuminator) SetBrightness(state float64) error {

	var err error = nil
	if state < 0 {
		state = 0
	} else if state > 1.0 {
		state = 1.0
	}
	level, ok := device.Node.GetValueWithId(level_switch).GetUint8()
	if ok {
		newLevel := uint8(state * maxDeviceBrightness)
		if level > 0 {
			err = device.setDeviceLevel(newLevel)
		} else {
			device.brightness = newLevel // to be applied when device is switched on
			device.emitter.Reset()
		}
	} else {
		err = fmt.Errorf("Unable to apply brightness - get failed.")
	}
	return err
}

//
// Issue a set against the OpenZWave API, then wait until the refreshed
// value matches the requested level or until a timeout, issuing refreshes
// as required.
//
func (device *illuminator) setDeviceLevel(level uint8) error {

	val := device.Node.GetValueWithId(level_switch)

	if level >= maxDeviceBrightness {
		// aeon will reject attempts to set the level to exactly 100
		level = maxDeviceBrightness - 1
	}

	if !val.SetUint8(level) {
		return fmt.Errorf("Failed to set level to %d - set failed", level)
	}
	timer := time.NewTimer(maxDelay)

	// loop until timeout or until refresh yields expected level

	for {
		if !val.Refresh() {
			return fmt.Errorf("Failed to set required level to %d - refresh failed", level)
		}
		select {
		case timeout := <-timer.C:
			_ = timeout
			if level != 0 {
				device.brightness = level
			}
			return fmt.Errorf("Failed to set required level to %d - timeout", level)
		case refreshed := <-device.refresh:
			_ = refreshed
			current, ok := val.GetUint8()
			if ok && current == level {
				if level != 0 {
					device.brightness = level
				}
				device.emitter.Reset()
				return nil
			}
		}
	}
}

//
// This call is used to reflect notifications about the current
// state of the light back to towards the ninja network
//
func (device *illuminator) sendLightState() {
	level, ok := device.Node.GetValueWithId(level_switch).GetUint8()
	if ok {
		//
		// Emit the current state, but filter out levels that don't change
		// within a specified period.
		//
		device.emitter.Emit(utils.WrapUint8(level))
	}
}

func (device *illuminator) unconditionalSendLightState(level uint8) {
	if device.brightness == maxDeviceBrightness-1 {
		device.brightness = 100
	}

	onOff := level != 0
	brightness := float64(device.brightness) / maxDeviceBrightness

	device.onOffChannel.SendState(onOff)
	device.brightnessChannel.SendState(brightness)
}
<|endoftext|>


<commit_before>// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
	"image"

	. "github.com/gizak/termui"
)

type List struct {
	Block
	Rows             []string
	WrapText         bool
	TextStyle        Style
	SelectedRow      uint
	topRow           uint
	SelectedRowStyle Style
}

func NewList() *List {
	return &List{
		Block:            *NewBlock(),
		TextStyle:        Theme.List.Text,
		SelectedRowStyle: Theme.List.Text,
	}
}

func (self *List) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	point := self.Inner.Min

	if self.SelectedRow >= uint(self.Inner.Max.Y)+self.topRow-2 {
		self.topRow = self.SelectedRow-uint(self.Inner.Max.Y)+2
	} else if self.SelectedRow < self.topRow {
		self.topRow = self.SelectedRow
	}

	for row := self.topRow; row < uint(len(self.Rows)) && point.Y < self.Inner.Max.Y; row++ {
		cells := ParseText(self.Rows[row], self.TextStyle)
		if self.WrapText {
			cells = WrapCells(cells, uint(self.Inner.Dx()))
		}
		for j := 0; j < len(cells) && point.Y < self.Inner.Max.Y; j++ {
			style := cells[j].Style
			if row == self.SelectedRow {
				style = self.SelectedRowStyle
			}
			if cells[j].Rune == '\n' {
				point = image.Pt(self.Inner.Min.X, point.Y+1)
			} else {
				if point.X+1 == self.Inner.Max.X+1 && len(cells) > self.Inner.Dx() {
					buf.SetCell(NewCell(ELLIPSES, style), point.Add(image.Pt(-1, 0)))
					break
				} else {
					buf.SetCell(NewCell(cells[j].Rune, style), point)
					point = point.Add(image.Pt(1, 0))
				}
			}
		}
		point = image.Pt(self.Inner.Min.X, point.Y+1)
	}

	if self.topRow > 0 {
		buf.SetCell(
			NewCell(UP_ARROW, NewStyle(ColorWhite)),
			image.Pt(self.Inner.Max.X-1, self.Inner.Min.Y),
		)
	}
	if len(self.Rows) > int(self.topRow)+self.Inner.Dy() {
		buf.SetCell(
			NewCell(DOWN_ARROW, NewStyle(ColorWhite)),
			image.Pt(self.Inner.Max.X-1, self.Inner.Max.Y-1),
		)
	}
}

func (self *List) ScrollUp() {
	if self.SelectedRow > 0 {
		self.SelectedRow--
		if self.SelectedRow < self.topRow {
			self.topRow--
		}
	}
}

func (self *List) ScrollDown() {
	if self.SelectedRow < uint(len(self.Rows))-1 {
		self.SelectedRow++
		if self.SelectedRow-self.topRow > uint(self.Inner.Dy()-1) {
			self.topRow++
		}
	}
}

func (self *List) PageUp() {   // Goes up one whole page.
	if int(self.SelectedRow)-self.Inner.Dy() >= 0 {
		self.topRow -= uint(self.Inner.Dy())
	} else {			// If at the first 'page', then go to the top and select the first item.
		self.topRow = 0
	}
	self.SelectedRow = self.topRow
}

func (self *List) PageDown() {  // Down one whole page
	if len(self.Rows)-int(self.topRow) > self.Inner.Dy() {
		self.topRow += uint(self.Inner.Dy())
		self.SelectedRow = self.topRow
	} else {   // If at last 'page', then select last item.
		self.SelectedRow = uint(len(self.Rows)-1)
	}
}
<commit_msg>Ran go fmt to clean it up a bit.<commit_after>// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
	"image"

	. "github.com/gizak/termui"
)

type List struct {
	Block
	Rows             []string
	WrapText         bool
	TextStyle        Style
	SelectedRow      uint
	topRow           uint
	SelectedRowStyle Style
}

func NewList() *List {
	return &List{
		Block:            *NewBlock(),
		TextStyle:        Theme.List.Text,
		SelectedRowStyle: Theme.List.Text,
	}
}

func (self *List) Draw(buf *Buffer) {
	self.Block.Draw(buf)

	point := self.Inner.Min

	if self.SelectedRow >= uint(self.Inner.Max.Y)+self.topRow-2 {
		self.topRow = self.SelectedRow - uint(self.Inner.Max.Y) + 2
	} else if self.SelectedRow < self.topRow {
		self.topRow = self.SelectedRow
	}

	for row := self.topRow; row < uint(len(self.Rows)) && point.Y < self.Inner.Max.Y; row++ {
		cells := ParseText(self.Rows[row], self.TextStyle)
		if self.WrapText {
			cells = WrapCells(cells, uint(self.Inner.Dx()))
		}
		for j := 0; j < len(cells) && point.Y < self.Inner.Max.Y; j++ {
			style := cells[j].Style
			if row == self.SelectedRow {
				style = self.SelectedRowStyle
			}
			if cells[j].Rune == '\n' {
				point = image.Pt(self.Inner.Min.X, point.Y+1)
			} else {
				if point.X+1 == self.Inner.Max.X+1 && len(cells) > self.Inner.Dx() {
					buf.SetCell(NewCell(ELLIPSES, style), point.Add(image.Pt(-1, 0)))
					break
				} else {
					buf.SetCell(NewCell(cells[j].Rune, style), point)
					point = point.Add(image.Pt(1, 0))
				}
			}
		}
		point = image.Pt(self.Inner.Min.X, point.Y+1)
	}

	if self.topRow > 0 {
		buf.SetCell(
			NewCell(UP_ARROW, NewStyle(ColorWhite)),
			image.Pt(self.Inner.Max.X-1, self.Inner.Min.Y),
		)
	}
	if len(self.Rows) > int(self.topRow)+self.Inner.Dy() {
		buf.SetCell(
			NewCell(DOWN_ARROW, NewStyle(ColorWhite)),
			image.Pt(self.Inner.Max.X-1, self.Inner.Max.Y-1),
		)
	}
}

func (self *List) ScrollUp() {
	if self.SelectedRow > 0 {
		self.SelectedRow--
		if self.SelectedRow < self.topRow {
			self.topRow--
		}
	}
}

func (self *List) ScrollDown() {
	if self.SelectedRow < uint(len(self.Rows))-1 {
		self.SelectedRow++
		if self.SelectedRow-self.topRow > uint(self.Inner.Dy()-1) {
			self.topRow++
		}
	}
}

func (self *List) PageUp() { // Goes up one whole page.
	if int(self.SelectedRow)-self.Inner.Dy() >= 0 {
		self.topRow -= uint(self.Inner.Dy())
	} else { // If at the first 'page', then go to the top and select the first item.
		self.topRow = 0
	}
	self.SelectedRow = self.topRow
}

func (self *List) PageDown() { // Down one whole page
	if len(self.Rows)-int(self.topRow) > self.Inner.Dy() {
		self.topRow += uint(self.Inner.Dy())
		self.SelectedRow = self.topRow
	} else { // If at last 'page', then select last item.
		self.SelectedRow = uint(len(self.Rows) - 1)
	}
}
<|endoftext|>