<commit_before>// Package slack implements the Channel type, for getting information about
// Slack channels, and the User type, for sending chats.
package slack

import (
	"fmt"
	"log"
	"net/url"
	"os"
)

const team = "monkeytacos"

var apiToken string

// TODO: can i get rid of one of these response types?
type channelResponse struct {
	Channel Channel `json:"channel"`
	Ok      bool    `json:"ok"`
	Err     string  `json:"error"`
}

type channelListResponse struct {
	Channels []Channel `json:"channels"`
	Ok       bool      `json:"ok"`
	Err      string    `json:"error"`
}

type Channel struct {
	ID      string   `json:"id"`
	Name    string   `json:"name"`
	Members []string `json:"members"`
	Team    string   // set in NewChannel
}

func init() {
	apiToken = os.Getenv("SLACK_API_TOKEN")
	if apiToken == "" {
		log.Fatal("SLACK_API_TOKEN not set")
	}
}

func NewChannel(name string) (Channel, error) {
	var emptyChannel Channel

	qsp := &url.Values{}
	qsp.Set("channel", name)
	listURL := NewURL("channels.list", qsp)
	cl := channelListResponse{}
	err := apiCall(listURL, &cl)
	if err != nil {
		return emptyChannel, APIError{err.Error()}
	}

	if cl.Ok != true {
		return emptyChannel, APIError{cl.Err}
	}

	for _, ch := range cl.Channels {
		if ch.Name == name {
			ch.Team = team
			return ch, nil
		}
	}

	return emptyChannel, fmt.Errorf("no channel named %q on team %q", name, team)
}

func (ch Channel) String() string {
	return fmt.Sprintf("%#v", ch)
}

func (ch *Channel) UpdateMembers() error {
	qsp := &url.Values{}
	qsp.Set("channel", ch.ID)
	channelURL := NewURL("channels.info", qsp)

	cr := channelResponse{}
	err := apiCall(channelURL, &cr)
	if err != nil {
		return APIError{err.Error()}
	}

	if !cr.Ok {
		return APIError{cr.Err}
	}

	ch.Members = cr.Channel.Members
	return nil
}
<commit_msg>Combine channel response structs into one<commit_after>// Package slack implements the Channel type, for getting information about
// Slack channels, and the User type, for sending chats.
package slack

import (
	"fmt"
	"log"
	"net/url"
	"os"
)

const team = "monkeytacos"

var apiToken string

func init() {
	apiToken = os.Getenv("SLACK_API_TOKEN")
	if apiToken == "" {
		log.Fatal("SLACK_API_TOKEN not set")
	}
}

type channelResponse struct {
	Channel  Channel   `json:"channel"`
	Channels []Channel `json:"channels"`
	Ok       bool      `json:"ok"`
	Err      string    `json:"error"`
}

type Channel struct {
	ID      string   `json:"id"`
	Name    string   `json:"name"`
	Members []string `json:"members"`
	Team    string   // set in NewChannel
}

func NewChannel(name string) (Channel, error) {
	var emptyChannel Channel

	qsp := &url.Values{}
	qsp.Set("channel", name)
	listURL := NewURL("channels.list", qsp)
	cr := channelResponse{}
	err := apiCall(listURL, &cr)
	if err != nil {
		return emptyChannel, APIError{err.Error()}
	}

	if cr.Ok != true {
		return emptyChannel, APIError{cr.Err}
	}

	for _, ch := range cr.Channels {
		if ch.Name == name {
			ch.Team = team
			return ch, nil
		}
	}

	return emptyChannel, fmt.Errorf("no channel named %q on team %q", name, team)
}

func (ch Channel) String() string {
	return fmt.Sprintf("%#v", ch)
}

func (ch *Channel) UpdateMembers() error {
	qsp := &url.Values{}
	qsp.Set("channel", ch.ID)
	channelURL := NewURL("channels.info", qsp)

	cr := channelResponse{}
	err := apiCall(channelURL, &cr)
	if err != nil {
		return APIError{err.Error()}
	}

	if !cr.Ok {
		return APIError{cr.Err}
	}

	ch.Members = cr.Channel.Members
	return nil
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2018 Red Hat, Inc.
 *
 */

package admitters

import (
	"encoding/json"

	"github.com/golang/mock/gomock"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	admissionv1 "k8s.io/api/admission/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"

	v1 "kubevirt.io/api/core/v1"
	snapshotv1 "kubevirt.io/api/snapshot/v1alpha1"
	"kubevirt.io/client-go/kubecli"
	"kubevirt.io/kubevirt/pkg/testutils"
	"kubevirt.io/kubevirt/pkg/virt-api/webhooks"
	virtconfig "kubevirt.io/kubevirt/pkg/virt-config"
)

var _ = Describe("Validating VirtualMachineSnapshot Admitter", func() {
	vmName := "vm"
	apiGroup := "kubevirt.io"

	config, _, kvInformer := testutils.NewFakeClusterConfigUsingKVConfig(&v1.KubeVirtConfiguration{})

	Context("Without feature gate enabled", func() {
		It("should reject anything", func() {
			snapshot := &snapshotv1.VirtualMachineSnapshot{
				Spec: snapshotv1.VirtualMachineSnapshotSpec{},
			}

			ar := createSnapshotAdmissionReview(snapshot)
			resp := createTestVMSnapshotAdmitter(config, nil).Admit(ar)
			Expect(resp.Allowed).To(BeFalse())
			Expect(resp.Result.Message).Should(Equal("snapshot feature gate not enabled"))
		})
	})

	Context("With feature gate enabled", func() {
		enableFeatureGate := func(featureGate string) {
			testutils.UpdateFakeKubeVirtClusterConfig(kvInformer, &v1.KubeVirt{
				Spec: v1.KubeVirtSpec{
					Configuration: v1.KubeVirtConfiguration{
						DeveloperConfiguration: &v1.DeveloperConfiguration{
							FeatureGates: []string{featureGate},
						},
					},
				},
			})
		}
		disableFeatureGates := func() {
			testutils.UpdateFakeKubeVirtClusterConfig(kvInformer, &v1.KubeVirt{
				Spec: v1.KubeVirtSpec{
					Configuration: v1.KubeVirtConfiguration{
						DeveloperConfiguration: &v1.DeveloperConfiguration{
							FeatureGates: make([]string, 0),
						},
					},
				},
			})
		}

		BeforeEach(func() {
			enableFeatureGate("Snapshot")
		})

		AfterEach(func() {
			disableFeatureGates()
		})

		It("should reject invalid request resource", func() {
			ar := &admissionv1.AdmissionReview{
				Request: &admissionv1.AdmissionRequest{
					Resource: webhooks.VirtualMachineGroupVersionResource,
				},
			}

			resp := createTestVMSnapshotAdmitter(config, nil).Admit(ar)
			Expect(resp.Allowed).To(BeFalse())
			Expect(resp.Result.Message).Should(ContainSubstring("unexpected resource"))
		})

		It("should reject missing apigroup", func() {
			snapshot := &snapshotv1.VirtualMachineSnapshot{
				Spec: snapshotv1.VirtualMachineSnapshotSpec{},
			}

			ar := createSnapshotAdmissionReview(snapshot)
			resp := createTestVMSnapshotAdmitter(config, nil).Admit(ar)
			Expect(resp.Allowed).To(BeFalse())
			Expect(len(resp.Result.Details.Causes)).To(Equal(1))
			Expect(resp.Result.Details.Causes[0].Field).To(Equal("spec.source.apiGroup"))
		})

		It("should reject when VM does not exist", func() {
			snapshot := &snapshotv1.VirtualMachineSnapshot{
				Spec: snapshotv1.VirtualMachineSnapshotSpec{
					Source: corev1.TypedLocalObjectReference{
						APIGroup: &apiGroup,
						Kind:     "VirtualMachine",
						Name:     vmName,
					},
				},
			}

			ar := createSnapshotAdmissionReview(snapshot)
			resp := createTestVMSnapshotAdmitter(config, nil).Admit(ar)
			Expect(resp.Allowed).To(BeFalse())
			Expect(len(resp.Result.Details.Causes)).To(Equal(1))
			Expect(resp.Result.Details.Causes[0].Field).To(Equal("spec.source.name"))
		})

		It("should reject spec update", func() {
			snapshot := &snapshotv1.VirtualMachineSnapshot{
				Spec: snapshotv1.VirtualMachineSnapshotSpec{
					Source: corev1.TypedLocalObjectReference{
						APIGroup: &apiGroup,
						Kind:     "VirtualMachine",
						Name:     vmName,
					},
				},
			}

			oldSnapshot := &snapshotv1.VirtualMachineSnapshot{
				Spec: snapshotv1.VirtualMachineSnapshotSpec{
					Source: corev1.TypedLocalObjectReference{
						APIGroup: &apiGroup,
						Kind:     "VirtualMachine",
						Name:     "baz",
					},
				},
			}

			ar := createSnapshotUpdateAdmissionReview(oldSnapshot, snapshot)
			resp := createTestVMSnapshotAdmitter(config, nil).Admit(ar)
			Expect(resp.Allowed).To(BeFalse())
			Expect(len(resp.Result.Details.Causes)).To(Equal(1))
			Expect(resp.Result.Details.Causes[0].Field).To(Equal("spec"))
		})

		It("should allow metadata update", func() {
			oldSnapshot := &snapshotv1.VirtualMachineSnapshot{
				Spec: snapshotv1.VirtualMachineSnapshotSpec{
					Source: corev1.TypedLocalObjectReference{
						APIGroup: &apiGroup,
						Kind:     "VirtualMachine",
						Name:     vmName,
					},
				},
			}

			snapshot := &snapshotv1.VirtualMachineSnapshot{
				ObjectMeta: metav1.ObjectMeta{
					Finalizers: []string{"finalizer"},
				},
				Spec: snapshotv1.VirtualMachineSnapshotSpec{
					Source: corev1.TypedLocalObjectReference{
						APIGroup: &apiGroup,
						Kind:     "VirtualMachine",
						Name:     vmName,
					},
				},
			}

			ar := createSnapshotUpdateAdmissionReview(oldSnapshot, snapshot)
			resp := createTestVMSnapshotAdmitter(config, nil).Admit(ar)
			Expect(resp.Allowed).To(BeTrue())
		})

		Context("when VirtualMachine exists", func() {
			var vm *v1.VirtualMachine

			BeforeEach(func() {
				vm = &v1.VirtualMachine{
					ObjectMeta: metav1.ObjectMeta{
						Name: vmName,
					},
				}
			})

			It("should accept when VM is running", func() {
				snapshot := &snapshotv1.VirtualMachineSnapshot{
					Spec: snapshotv1.VirtualMachineSnapshotSpec{
						Source: corev1.TypedLocalObjectReference{
							APIGroup: &apiGroup,
							Kind:     "VirtualMachine",
							Name:     vmName,
						},
					},
				}

				t := true
				vm.Spec.Running = &t

				ar := createSnapshotAdmissionReview(snapshot)
				resp := createTestVMSnapshotAdmitter(config, vm).Admit(ar)
				Expect(resp.Allowed).To(BeTrue())
			})

			It("should reject invalid kind", func() {
				snapshot := &snapshotv1.VirtualMachineSnapshot{
					Spec: snapshotv1.VirtualMachineSnapshotSpec{
						Source: corev1.TypedLocalObjectReference{
							APIGroup: &apiGroup,
							Kind:     "VirtualMachineInstance",
							Name:     vmName,
						},
					},
				}

				t := true
				vm.Spec.Running = &t

				ar := createSnapshotAdmissionReview(snapshot)
				resp := createTestVMSnapshotAdmitter(config, vm).Admit(ar)
				Expect(resp.Allowed).To(BeFalse())
				Expect(len(resp.Result.Details.Causes)).To(Equal(1))
				Expect(resp.Result.Details.Causes[0].Field).To(Equal("spec.source.kind"))
			})

			It("should reject invalid apiGroup", func() {
				g := "foo.bar"
				snapshot := &snapshotv1.VirtualMachineSnapshot{
					Spec: snapshotv1.VirtualMachineSnapshotSpec{
						Source: corev1.TypedLocalObjectReference{
							APIGroup: &g,
							Kind:     "VirtualMachine",
							Name:     vmName,
						},
					},
				}

				t := true
				vm.Spec.Running = &t

				ar := createSnapshotAdmissionReview(snapshot)
				resp := createTestVMSnapshotAdmitter(config, vm).Admit(ar)
				Expect(resp.Allowed).To(BeFalse())
				Expect(len(resp.Result.Details.Causes)).To(Equal(1))
				Expect(resp.Result.Details.Causes[0].Field).To(Equal("spec.source.apiGroup"))
			})

			It("should accept when VM is not running", func() {
				snapshot := &snapshotv1.VirtualMachineSnapshot{
					Spec: snapshotv1.VirtualMachineSnapshotSpec{
						Source: corev1.TypedLocalObjectReference{
							APIGroup: &apiGroup,
							Kind:     "VirtualMachine",
							Name:     vmName,
						},
					},
				}

				f := false
				vm.Spec.Running = &f

				ar := createSnapshotAdmissionReview(snapshot)
				resp := createTestVMSnapshotAdmitter(config, vm).Admit(ar)
				Expect(resp.Allowed).To(BeTrue())
			})
		})
	})
})

func createSnapshotAdmissionReview(snapshot *snapshotv1.VirtualMachineSnapshot) *admissionv1.AdmissionReview {
	bytes, _ := json.Marshal(snapshot)

	ar := &admissionv1.AdmissionReview{
		Request: &admissionv1.AdmissionRequest{
			Operation: admissionv1.Create,
			Namespace: "foo",
			Resource: metav1.GroupVersionResource{
				Group:    "snapshot.kubevirt.io",
				Resource: "virtualmachinesnapshots",
			},
			Object: runtime.RawExtension{
				Raw: bytes,
			},
		},
	}

	return ar
}

func createSnapshotUpdateAdmissionReview(old, current *snapshotv1.VirtualMachineSnapshot) *admissionv1.AdmissionReview {
	oldBytes, _ := json.Marshal(old)
	currentBytes, _ := json.Marshal(current)

	ar := &admissionv1.AdmissionReview{
		Request: &admissionv1.AdmissionRequest{
			Operation: admissionv1.Update,
			Namespace: "foo",
			Resource: metav1.GroupVersionResource{
				Group:    "snapshot.kubevirt.io",
				Resource: "virtualmachinesnapshots",
			},
			Object: runtime.RawExtension{
				Raw: currentBytes,
			},
			OldObject: runtime.RawExtension{
				Raw: oldBytes,
			},
		},
	}

	return ar
}

func createTestVMSnapshotAdmitter(config *virtconfig.ClusterConfig, vm *v1.VirtualMachine) *VMSnapshotAdmitter {
	ctrl := gomock.NewController(GinkgoT())
	virtClient := kubecli.NewMockKubevirtClient(ctrl)
	vmInterface := kubecli.NewMockVirtualMachineInterface(ctrl)
	virtClient.EXPECT().VirtualMachine(gomock.Any()).Return(vmInterface).AnyTimes()
	if vm == nil {
		err := errors.NewNotFound(schema.GroupResource{Group: "kubevirt.io", Resource: "virtualmachines"}, "foo")
		vmInterface.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, err)
	} else {
		vmInterface.EXPECT().Get(vm.Name, gomock.Any()).Return(vm, nil)
	}
	return &VMSnapshotAdmitter{Config: config, Client: virtClient}
}
<commit_msg>Expect(len(X)).To(Equal(Y))” to “Expect(X).To(HaveLen(Y))<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2018 Red Hat, Inc.
 *
 */

package admitters

import (
	"encoding/json"

	"github.com/golang/mock/gomock"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	admissionv1 "k8s.io/api/admission/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"

	v1 "kubevirt.io/api/core/v1"
	snapshotv1 "kubevirt.io/api/snapshot/v1alpha1"
	"kubevirt.io/client-go/kubecli"
	"kubevirt.io/kubevirt/pkg/testutils"
	"kubevirt.io/kubevirt/pkg/virt-api/webhooks"
	virtconfig "kubevirt.io/kubevirt/pkg/virt-config"
)

var _ = Describe("Validating VirtualMachineSnapshot Admitter", func() {
	vmName := "vm"
	apiGroup := "kubevirt.io"

	config, _, kvInformer := testutils.NewFakeClusterConfigUsingKVConfig(&v1.KubeVirtConfiguration{})

	Context("Without feature gate enabled", func() {
		It("should reject anything", func() {
			snapshot := &snapshotv1.VirtualMachineSnapshot{
				Spec: snapshotv1.VirtualMachineSnapshotSpec{},
			}

			ar := createSnapshotAdmissionReview(snapshot)
			resp := createTestVMSnapshotAdmitter(config, nil).Admit(ar)
			Expect(resp.Allowed).To(BeFalse())
			Expect(resp.Result.Message).Should(Equal("snapshot feature gate not enabled"))
		})
	})

	Context("With feature gate enabled", func() {
		enableFeatureGate := func(featureGate string) {
			testutils.UpdateFakeKubeVirtClusterConfig(kvInformer, &v1.KubeVirt{
				Spec: v1.KubeVirtSpec{
					Configuration: v1.KubeVirtConfiguration{
						DeveloperConfiguration: &v1.DeveloperConfiguration{
							FeatureGates: []string{featureGate},
						},
					},
				},
			})
		}
		disableFeatureGates := func() {
			testutils.UpdateFakeKubeVirtClusterConfig(kvInformer, &v1.KubeVirt{
				Spec: v1.KubeVirtSpec{
					Configuration: v1.KubeVirtConfiguration{
						DeveloperConfiguration: &v1.DeveloperConfiguration{
							FeatureGates: make([]string, 0),
						},
					},
				},
			})
		}

		BeforeEach(func() {
			enableFeatureGate("Snapshot")
		})

		AfterEach(func() {
			disableFeatureGates()
		})

		It("should reject invalid request resource", func() {
			ar := &admissionv1.AdmissionReview{
				Request: &admissionv1.AdmissionRequest{
					Resource: webhooks.VirtualMachineGroupVersionResource,
				},
			}

			resp := createTestVMSnapshotAdmitter(config, nil).Admit(ar)
			Expect(resp.Allowed).To(BeFalse())
			Expect(resp.Result.Message).Should(ContainSubstring("unexpected resource"))
		})

		It("should reject missing apigroup", func() {
			snapshot := &snapshotv1.VirtualMachineSnapshot{
				Spec: snapshotv1.VirtualMachineSnapshotSpec{},
			}

			ar := createSnapshotAdmissionReview(snapshot)
			resp := createTestVMSnapshotAdmitter(config, nil).Admit(ar)
			Expect(resp.Allowed).To(BeFalse())
			Expect(resp.Result.Details.Causes).To(HaveLen(1))
			Expect(resp.Result.Details.Causes[0].Field).To(Equal("spec.source.apiGroup"))
		})

		It("should reject when VM does not exist", func() {
			snapshot := &snapshotv1.VirtualMachineSnapshot{
				Spec: snapshotv1.VirtualMachineSnapshotSpec{
					Source: corev1.TypedLocalObjectReference{
						APIGroup: &apiGroup,
						Kind:     "VirtualMachine",
						Name:     vmName,
					},
				},
			}

			ar := createSnapshotAdmissionReview(snapshot)
			resp := createTestVMSnapshotAdmitter(config, nil).Admit(ar)
			Expect(resp.Allowed).To(BeFalse())
			Expect(resp.Result.Details.Causes).To(HaveLen(1))
			Expect(resp.Result.Details.Causes[0].Field).To(Equal("spec.source.name"))
		})

		It("should reject spec update", func() {
			snapshot := &snapshotv1.VirtualMachineSnapshot{
				Spec: snapshotv1.VirtualMachineSnapshotSpec{
					Source: corev1.TypedLocalObjectReference{
						APIGroup: &apiGroup,
						Kind:     "VirtualMachine",
						Name:     vmName,
					},
				},
			}

			oldSnapshot := &snapshotv1.VirtualMachineSnapshot{
				Spec: snapshotv1.VirtualMachineSnapshotSpec{
					Source: corev1.TypedLocalObjectReference{
						APIGroup: &apiGroup,
						Kind:     "VirtualMachine",
						Name:     "baz",
					},
				},
			}

			ar := createSnapshotUpdateAdmissionReview(oldSnapshot, snapshot)
			resp := createTestVMSnapshotAdmitter(config, nil).Admit(ar)
			Expect(resp.Allowed).To(BeFalse())
			Expect(resp.Result.Details.Causes).To(HaveLen(1))
			Expect(resp.Result.Details.Causes[0].Field).To(Equal("spec"))
		})

		It("should allow metadata update", func() {
			oldSnapshot := &snapshotv1.VirtualMachineSnapshot{
				Spec: snapshotv1.VirtualMachineSnapshotSpec{
					Source: corev1.TypedLocalObjectReference{
						APIGroup: &apiGroup,
						Kind:     "VirtualMachine",
						Name:     vmName,
					},
				},
			}

			snapshot := &snapshotv1.VirtualMachineSnapshot{
				ObjectMeta: metav1.ObjectMeta{
					Finalizers: []string{"finalizer"},
				},
				Spec: snapshotv1.VirtualMachineSnapshotSpec{
					Source: corev1.TypedLocalObjectReference{
						APIGroup: &apiGroup,
						Kind:     "VirtualMachine",
						Name:     vmName,
					},
				},
			}

			ar := createSnapshotUpdateAdmissionReview(oldSnapshot, snapshot)
			resp := createTestVMSnapshotAdmitter(config, nil).Admit(ar)
			Expect(resp.Allowed).To(BeTrue())
		})

		Context("when VirtualMachine exists", func() {
			var vm *v1.VirtualMachine

			BeforeEach(func() {
				vm = &v1.VirtualMachine{
					ObjectMeta: metav1.ObjectMeta{
						Name: vmName,
					},
				}
			})

			It("should accept when VM is running", func() {
				snapshot := &snapshotv1.VirtualMachineSnapshot{
					Spec: snapshotv1.VirtualMachineSnapshotSpec{
						Source: corev1.TypedLocalObjectReference{
							APIGroup: &apiGroup,
							Kind:     "VirtualMachine",
							Name:     vmName,
						},
					},
				}

				t := true
				vm.Spec.Running = &t

				ar := createSnapshotAdmissionReview(snapshot)
				resp := createTestVMSnapshotAdmitter(config, vm).Admit(ar)
				Expect(resp.Allowed).To(BeTrue())
			})

			It("should reject invalid kind", func() {
				snapshot := &snapshotv1.VirtualMachineSnapshot{
					Spec: snapshotv1.VirtualMachineSnapshotSpec{
						Source: corev1.TypedLocalObjectReference{
							APIGroup: &apiGroup,
							Kind:     "VirtualMachineInstance",
							Name:     vmName,
						},
					},
				}

				t := true
				vm.Spec.Running = &t

				ar := createSnapshotAdmissionReview(snapshot)
				resp := createTestVMSnapshotAdmitter(config, vm).Admit(ar)
				Expect(resp.Allowed).To(BeFalse())
				Expect(resp.Result.Details.Causes).To(HaveLen(1))
				Expect(resp.Result.Details.Causes[0].Field).To(Equal("spec.source.kind"))
			})

			It("should reject invalid apiGroup", func() {
				g := "foo.bar"
				snapshot := &snapshotv1.VirtualMachineSnapshot{
					Spec: snapshotv1.VirtualMachineSnapshotSpec{
						Source: corev1.TypedLocalObjectReference{
							APIGroup: &g,
							Kind:     "VirtualMachine",
							Name:     vmName,
						},
					},
				}

				t := true
				vm.Spec.Running = &t

				ar := createSnapshotAdmissionReview(snapshot)
				resp := createTestVMSnapshotAdmitter(config, vm).Admit(ar)
				Expect(resp.Allowed).To(BeFalse())
				Expect(resp.Result.Details.Causes).To(HaveLen(1))
				Expect(resp.Result.Details.Causes[0].Field).To(Equal("spec.source.apiGroup"))
			})

			It("should accept when VM is not running", func() {
				snapshot := &snapshotv1.VirtualMachineSnapshot{
					Spec: snapshotv1.VirtualMachineSnapshotSpec{
						Source: corev1.TypedLocalObjectReference{
							APIGroup: &apiGroup,
							Kind:     "VirtualMachine",
							Name:     vmName,
						},
					},
				}

				f := false
				vm.Spec.Running = &f

				ar := createSnapshotAdmissionReview(snapshot)
				resp := createTestVMSnapshotAdmitter(config, vm).Admit(ar)
				Expect(resp.Allowed).To(BeTrue())
			})
		})
	})
})

func createSnapshotAdmissionReview(snapshot *snapshotv1.VirtualMachineSnapshot) *admissionv1.AdmissionReview {
	bytes, _ := json.Marshal(snapshot)

	ar := &admissionv1.AdmissionReview{
		Request: &admissionv1.AdmissionRequest{
			Operation: admissionv1.Create,
			Namespace: "foo",
			Resource: metav1.GroupVersionResource{
				Group:    "snapshot.kubevirt.io",
				Resource: "virtualmachinesnapshots",
			},
			Object: runtime.RawExtension{
				Raw: bytes,
			},
		},
	}

	return ar
}

func createSnapshotUpdateAdmissionReview(old, current *snapshotv1.VirtualMachineSnapshot) *admissionv1.AdmissionReview {
	oldBytes, _ := json.Marshal(old)
	currentBytes, _ := json.Marshal(current)

	ar := &admissionv1.AdmissionReview{
		Request: &admissionv1.AdmissionRequest{
			Operation: admissionv1.Update,
			Namespace: "foo",
			Resource: metav1.GroupVersionResource{
				Group:    "snapshot.kubevirt.io",
				Resource: "virtualmachinesnapshots",
			},
			Object: runtime.RawExtension{
				Raw: currentBytes,
			},
			OldObject: runtime.RawExtension{
				Raw: oldBytes,
			},
		},
	}

	return ar
}

func createTestVMSnapshotAdmitter(config *virtconfig.ClusterConfig, vm *v1.VirtualMachine) *VMSnapshotAdmitter {
	ctrl := gomock.NewController(GinkgoT())
	virtClient := kubecli.NewMockKubevirtClient(ctrl)
	vmInterface := kubecli.NewMockVirtualMachineInterface(ctrl)
	virtClient.EXPECT().VirtualMachine(gomock.Any()).Return(vmInterface).AnyTimes()
	if vm == nil {
		err := errors.NewNotFound(schema.GroupResource{Group: "kubevirt.io", Resource: "virtualmachines"}, "foo")
		vmInterface.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, err)
	} else {
		vmInterface.EXPECT().Get(vm.Name, gomock.Any()).Return(vm, nil)
	}
	return &VMSnapshotAdmitter{Config: config, Client: virtClient}
}
<|endoftext|>


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

package testutil

import (
	"bytes"
	"fmt"
	"path/filepath"
	"regexp"
	"time"

	"v.io/x/devtools/internal/collect"
	"v.io/x/devtools/internal/tool"
	"v.io/x/devtools/internal/util"
	"v.io/x/devtools/internal/xunit"
)

// generateXUnitTestSuite generates an xUnit test suite that
// encapsulates the given input.
func generateXUnitTestSuite(ctx *tool.Context, success bool, pkg string, duration time.Duration, output string) *xunit.TestSuite {
	// Generate an xUnit test suite describing the result.
	s := xunit.TestSuite{Name: pkg}
	c := xunit.TestCase{
		Classname: pkg,
		Name:      "Test",
		Time:      fmt.Sprintf("%.2f", duration.Seconds()),
	}
	if !success {
		fmt.Fprintf(ctx.Stdout(), "%s ... failed\n%v\n", pkg, output)
		f := xunit.Failure{
			Message: "vrpc",
			Data:    output,
		}
		c.Failures = append(c.Failures, f)
		s.Failures++
	} else {
		fmt.Fprintf(ctx.Stdout(), "%s ... ok\n", pkg)
	}
	s.Tests++
	s.Cases = append(s.Cases, c)
	return &s
}

// testProdService test the given production service.
func testProdService(ctx *tool.Context, service prodService) (*xunit.TestSuite, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	bin := filepath.Join(root, "release", "go", "bin", "vrpc")
	var out bytes.Buffer
	opts := ctx.Run().Opts()
	opts.Stdout = &out
	opts.Stderr = &out
	start := time.Now()
	if err := ctx.Run().TimedCommandWithOpts(DefaultTestTimeout, opts, bin, "signature", service.objectName); err != nil {
		return generateXUnitTestSuite(ctx, false, service.name, time.Now().Sub(start), out.String()), nil
	}
	if !service.regexp.Match(out.Bytes()) {
		fmt.Fprintf(ctx.Stderr(), "couldn't match regexp `%s` in output:\n%v\n", service.regexp, out.String())
		return generateXUnitTestSuite(ctx, false, service.name, time.Now().Sub(start), "mismatching signature"), nil
	}
	return generateXUnitTestSuite(ctx, true, service.name, time.Now().Sub(start), ""), nil
}

type prodService struct {
	name       string
	objectName string
	regexp     *regexp.Regexp
}

// vanadiumProdServicesTest runs a test of vanadium production services.
func vanadiumProdServicesTest(ctx *tool.Context, testName string, opts ...TestOpt) (_ *TestResult, e error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Install the vrpc tool.
	if err := ctx.Run().Command("v23", "go", "install", "v.io/x/ref/cmd/vrpc"); err != nil {
		return nil, internalTestError{err, "Install VRPC"}
	}

	// Describe the test cases.
	blessingRoot, namespaceRoot := getServiceOpts(opts)
	allPassed, suites := true, []xunit.TestSuite{}
	services := []prodService{
		prodService{
			name:       "mounttable",
			objectName: namespaceRoot,
			regexp:     regexp.MustCompile(`MountTable[[:space:]]+interface`),
		},
		prodService{
			name:       "application repository",
			objectName: namespaceRoot + "/applications",
			regexp:     regexp.MustCompile(`Application[[:space:]]+interface`),
		},
		prodService{
			name:       "binary repository",
			objectName: namespaceRoot + "/binaries",
			regexp:     regexp.MustCompile(`Binary[[:space:]]+interface`),
		},
		prodService{
			name:       "macaroon service",
			objectName: namespaceRoot + "/identity/" + blessingRoot + "/root/macaroon",
			regexp:     regexp.MustCompile(`MacaroonBlesser[[:space:]]+interface`),
		},
		prodService{
			name:       "google identity service",
			objectName: namespaceRoot + "/identity/" + blessingRoot + "/root/google",
			regexp:     regexp.MustCompile(`OAuthBlesser[[:space:]]+interface`),
		},
		prodService{
			objectName: namespaceRoot + "/identity/" + blessingRoot + "/root/discharger",
			name:       "binary discharger",
			regexp:     regexp.MustCompile(`Discharger[[:space:]]+interface`),
		},
	}

	for _, service := range services {
		suite, err := testProdService(ctx, service)
		if err != nil {
			return nil, err
		}
		allPassed = allPassed && (suite.Failures == 0)
		suites = append(suites, *suite)
	}

	// Create the xUnit report.
	if err := xunit.CreateReport(ctx, testName, suites); err != nil {
		return nil, err
	}
	if !allPassed {
		return &TestResult{Status: TestFailed}, nil
	}
	return &TestResult{Status: TestPassed}, nil
}

// getServiceOpts extracts blessing root and namespace root from the given
// TestOpts.
func getServiceOpts(opts []TestOpt) (string, string) {
	blessingRoot := "dev.v.io"
	namespaceRoot := "/ns.dev.v.io:8101"
	for _, opt := range opts {
		switch v := opt.(type) {
		case BlessingsRootOpt:
			blessingRoot = string(v)
		case NamespaceRootOpt:
			namespaceRoot = string(v)
		}
	}
	return blessingRoot, namespaceRoot
}
<commit_msg>prod-services-test: Test the HTTP service and then some.<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testutil

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"path/filepath"
	"regexp"
	"time"

	"v.io/x/devtools/internal/collect"
	"v.io/x/devtools/internal/tool"
	"v.io/x/devtools/internal/util"
	"v.io/x/devtools/internal/xunit"
)

// generateXUnitTestSuite generates an xUnit test suite that
// encapsulates the given input.
func generateXUnitTestSuite(ctx *tool.Context, failure *xunit.Failure, pkg string, duration time.Duration) *xunit.TestSuite {
	// Generate an xUnit test suite describing the result.
	s := xunit.TestSuite{Name: pkg}
	c := xunit.TestCase{
		Classname: pkg,
		Name:      "Test",
		Time:      fmt.Sprintf("%.2f", duration.Seconds()),
	}
	if failure != nil {
		fmt.Fprintf(ctx.Stdout(), "%s ... failed\n%v\n", pkg, failure.Data)
		c.Failures = append(c.Failures, *failure)
		s.Failures++
	} else {
		fmt.Fprintf(ctx.Stdout(), "%s ... ok\n", pkg)
	}
	s.Tests++
	s.Cases = append(s.Cases, c)
	return &s
}

// testSingleProdService test the given production service.
func testSingleProdService(ctx *tool.Context, vroot, principalDir string, service prodService) *xunit.TestSuite {
	bin := filepath.Join(vroot, "release", "go", "bin", "vrpc")
	var out bytes.Buffer
	opts := ctx.Run().Opts()
	opts.Stdout = &out
	opts.Stderr = &out
	start := time.Now()
	if err := ctx.Run().TimedCommandWithOpts(DefaultTestTimeout, opts, bin, "--veyron.credentials", principalDir, "signature", service.objectName); err != nil {
		return generateXUnitTestSuite(ctx, &xunit.Failure{"vrpc", out.String()}, service.name, time.Now().Sub(start))
	}
	if !service.regexp.Match(out.Bytes()) {
		fmt.Fprintf(ctx.Stderr(), "couldn't match regexp %q in output:\n%v\n", service.regexp, out.String())
		return generateXUnitTestSuite(ctx, &xunit.Failure{"vrpc", "mismatching signature"}, service.name, time.Now().Sub(start))
	}
	return generateXUnitTestSuite(ctx, nil, service.name, time.Now().Sub(start))
}

type prodService struct {
	name       string         // Name to use for the test description
	objectName string         // Object name of the service to connect to
	regexp     *regexp.Regexp // Regexp that should match the signature output
}

// vanadiumProdServicesTest runs a test of vanadium production services.
func vanadiumProdServicesTest(ctx *tool.Context, testName string, opts ...TestOpt) (_ *TestResult, e error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	vroot, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}

	// Install the vrpc tool.
	if err := ctx.Run().Command("v23", "go", "install", "v.io/x/ref/cmd/vrpc"); err != nil {
		return nil, internalTestError{err, "Install VRPC"}
	}
	// Install the principal tool.
	if err := ctx.Run().Command("v23", "go", "install", "v.io/x/ref/cmd/principal"); err != nil {
		return nil, internalTestError{err, "Install Principal"}
	}
	tmpdir, err := ctx.Run().TempDir("", "prod-services-test")
	if err != nil {
		return nil, internalTestError{err, "Create temporary directory"}
	}
	defer collect.Error(func() error { return ctx.Run().RemoveAll(tmpdir) }, &e)

	blessingRoot, namespaceRoot := getServiceOpts(opts)
	allPassed, suites := true, []xunit.TestSuite{}

	// Fetch the "root" blessing that all services are blessed by.
	suite, pubkey, blessingNames := testIdentityProviderHTTP(ctx, blessingRoot)
	suites = append(suites, *suite)

	if suite.Failures == 0 {
		// Setup a principal that will be used by testAllProdServices and will
		// recognize the blessings of the prod services.
		principalDir, err := setupPrincipal(ctx, vroot, tmpdir, pubkey, blessingNames)
		if err != nil {
			return nil, err
		}
		for _, suite := range testAllProdServices(ctx, vroot, principalDir, blessingRoot, namespaceRoot) {
			allPassed = allPassed && (suite.Failures == 0)
			suites = append(suites, *suite)
		}
	}

	// Create the xUnit report.
	if err := xunit.CreateReport(ctx, testName, suites); err != nil {
		return nil, err
	}
	for _, suite := range suites {
		if suite.Failures > 0 {
			// At least one test failed:
			return &TestResult{Status: TestFailed}, nil
		}
	}
	return &TestResult{Status: TestPassed}, nil
}

func testAllProdServices(ctx *tool.Context, vroot, principalDir, blessingRoot, namespaceRoot string) []*xunit.TestSuite {
	services := []prodService{
		prodService{
			name:       "mounttable",
			objectName: namespaceRoot,
			regexp:     regexp.MustCompile(`MountTable[[:space:]]+interface`),
		},
		prodService{
			name:       "application repository",
			objectName: namespaceRoot + "/applications",
			regexp:     regexp.MustCompile(`Application[[:space:]]+interface`),
		},
		prodService{
			name:       "binary repository",
			objectName: namespaceRoot + "/binaries",
			regexp:     regexp.MustCompile(`Binary[[:space:]]+interface`),
		},
		prodService{
			name:       "macaroon service",
			objectName: namespaceRoot + "/identity/" + blessingRoot + "/root/macaroon",
			regexp:     regexp.MustCompile(`MacaroonBlesser[[:space:]]+interface`),
		},
		prodService{
			name:       "google identity service",
			objectName: namespaceRoot + "/identity/" + blessingRoot + "/root/google",
			regexp:     regexp.MustCompile(`OAuthBlesser[[:space:]]+interface`),
		},
		prodService{
			objectName: namespaceRoot + "/identity/" + blessingRoot + "/root/discharger",
			name:       "binary discharger",
			regexp:     regexp.MustCompile(`Discharger[[:space:]]+interface`),
		},
	}

	var suites []*xunit.TestSuite
	for _, service := range services {
		suites = append(suites, testSingleProdService(ctx, vroot, principalDir, service))
	}
	return suites
}

// testIdentityProviderHTTP tests that the identity provider's HTTP server is
// up and running and also fetches the set of blessing names that the provider
// claims to be authoritative on and the public key (encoded) used by that
// identity provider to sign certificates for blessings.
//
// PARANOIA ALERT:
// This function is subject to man-in-the-middle attacks because it does not
// verify the TLS certificates presented by the server. This does open the
// door for an attack where a parallel universe of services could be setup
// and fool this production services test into thinking all services are
// up and running when they may not be.
//
// The attacker in this case will have to be able to mess with the routing
// tables on the machine running this test, or the network routes of routers
// used by the machine, or mess up DNS entries.
func testIdentityProviderHTTP(ctx *tool.Context, blessingRoot string) (suite *xunit.TestSuite, publickey string, blessingNames []string) {
	url := fmt.Sprintf("https://%s/auth/blessing-root", blessingRoot)
	start := time.Now()
	var response struct {
		Names     []string `json:"names"`
		PublicKey string   `json:"publicKey"`
	}
	resp, err := http.Get(url)
	if err == nil {
		defer resp.Body.Close()
		err = json.NewDecoder(resp.Body).Decode(&response)
	}
	var failure *xunit.Failure
	if err != nil {
		failure = &xunit.Failure{"identityd HTTP", err.Error()}
	}
	return generateXUnitTestSuite(ctx, failure, url, time.Now().Sub(start)), response.PublicKey, response.Names
}

func setupPrincipal(ctx *tool.Context, vroot, tmpdir, pubkey string, blessingNames []string) (string, error) {
	dir := filepath.Join(tmpdir, "credentials")
	bin := filepath.Join(vroot, "release", "go", "bin", "principal")
	if err := ctx.Run().TimedCommand(DefaultTestTimeout, bin, "create", dir, "prod-services-tester"); err != nil {
		fmt.Fprintf(ctx.Stderr(), "principal create failed: %v\n", err)
		return "", err
	}
	for _, name := range blessingNames {
		if err := ctx.Run().TimedCommand(DefaultTestTimeout, bin, "--veyron.credentials", dir, "addtoroots", pubkey, name); err != nil {
			fmt.Fprintf(ctx.Stderr(), "principal addtoroots %v %v failed: %v\n", pubkey, name, err)
			return "", err
		}
	}
	return dir, nil
}

// getServiceOpts extracts blessing root and namespace root from the given
// TestOpts.
func getServiceOpts(opts []TestOpt) (string, string) {
	blessingRoot := "dev.v.io"
	namespaceRoot := "/ns.dev.v.io:8101"
	for _, opt := range opts {
		switch v := opt.(type) {
		case BlessingsRootOpt:
			blessingRoot = string(v)
		case NamespaceRootOpt:
			namespaceRoot = string(v)
		}
	}
	return blessingRoot, namespaceRoot
}
<|endoftext|>


<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 verify

import (
	"fmt"

	"github.com/jacobsa/comeback/internal/blob"
	"github.com/jacobsa/syncutil"
	"github.com/jacobsa/timeutil"
	"golang.org/x/net/context"
)

// Read all blobs necessary for verifying the directory structure rooted at a
// set of backup root scores, ensuring that the entire directory structure is
// intact in GCS.
//
// Optionally, all file content is also read and verified. This is less
// important than verifying directory connectedness if we trust that GCS does
// not corrupt object metadata (where we store expected CRC32C and MD5) and
// does correctly report the object's CRC32C and MD5 sums in listings,
// verifying them periodically.
//
// If work is to be preserved across runs, knownStructure should be filled in
// with parenthood information from previously-generated records (for both
// files and directories). Nodes that exist as keys in this map will not be
// re-verified, except to confirm that their content still exists in allScores.
//
// It is expected that the blob store's Load method does score verification for
// us.
func Verify(
	ctx context.Context,
	readFiles bool,
	rootScores []blob.Score,
	allScores []blob.Score,
	knownStructure map[Node][]Node,
	records chan<- Record,
	bs blob.Store) (err error) {
	b := syncutil.NewBundle(ctx)

	// Explore the graph starting at the specified roots. Use an "experimentally
	// determined" parallelism, which in theory should depend on bandwidth-delay
	// products but in practice comes down to when the OS gets cranky about open
	// files.
	graphNodes := make(chan graph.Node, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(graphNodes)
		const parallelism = 128

		sf := newSuccessorFinder(
			readFiles,
			allScores,
			knownStructure,
			records,
			timeutil.RealClock(),
			bs)

		var graphRoots []graph.Node
		for _, s := range rootScores {
			n := Node{
				Score: s,
				Dir:   true,
			}

			graphRoots = append(graphRoots, n)
		}

		err = graph.ExploreDirectedGraph(
			ctx,
			sf,
			graphRoots,
			graphNodes,
			parallelism)

		if err != nil {
			err = fmt.Errorf("ExploreDirectedGraph: %v", err)
			return
		}

		return
	})

	// Throw away the graph nodes returned by ExploreDirectedGraph. We don't need
	// them; the successor finder mints records and writes them to the channel
	// for us.
	b.Add(func(ctx context.Context) (err error) {
		for _ = range graphNodes {
		}

		return
	})

	err = b.Join()
	return
}
<commit_msg>Fixed verify.go.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 verify

import (
	"fmt"

	"github.com/jacobsa/comeback/internal/blob"
	"github.com/jacobsa/comeback/internal/dag"
	"github.com/jacobsa/timeutil"
	"golang.org/x/net/context"
)

// Read all blobs necessary for verifying the directory structure rooted at a
// set of backup root scores, ensuring that the entire directory structure is
// intact in GCS.
//
// Optionally, all file content is also read and verified. This is less
// important than verifying directory connectedness if we trust that GCS does
// not corrupt object metadata (where we store expected CRC32C and MD5) and
// does correctly report the object's CRC32C and MD5 sums in listings,
// verifying them periodically.
//
// If work is to be preserved across runs, knownStructure should be filled in
// with parenthood information from previously-generated records (for both
// files and directories). Nodes that exist as keys in this map will not be
// re-verified, except to confirm that their content still exists in allScores.
//
// It is expected that the blob store's Load method does score verification for
// us.
func Verify(
	ctx context.Context,
	readFiles bool,
	rootScores []blob.Score,
	allScores []blob.Score,
	knownStructure map[Node][]Node,
	records chan<- Record,
	blobStore blob.Store) (err error) {
	clock := timeutil.RealClock()

	// Set up a dependency resolver that reads directory listings. It also takes
	// care of confirming that all scores (for files and directories) exist.
	dr := newDependencyResolver(
		allScores,
		knownStructure,
		records,
		blobStore,
		clock)

	// Do we need to do anything for file nodes?
	var visitor dag.Visitor
	if readFiles {
		visitor = newVisitor(records, blobStore, clock)
	} else {
		visitor = &doNothingVisitor{}
	}

	// Traverse the graph.
	var rootNodes []dag.Node
	for _, s := range rootScores {
		n := Node{
			Score: s,
			Dir:   true,
		}

		rootNodes = append(rootNodes, n)
	}

	const parallelism = 128
	err = dag.Visit(ctx, rootNodes, dr, visitor, parallelism)
	if err != nil {
		err = fmt.Errorf("dag.Visit: %v", err)
		return
	}

	return
}
<|endoftext|>


<commit_before>package cli

import "os"
import "fmt"
import "bytes"
import "strings"

// CLI represents a set of defined flags.  The zero value of a FlagSet
// has no name and has ContinueOnError error handling.
type CLI struct {
	flagable
	paramable
	subCommandable
	*writer

	fn          commandFn
	name        string
	description string
	parsed      bool
}

// NewCLI returns a new cli with the specified name and
// error handling property.
func NewCLI(version, desc string, fn commandFn, paramNames ...string) *CLI {
	nameParts := strings.Split(os.Args[0], "/")
	cli := new(CLI)
	cli.init(nameParts[len(nameParts)-1], desc, fn, paramNames...)
	cli.version = version
	cli.description = desc
	return cli
}

// DefineSubCommand return a SubCommand and adds the current CLI as the parent
func (cmd *CLI) DefineSubCommand(name string, desc string, fn commandFn, paramNames ...string) *SubCommand {
	subcmd := cmd.subCommandable.DefineSubCommand(name, desc, fn, paramNames...)
	cmd.parent = subcmd
	return subcmd
}

// Description returns the command description
func (cmd *CLI) Description() string {
	return cmd.description
}

// Name returns the command name
func (cmd *CLI) Name() string {
	var name string
	if cmd.parent != nil {
		name = strings.Join([]string{cmd.parent.Name(), cmd.name}, " ")
	} else {
		name = cmd.name
	}
	return name
}

// Parsed reports whether f.Parse has been called.
func (cmd *CLI) Parsed() bool {
	cmd.parsed = cmd.flagable.Parsed() && cmd.paramable.Parsed() && cmd.subCommandable.Parsed()
	return cmd.parsed
}

// Start starts the command with args, arg[0] is ignored
func (cmd *CLI) Start(args ...string) {
	if args == nil {
		args = os.Args
	}

	if len(args) > 1 {
		args = args[1:]
	} else {
		args = []string{}
	}

	// parse flags and args
	args = cmd.flagable.parse(args)

	// Show a version
	if len(cmd.Version()) > 0 && cmd.Flag("version").Get() == true {
		fmt.Println(cmd.Name(), cmd.Version())
		return
	}

	// Show Help
	if cmd.Flag("help").Get() == true {
		cmd.Usage()
		return
	}

	// Parse Params
	args = cmd.paramable.parse(args)

	if cmd.parseSubCommands(args) {
		return
	}

	// Run the function
	cmd.fn(cmd)
}

// UsageString returns the command usage as a string
func (cmd *CLI) UsageString() string {
	hasSubCommands := len(cmd.subCommands) > 0
	hasParams := len(cmd.params) > 0
	hasDescription := len(cmd.description) > 0

	// Start the Buffer
	var buff bytes.Buffer

	buff.WriteString("Usage:\n")
	buff.WriteString(fmt.Sprintf("  %s [options...]", cmd.Name()))

	// Write Param Syntax
	if hasParams {
		buff.WriteString(fmt.Sprintf(" %s", cmd.paramable.UsageString()))
	}

	// Write Sub Command Syntax
	if hasSubCommands {
		buff.WriteString(" <command> [arg...]")
	}

	if hasDescription {
		buff.WriteString(fmt.Sprintf("\n\n%s", cmd.Description()))
	}

	// Write Flags Syntax
	buff.WriteString("\n\nOptions:\n")
	buff.WriteString(cmd.flagable.UsageString())

	// Write Sub Command List
	if hasSubCommands {
		buff.WriteString("\n\nCommands:\n")
		buff.WriteString(cmd.subCommandable.UsageString())
	}

	// Return buffer as string
	return buff.String()
}

func (cmd *CLI) init(name, desc string, fn commandFn, paramNames ...string) {
	writer := &writer{ErrorHandling: ExitOnError}
	cmd.writer = writer
	cmd.flagable = flagable{writer: writer}
	cmd.paramable = paramable{writer: writer}
	cmd.subCommandable = subCommandable{writer: writer}
	cmd.name = name
	cmd.fn = fn
	cmd.description = desc
	cmd.setParams(paramNames...)
	cmd.usage = func() { fmt.Println(cmd.UsageString()) }
}
<commit_msg>fix subcmd bug<commit_after>package cli

import "os"
import "fmt"
import "bytes"
import "strings"

// CLI represents a set of defined flags.  The zero value of a FlagSet
// has no name and has ContinueOnError error handling.
type CLI struct {
	flagable
	paramable
	subCommandable
	*writer

	fn          commandFn
	name        string
	description string
	parsed      bool
}

// NewCLI returns a new cli with the specified name and
// error handling property.
func NewCLI(version, desc string, fn commandFn, paramNames ...string) *CLI {
	nameParts := strings.Split(os.Args[0], "/")
	cli := new(CLI)
	cli.init(nameParts[len(nameParts)-1], desc, fn, paramNames...)
	cli.version = version
	cli.description = desc
	return cli
}

// DefineSubCommand return a SubCommand and adds the current CLI as the parent
func (cmd *CLI) DefineSubCommand(name string, desc string, fn commandFn, paramNames ...string) *SubCommand {
	subcmd := cmd.subCommandable.DefineSubCommand(name, desc, fn, paramNames...)
	subcmd.parent = cmd
	return subcmd
}

// Description returns the command description
func (cmd *CLI) Description() string {
	return cmd.description
}

// Name returns the command name
func (cmd *CLI) Name() string {
	var name string
	if cmd.parent != nil {
		name = strings.Join([]string{cmd.parent.Name(), cmd.name}, " ")
	} else {
		name = cmd.name
	}
	return name
}

// Parsed reports whether f.Parse has been called.
func (cmd *CLI) Parsed() bool {
	cmd.parsed = cmd.flagable.Parsed() && cmd.paramable.Parsed() && cmd.subCommandable.Parsed()
	return cmd.parsed
}

// Start starts the command with args, arg[0] is ignored
func (cmd *CLI) Start(args ...string) {
	if args == nil {
		args = os.Args
	}

	if len(args) > 1 {
		args = args[1:]
	} else {
		args = []string{}
	}

	// parse flags and args
	args = cmd.flagable.parse(args)

	// Show a version
	if len(cmd.Version()) > 0 && cmd.Flag("version").Get() == true {
		fmt.Println(cmd.Name(), cmd.Version())
		return
	}

	// Show Help
	if cmd.Flag("help").Get() == true {
		cmd.Usage()
		return
	}

	// Parse Params
	args = cmd.paramable.parse(args)

	if cmd.parseSubCommands(args) {
		return
	}

	// Run the function
	cmd.fn(cmd)
}

// UsageString returns the command usage as a string
func (cmd *CLI) UsageString() string {
	hasSubCommands := len(cmd.subCommands) > 0
	hasParams := len(cmd.params) > 0
	hasDescription := len(cmd.description) > 0

	// Start the Buffer
	var buff bytes.Buffer

	buff.WriteString("Usage:\n")
	buff.WriteString(fmt.Sprintf("  %s [options...]", cmd.Name()))

	// Write Param Syntax
	if hasParams {
		buff.WriteString(fmt.Sprintf(" %s", cmd.paramable.UsageString()))
	}

	// Write Sub Command Syntax
	if hasSubCommands {
		buff.WriteString(" <command> [arg...]")
	}

	if hasDescription {
		buff.WriteString(fmt.Sprintf("\n\n%s", cmd.Description()))
	}

	// Write Flags Syntax
	buff.WriteString("\n\nOptions:\n")
	buff.WriteString(cmd.flagable.UsageString())

	// Write Sub Command List
	if hasSubCommands {
		buff.WriteString("\n\nCommands:\n")
		buff.WriteString(cmd.subCommandable.UsageString())
	}

	// Return buffer as string
	return buff.String()
}

func (cmd *CLI) init(name, desc string, fn commandFn, paramNames ...string) {
	writer := &writer{ErrorHandling: ExitOnError}
	cmd.writer = writer
	cmd.flagable = flagable{writer: writer}
	cmd.paramable = paramable{writer: writer}
	cmd.subCommandable = subCommandable{writer: writer}
	cmd.name = name
	cmd.fn = fn
	cmd.description = desc
	cmd.setParams(paramNames...)
	cmd.usage = func() { fmt.Println(cmd.UsageString()) }
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. 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 flushfs

import (
	"os"
	"sync"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseutil"
	"golang.org/x/net/context"
)

// Create a file system containing a single file named "foo".
//
// The file may be opened for reading and/or writing. Its initial contents are
// empty. Whenever a flush or fsync is received, the supplied function will be
// called with the current contents of the file.
func NewFileSystem(
	reportFlush func(string),
	reportFsync func(string)) (fs fuse.FileSystem, err error) {
	fs = &flushFS{}
	return
}

const fooID = fuse.RootInodeID + 1

type flushFS struct {
	fuseutil.NotImplementedFileSystem

	mu  sync.Mutex
	foo *os.File // GUARDED_BY(mu)
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// LOCKS_REQUIRED(fs.mu)
func (fs *flushFS) rootAttributes() fuse.InodeAttributes {
	return fuse.InodeAttributes{
		Nlink: 1,
		Mode:  0777 | os.ModeDir,
	}
}

// LOCKS_REQUIRED(fs.mu)
func (fs *flushFS) fooAttributes() fuse.InodeAttributes {
	return fuse.InodeAttributes{
		Nlink: 1,
		Mode:  0777,
	}
}

////////////////////////////////////////////////////////////////////////
// File system methods
////////////////////////////////////////////////////////////////////////

func (fs *flushFS) Init(
	ctx context.Context,
	req *fuse.InitRequest) (
	resp *fuse.InitResponse, err error) {
	resp = &fuse.InitResponse{}
	return
}

func (fs *flushFS) LookUpInode(
	ctx context.Context,
	req *fuse.LookUpInodeRequest) (
	resp *fuse.LookUpInodeResponse, err error) {
	resp = &fuse.LookUpInodeResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Sanity check.
	if req.Parent != fuse.RootInodeID || req.Name != "foo" {
		err = fuse.ENOENT
		return
	}

	resp.Entry = fuse.ChildInodeEntry{
		Child:      fooID,
		Attributes: fs.fooAttributes(),
	}

	return
}

func (fs *flushFS) GetInodeAttributes(
	ctx context.Context,
	req *fuse.GetInodeAttributesRequest) (
	resp *fuse.GetInodeAttributesResponse, err error) {
	resp = &fuse.GetInodeAttributesResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	switch req.Inode {
	case fuse.RootInodeID:
		resp.Attributes = fs.rootAttributes()
		return

	case fooID:
		resp.Attributes = fs.fooAttributes()
		return

	default:
		err = fuse.ENOENT
		return
	}
}
<commit_msg>flushFS.OpenFile.<commit_after>// Copyright 2015 Google Inc. 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 flushfs

import (
	"os"
	"sync"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseutil"
	"golang.org/x/net/context"
)

// Create a file system containing a single file named "foo".
//
// The file may be opened for reading and/or writing. Its initial contents are
// empty. Whenever a flush or fsync is received, the supplied function will be
// called with the current contents of the file.
func NewFileSystem(
	reportFlush func(string),
	reportFsync func(string)) (fs fuse.FileSystem, err error) {
	fs = &flushFS{}
	return
}

const fooID = fuse.RootInodeID + 1

type flushFS struct {
	fuseutil.NotImplementedFileSystem

	mu  sync.Mutex
	foo *os.File // GUARDED_BY(mu)
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// LOCKS_REQUIRED(fs.mu)
func (fs *flushFS) rootAttributes() fuse.InodeAttributes {
	return fuse.InodeAttributes{
		Nlink: 1,
		Mode:  0777 | os.ModeDir,
	}
}

// LOCKS_REQUIRED(fs.mu)
func (fs *flushFS) fooAttributes() fuse.InodeAttributes {
	return fuse.InodeAttributes{
		Nlink: 1,
		Mode:  0777,
	}
}

////////////////////////////////////////////////////////////////////////
// File system methods
////////////////////////////////////////////////////////////////////////

func (fs *flushFS) Init(
	ctx context.Context,
	req *fuse.InitRequest) (
	resp *fuse.InitResponse, err error) {
	resp = &fuse.InitResponse{}
	return
}

func (fs *flushFS) LookUpInode(
	ctx context.Context,
	req *fuse.LookUpInodeRequest) (
	resp *fuse.LookUpInodeResponse, err error) {
	resp = &fuse.LookUpInodeResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Sanity check.
	if req.Parent != fuse.RootInodeID || req.Name != "foo" {
		err = fuse.ENOENT
		return
	}

	resp.Entry = fuse.ChildInodeEntry{
		Child:      fooID,
		Attributes: fs.fooAttributes(),
	}

	return
}

func (fs *flushFS) GetInodeAttributes(
	ctx context.Context,
	req *fuse.GetInodeAttributesRequest) (
	resp *fuse.GetInodeAttributesResponse, err error) {
	resp = &fuse.GetInodeAttributesResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	switch req.Inode {
	case fuse.RootInodeID:
		resp.Attributes = fs.rootAttributes()
		return

	case fooID:
		resp.Attributes = fs.fooAttributes()
		return

	default:
		err = fuse.ENOENT
		return
	}
}

func (fs *flushFS) OpenFile(
	ctx context.Context,
	req *fuse.OpenFileRequest) (
	resp *fuse.OpenFileResponse, err error) {
	resp = &fuse.OpenFileResponse{}

	fs.mu.Lock()
	defer fs.mu.Unlock()

	// Sanity check.
	if req.Inode != fooID {
		err = fuse.ENOSYS
		return
	}

	return
}
<|endoftext|>


<commit_before>package clocky

import (
	"http"
	"io"
	"io/ioutil"
	"os"

	"appengine"
	"appengine/memcache"
	"appengine/urlfetch"
)

// http://www.sfmta.com/cms/asite/nextmunidata.htm
const NextBusURL = ("http://webservices.nextbus.com/service/publicXMLFeed?" +
	"command=predictionsForMultiStops&a=sf-muni" +
	"&stops=1|null|4016" +
	"&stop=1|null|6297" +
	"&stops=10|null|5859" +
	"&stops=12|null|5859" +
	"&stops=27|null|35165" +
	"&stops=47|null|6825" +
	"&stops=49|null|6825")

const WeatherURL = "http://forecast.weather.gov/MapClick.php?lat=37.79570&lon=-122.42100&FcstType=dwml&unit=1"

func fetch(r *http.Request, key, url string, expiration int32) os.Error {
	c := appengine.NewContext(r)

	client := urlfetch.Client(c)
	resp, err := client.Get(url)
	if err != nil {
		return err
	}
	contents, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	resp.Body.Close()

	item := &memcache.Item{
		Key:        key,
		Value:      contents,
		Expiration: expiration,
	}
	if err := memcache.Set(c, item); err != nil {
		return err
	}

	return nil
}

const nextBusKey = "nextbus"
const weatherKey = "weather"

func fetchNextBus(r *http.Request) os.Error {
	return fetch(r, nextBusKey, NextBusURL, 300)
}

func fetchWeather(r *http.Request) os.Error {
	return fetch(r, weatherKey, WeatherURL, 4*3600)
}

func fetchNextBusHandler(w http.ResponseWriter, r *http.Request) {
	if err := fetchNextBus(r); err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, "ok\n")
}

func fetchWeatherHandler(w http.ResponseWriter, r *http.Request) {
	if err := fetchWeather(r); err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, "ok\n")
}

func warmup(w http.ResponseWriter, r *http.Request) {
	if err := fetchNextBus(r); err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	if err := fetchWeather(r); err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, "ok\n")
}

func init() {
	http.HandleFunc("/fetch/nextbus", fetchNextBusHandler)
	http.HandleFunc("/fetch/weather", fetchWeatherHandler)
	http.HandleFunc("/ah/_warmup", warmup)
}
<commit_msg>Fixes.<commit_after>package clocky

import (
	"http"
	"io"
	"io/ioutil"
	"os"

	"appengine"
	"appengine/memcache"
	"appengine/urlfetch"
)

// http://www.sfmta.com/cms/asite/nextmunidata.htm
const NextBusURL = ("http://webservices.nextbus.com/service/publicXMLFeed?" +
	"command=predictionsForMultiStops&a=sf-muni" +
	"&stops=1|null|4016" +
	"&stop=1|null|6297" +
	"&stops=10|null|5859" +
	"&stops=12|null|5859" +
	"&stops=27|null|35165" +
	"&stops=47|null|6825" +
	"&stops=49|null|6825")

const WeatherURL = "http://forecast.weather.gov/MapClick.php?lat=37.79570&lon=-122.42100&FcstType=dwml&unit=1"

func fetch(r *http.Request, key, url string, expiration int32) os.Error {
	c := appengine.NewContext(r)

	client := urlfetch.Client(c)
	resp, err := client.Get(url)
	if err != nil {
		return err
	}
	contents, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	resp.Body.Close()

	item := &memcache.Item{
		Key:        key,
		Value:      contents,
		Expiration: expiration,
	}
	if err := memcache.Set(c, item); err != nil {
		return err
	}

	return nil
}

const nextBusKey = "nextbus"
const weatherKey = "weather"

// Normally called whenever a page refresh notices stale data.
func fetchNextBus(r *http.Request) os.Error {
	return fetch(r, nextBusKey, NextBusURL, 300)
}

// Normally called hourly by cron.
// http://graphical.weather.gov/xml/mdl/XML/Design/WebServicesUseGuildlines.php
func fetchWeather(r *http.Request) os.Error {
	return fetch(r, weatherKey, WeatherURL, 4*3600)
}

func fetchNextBusHandler(w http.ResponseWriter, r *http.Request) {
	if err := fetchNextBus(r); err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, "ok\n")
}

func fetchWeatherHandler(w http.ResponseWriter, r *http.Request) {
	if err := fetchWeather(r); err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, "ok\n")
}

func warmup(w http.ResponseWriter, r *http.Request) {
	ch := make(chan os.Error)
	go func() { ch <- fetchNextBus(r) }()
	go func() { ch <- fetchWeather(r) }()
	for i := 0; i < 2; i++ {
		if err := <-ch; err != nil {
			http.Error(w, err.String(), http.StatusInternalServerError)
			return
		}
	}
	io.WriteString(w, "ok\n")
}

func init() {
	http.HandleFunc("/fetch/nextbus", fetchNextBusHandler)
	http.HandleFunc("/fetch/weather", fetchWeatherHandler)
	http.HandleFunc("/_ah/warmup", warmup)
}
<|endoftext|>


<commit_before>package ws

import (
	"fmt"
	"hash/fnv"
	"os"
	"path/filepath"
	"sort"
	"sync"
)

const (
	FlagDir uint32 = 1 << iota
	FlagLogical
	FlagMount
)

type Id uint32

func NewId(path string) Id {
	h := fnv.New32()
	h.Write([]byte(path))
	return Id(h.Sum32())
}

type Dir struct {
	Path     string
	Children []*Res
}

type Res struct {
	sync.Mutex
	Id     Id
	Name   string
	Flag   uint32
	Parent *Res
	*Dir
}

func (r *Res) Path() string {
	if r == nil {
		return ""
	}
	if r.Dir != nil {
		return r.Dir.Path
	}
	return r.Parent.Path() + string(os.PathSeparator) + r.Name
}
func newChild(pa *Res, fi os.FileInfo) *Res {
	r := &Res{Name: fi.Name(), Parent: pa}
	p := r.Path()
	r.Id = NewId(p)
	if fi.IsDir() {
		r.Flag |= FlagDir
		r.Dir = &Dir{Path: p}
	}
	return r
}

type Watcher interface {
	Watch(r *Res) error
	Close()
}

type Config struct {
	CapHint uint
	Watcher func(*Ws) (Watcher, error)
}

type Ws struct {
	sync.RWMutex
	config  Config
	root    *Res
	all     map[Id]*Res
	watcher Watcher
}

// New creates a workspace configured with c.
func New(c Config) *Ws {
	r := &Res{Id: NewId("/")}
	w := &Ws{config: c, root: r, all: make(map[Id]*Res, c.CapHint)}
	w.all[r.Id] = r
	return w
}

// Mount adds the directory tree at path to the workspace.
func (w *Ws) Mount(path string) (*Res, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return nil, err
	}
	if !fi.IsDir() {
		return nil, fmt.Errorf("not a directory")
	}
	r, err := w.mount(path)
	if err != nil {
		return r, err
	}
	r.Lock()
	err = read(r)
	r.Unlock()
	if err != nil {
		return r, err
	}
	w.Lock()
	defer w.Unlock()
	w.addAllChildren(r)
	return r, nil
}
func (w *Ws) mount(path string) (*Res, error) {
	id := NewId(path)
	d, f := filepath.Split(path)
	w.Lock()
	defer w.Unlock()
	r, ok := w.all[id]
	if ok {
		return r, fmt.Errorf("duplicate")
	}
	r = &Res{Id: id, Name: f, Flag: FlagDir | FlagMount, Dir: &Dir{Path: path}}
	// add virtual parent
	r.Parent = w.addParent(d[:len(d)-1])
	w.all[id] = r
	return r, nil
}

// Close closes the workspace.
// The workspace and all its resources are invalid afterwards.
func (w *Ws) Close() {
	w.Lock()
	defer w.Unlock()
	if w.watcher != nil {
		w.watcher.Close()
		w.watcher = nil
	}
	// scatter garbage
	for id, r := range w.all {
		r.Parent, r.Dir = nil, nil
		delete(w.all, id)
	}
	w.all = nil
	w.root = nil
}
func (w *Ws) addParent(path string) *Res {
	id := NewId(path)
	if r, ok := w.all[id]; ok {
		return r
	}
	parent, name := filepath.Split(path)
	var pa *Res
	if len(parent) > 0 && parent != "/" {
		pa = w.addParent(parent[:len(parent)-1])
	}
	r := &Res{Id: id, Name: name, Flag: FlagDir | FlagLogical, Parent: pa}
	w.all[id] = r
	return r
}
func read(r *Res) error {
	f, err := os.Open(r.Dir.Path)
	if err != nil {
		return err
	}
	list, err := f.Readdir(-1)
	f.Close()
	if err != nil {
		return err
	}
	children := make([]*Res, 0, len(list))
	for _, fi := range list {
		children = append(children, newChild(r, fi))
	}
	sort.Sort(byTypeAndName(children))
	r.Children = children
	for _, c := range children {
		if c.Flag&FlagDir != 0 {
			if err := read(c); err != nil {
				fmt.Println(err)
			}
		}
	}
	return nil
}
func (w *Ws) addAllChildren(r *Res) {
	for _, c := range r.Children {
		w.all[c.Id] = c
		if c.Flag&FlagDir != 0 {
			w.addAllChildren(c)
		}
	}
	if w.watcher != nil {
		err := w.watcher.Watch(r)
		if err != nil {
			fmt.Println(err)
		}
	}
}

type byTypeAndName []*Res

func (l byTypeAndName) Len() int {
	return len(l)
}
func (l byTypeAndName) Less(i, j int) bool {
	if isdir := l[i].Flag&FlagDir != 0; isdir != (l[j].Flag&FlagDir != 0) {
		return isdir
	}
	return l[i].Name < l[j].Name
}
func (l byTypeAndName) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}
<commit_msg>adds handler callback, workspace operations and controller.<commit_after>package ws

import (
	"fmt"
	"hash/fnv"
	"os"
	"path/filepath"
	"sort"
	"sync"
)

const (
	FlagDir uint32 = 1 << iota
	FlagLogical
	FlagMount
)

type Id uint32

func NewId(path string) Id {
	h := fnv.New32()
	h.Write([]byte(path))
	return Id(h.Sum32())
}

type Dir struct {
	Path     string
	Children []*Res
}

type Res struct {
	sync.Mutex
	Id     Id
	Name   string
	Flag   uint32
	Parent *Res
	*Dir
}

func (r *Res) Path() string {
	if r == nil {
		return ""
	}
	if r.Dir != nil {
		return r.Dir.Path
	}
	return r.Parent.Path() + string(os.PathSeparator) + r.Name
}
func newChild(pa *Res, fi os.FileInfo) *Res {
	r := &Res{Name: fi.Name(), Parent: pa}
	p := r.Path()
	r.Id = NewId(p)
	if fi.IsDir() {
		r.Flag |= FlagDir
		r.Dir = &Dir{Path: p}
	}
	return r
}

type Watcher interface {
	Watch(r *Res) error
	Close() error
}
type Op uint

const (
	Add Op = 1 << iota
	Change
	Remove
	_
	Create
	Modify
	Delete
	_
	WsMask Op = 0x0F
	FsMask Op = 0xF0
)

type Config struct {
	CapHint uint
	Watcher func(Controller) (Watcher, error)
	Handler func(Op, *Res)
}

func (c Config) handle(op Op, r *Res) {
	if c.Handler != nil {
		c.Handler(op, r)
	}
}

type Controller interface {
	Control(op Op, id Id, name string) error
}

type Ws struct {
	sync.RWMutex
	config  Config
	root    *Res
	all     map[Id]*Res
	watcher Watcher
}

// New creates a workspace configured with c.
func New(c Config) *Ws {
	r := &Res{Id: NewId("/")}
	w := &Ws{config: c, root: r, all: make(map[Id]*Res, c.CapHint)}
	w.all[r.Id] = r
	return w
}

// Mount adds the directory tree at path to the workspace.
func (w *Ws) Mount(path string) (*Res, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return nil, err
	}
	if !fi.IsDir() {
		return nil, fmt.Errorf("not a directory")
	}
	r, err := w.mount(path)
	if err != nil {
		return r, err
	}
	r.Lock()
	err = read(r)
	r.Unlock()
	if err != nil {
		return r, err
	}
	w.Lock()
	defer w.Unlock()
	w.addAllChildren(0, r)
	return r, nil
}
func (w *Ws) mount(path string) (*Res, error) {
	id := NewId(path)
	d, f := filepath.Split(path)
	w.Lock()
	defer w.Unlock()
	if w.watcher == nil && w.config.Watcher != nil {
		watcher, err := w.config.Watcher((*ctrl)(w))
		if err != nil {
			return nil, err
		}
		w.watcher = watcher
	}
	r, ok := w.all[id]
	if ok {
		return r, fmt.Errorf("duplicate")
	}
	r = &Res{Id: id, Name: f, Flag: FlagDir | FlagMount, Dir: &Dir{Path: path}}
	// add virtual parent
	r.Parent = w.addParent(d[:len(d)-1])
	w.all[id] = r
	w.config.handle(Add, r)
	return r, nil
}

// Close closes the workspace.
// The workspace and all its resources are invalid afterwards.
func (w *Ws) Close() {
	w.Lock()
	defer w.Unlock()
	if w.watcher != nil {
		w.watcher.Close()
		w.watcher = nil
	}
	// scatter garbage
	for id, r := range w.all {
		r.Parent, r.Dir = nil, nil
		delete(w.all, id)
	}
	w.all = nil
	w.root = nil
}
func (w *Ws) addParent(path string) *Res {
	id := NewId(path)
	if r, ok := w.all[id]; ok {
		return r
	}
	parent, name := filepath.Split(path)
	var pa *Res
	if len(parent) > 0 && parent != "/" {
		pa = w.addParent(parent[:len(parent)-1])
	}
	r := &Res{Id: id, Name: name, Flag: FlagDir | FlagLogical, Parent: pa}
	w.all[id] = r
	return r
}
func read(r *Res) error {
	f, err := os.Open(r.Dir.Path)
	if err != nil {
		return err
	}
	list, err := f.Readdir(-1)
	f.Close()
	if err != nil {
		return err
	}
	children := make([]*Res, 0, len(list))
	for _, fi := range list {
		children = append(children, newChild(r, fi))
	}
	sort.Sort(byTypeAndName(children))
	r.Children = children
	for _, c := range children {
		if c.Flag&FlagDir != 0 {
			if err := read(c); err != nil {
				fmt.Println(err)
			}
		}
	}
	return nil
}
func (w *Ws) addAllChildren(fsop Op, r *Res) {
	for _, c := range r.Children {
		w.all[c.Id] = c
		w.config.handle(fsop|Add, c)
		if c.Flag&FlagDir != 0 {
			w.addAllChildren(fsop, c)
		}
	}
	if w.watcher != nil {
		err := w.watcher.Watch(r)
		if err != nil {
			fmt.Println(err)
		}
	}
	w.config.handle(fsop|Change, r)
}

type ctrl Ws

func (w *ctrl) Control(op Op, id Id, name string) error {
	var r, p *Res
	w.Lock()
	defer w.Unlock()
	r = w.all[id]
	if name != "" {
		p, r = r, nil
		if p != nil && p.Dir != nil {
			p.Lock()
			r = find(p.Children, name)
			p.Unlock()
		}
	}
	switch {
	case op&Delete != 0:
		if r == nil {
			break
		}
		return w.remove(op, r)
	case r != nil:
		// res found, modify
		return w.change(op, r)
	case p != nil:
		// parent found create child
		return w.add(op, p, name)
	}
	// not found, ignore
	return nil
}
func (w *ctrl) change(fsop Op, r *Res) error {
	w.config.handle(fsop|Change, r)
	return nil
}
func (w *ctrl) remove(fsop Op, r *Res) error {
	if p := r.Parent; p != nil {
		p.Lock()
		defer p.Unlock()
		if p.Dir != nil {
			p.Children = remove(p.Children, r)
		}
	}
	rm := []*Res{r}
	if r.Dir != nil {
		walk(r.Children, func(c *Res) error {
			rm = append(rm, c)
			return nil
		})
	}
	for i := len(rm) - 1; i >= 0; i-- {
		c := rm[i]
		w.config.handle(fsop|Remove, c)
		delete(w.all, c.Id)
	}
	return nil
}
func (w *ctrl) add(fsop Op, p *Res, name string) error {
	p.Lock()
	defer p.Unlock()
	// new lock try again
	r := find(p.Children, name)
	// ignore duplicate
	if r != nil {
		return nil
	}
	r = &Res{Name: name, Parent: p}
	path := r.Path()
	r.Id = NewId(path)
	fi, err := os.Stat(path)
	if err != nil {
		return err
	}

	p.Children = insert(p.Children, r)
	w.all[r.Id] = r
	if !fi.IsDir() {
		// send event
		return nil
	}
	r.Flag |= FlagDir
	r.Dir = &Dir{Path: path}
	if err = read(r); err != nil {
		return err
	}
	w.config.handle(fsop|Add, r)
	(*Ws)(w).addAllChildren(fsop, r)
	return nil
}

type byTypeAndName []*Res

func (l byTypeAndName) Len() int {
	return len(l)
}
func less(i, j *Res) bool {
	if isdir := i.Flag&FlagDir != 0; isdir != (j.Flag&FlagDir != 0) {
		return isdir
	}
	return i.Name < j.Name
}
func (l byTypeAndName) Less(i, j int) bool {
	return less(l[i], l[j])
}
func (l byTypeAndName) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}

func insert(l []*Res, r *Res) []*Res {
	i := sort.Search(len(l), func(i int) bool {
		return less(r, l[i])
	})
	if i < len(l) {
		if i > 0 && l[i-1].Id == r.Id {
			l[i-1] = r
			return l
		}
		return append(l[:i], append([]*Res{r}, l[i:]...)...)
	}
	return append(l, r)
}
func remove(l []*Res, r *Res) []*Res {
	i := sort.Search(len(l), func(i int) bool {
		return less(r, l[i])
	})
	if i > 0 && l[i-1].Id == r.Id {
		return append(l[:i-1], l[i:]...)
	}
	return l
}

var Skip = fmt.Errorf("skip")

func walk(l []*Res, f func(*Res) error) error {
	var err error
	for _, c := range l {
		if err = f(c); err == Skip {
			continue
		}
		if err != nil {
			return err
		}
		if c.Dir != nil {
			if err = walk(c.Children, f); err != nil {
				return err
			}
		}
	}
	return nil
}
func find(l []*Res, name string) *Res {
	for _, r := range l {
		if r.Name == name {
			return r
		}
	}
	return nil
}
<|endoftext|>


<commit_before>/*
 * Copyright 2018, 2021 Arm Limited.
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 (
	"flag"
	"os"
	"runtime/pprof"

	"github.com/ARM-software/bob-build/core"
	"github.com/ARM-software/bob-build/internal/utils"
)

func main() {
	// The primary builder should use the global flag set because the
	// bootstrap package registers its own flags there.
	flag.Parse()

	cpuprofile, present := os.LookupEnv("BOB_CPUPROFILE")
	if present && cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			utils.Die("%v", err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	core.Main()
}
<commit_msg>Add memory profiling support<commit_after>/*
 * Copyright 2018, 2021 Arm Limited.
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 (
	"flag"
	"os"
	"runtime"
	"runtime/pprof"

	"github.com/ARM-software/bob-build/core"
	"github.com/ARM-software/bob-build/internal/utils"
)

func main() {
	// The primary builder should use the global flag set because the
	// bootstrap package registers its own flags there.
	flag.Parse()

	cpuprofile, present := os.LookupEnv("BOB_CPUPROFILE")
	if present && cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			utils.Die("%v", err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	core.Main()

	memprofile, present := os.LookupEnv("BOB_MEMPROFILE")
	if present && memprofile != "" {
		f, err := os.Create(memprofile)
		if err != nil {
			utils.Die("%v", err)
		}
		runtime.GC()
		pprof.WriteHeapProfile(f)
	}
}
<|endoftext|>


<commit_before>package main

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

	"github.com/paked/configure"
	"github.com/paked/messenger"
)

var (
	conf        = configure.New()
	verifyToken = conf.String("verify-token", "mad-skrilla", "The token used to verify facebook")
	verify      = conf.Bool("should-verify", false, "Whether or not the app should verify itself")
	pageToken   = conf.String("page-token", "not skrilla", "The token that is used to verify the page on facebook")
	appSecret   = conf.String("app-secret", "", "The app secret from the facebook developer portal")
	port        = conf.Int("port", 8080, "The port used to serve the messenger bot")
)

func main() {
	conf.Use(configure.NewFlag())
	conf.Use(configure.NewEnvironment())
	conf.Use(configure.NewJSONFromFile("config.json"))

	conf.Parse()

	// Create a new messenger client
	client := messenger.New(messenger.Options{
		Verify:      *verify,
		AppSecret:   *appSecret,
		VerifyToken: *verifyToken,
		Token:       *pageToken,
	})

	// Setup a handler to be triggered when a message is received
	client.HandleMessage(func(m messenger.Message, r *messenger.Response) {
		fmt.Printf("%v (Sent, %v)\n", m.Text, m.Time.Format(time.UnixDate))

		p, err := client.ProfileByID(m.Sender.ID)
		if err != nil {
			fmt.Println("Something went wrong!", err)
		}

		r.Text(fmt.Sprintf("Hello, %v!", p.FirstName))
	})

	// Setup a handler to be triggered when a message is delivered
	client.HandleDelivery(func(d messenger.Delivery, r *messenger.Response) {
		fmt.Println("Delivered at:", d.Watermark().Format(time.UnixDate))
	})

	// Setup a handler to be triggered when a message is read
	client.HandleRead(func(m messenger.Read, r *messenger.Response) {
		fmt.Println("Read at:", m.Watermark().Format(time.UnixDate))
	})

	fmt.Printf("Serving messenger bot on localhost:%d\n", *port)

	http.ListenAndServe(fmt.Sprintf("localhost:%d", *port), client.Handler())
}
<commit_msg>Fix example to build with latest version of the library<commit_after>package main

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

	"github.com/paked/configure"
	"github.com/paked/messenger"
)

var (
	conf        = configure.New()
	verifyToken = conf.String("verify-token", "mad-skrilla", "The token used to verify facebook")
	verify      = conf.Bool("should-verify", false, "Whether or not the app should verify itself")
	pageToken   = conf.String("page-token", "not skrilla", "The token that is used to verify the page on facebook")
	appSecret   = conf.String("app-secret", "", "The app secret from the facebook developer portal")
	port        = conf.Int("port", 8080, "The port used to serve the messenger bot")
)

func main() {
	conf.Use(configure.NewFlag())
	conf.Use(configure.NewEnvironment())
	conf.Use(configure.NewJSONFromFile("config.json"))

	conf.Parse()

	// Create a new messenger client
	client := messenger.New(messenger.Options{
		Verify:      *verify,
		AppSecret:   *appSecret,
		VerifyToken: *verifyToken,
		Token:       *pageToken,
	})

	// Setup a handler to be triggered when a message is received
	client.HandleMessage(func(m messenger.Message, r *messenger.Response) {
		fmt.Printf("%v (Sent, %v)\n", m.Text, m.Time.Format(time.UnixDate))

		p, err := client.ProfileByID(m.Sender.ID)
		if err != nil {
			fmt.Println("Something went wrong!", err)
		}

		r.Text(fmt.Sprintf("Hello, %v!", p.FirstName), messenger.ResponseType)
	})

	// Setup a handler to be triggered when a message is delivered
	client.HandleDelivery(func(d messenger.Delivery, r *messenger.Response) {
		fmt.Println("Delivered at:", d.Watermark().Format(time.UnixDate))
	})

	// Setup a handler to be triggered when a message is read
	client.HandleRead(func(m messenger.Read, r *messenger.Response) {
		fmt.Println("Read at:", m.Watermark().Format(time.UnixDate))
	})

	fmt.Printf("Serving messenger bot on localhost:%d\n", *port)

	http.ListenAndServe(fmt.Sprintf("localhost:%d", *port), client.Handler())
}
<|endoftext|>


<commit_before>package backend

import (
	"encoding/json"
	"net/http"
	"regexp"

	"github.com/Sirupsen/logrus"
	"github.com/go-errors/errors"
	"github.com/mhe/gabi"
	"github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/irmaserver"
)

func Initialize(configuration *irmaserver.Configuration) error {
	conf = configuration

	if conf.Logger == nil {
		conf.Logger = logrus.New()
		conf.Logger.Level = logrus.DebugLevel
		conf.Logger.Formatter = &logrus.TextFormatter{}
	}

	if conf.IrmaConfiguration == nil {
		var err error
		conf.IrmaConfiguration, err = irma.NewConfiguration(conf.IrmaConfigurationPath, "")
		if err != nil {
			return err
		}
		if err = conf.IrmaConfiguration.ParseFolder(); err != nil {
			return err
		}
	}

	return nil
}

func StartSession(request irma.SessionRequest) (*irma.Qr, string, error) {
	if err := request.Validate(); err != nil {
		return nil, "", err
	}
	action := irma.ActionUnknown
	switch request.(type) {
	case *irma.DisclosureRequest:
		action = irma.ActionDisclosing
	case *irma.SignatureRequest:
		action = irma.ActionSigning
	case *irma.IssuanceRequest:
		action = irma.ActionIssuing
		if err := validateIssuanceRequest(request.(*irma.IssuanceRequest)); err != nil {
			return nil, "", err
		}
	default:
		conf.Logger.Warnf("Attempt to start session of invalid type")
		return nil, "", errors.New("Invalid session type")
	}

	session := newSession(action, request)
	conf.Logger.Infof("%s session started, token %s", action, session.token)
	return &irma.Qr{
		Type: action,
		URL:  session.token,
	}, session.token, nil
}

func GetSessionResult(token string) *irmaserver.SessionResult {
	session := sessions.get(token)
	if session != nil {
		return nil
	}
	return session.result
}

func HandleProtocolMessage(
	path string,
	method string,
	headers map[string][]string,
	message []byte,
) (status int, output []byte, result *irmaserver.SessionResult) {
	// Parse path into session and action
	if len(path) > 0 { // Remove any starting and trailing slash
		if path[0] == '/' {
			path = path[1:]
		}
		if path[len(path)-1] == '/' {
			path = path[:len(path)-1]
		}
	}
	conf.Logger.Debugf("Routing protocol message: %s %s", method, path)
	pattern := regexp.MustCompile("(\\w+)/?(\\w*)")
	matches := pattern.FindStringSubmatch(path)
	if len(matches) != 3 {
		conf.Logger.Warnf("Invalid URL: %s", path)
		status, output = responseJson(nil, getError(irmaserver.ErrorInvalidRequest, ""))
		return
	}

	// Fetch the session
	token := matches[1]
	verb := matches[2]
	session := sessions.get(token)
	if session == nil {
		conf.Logger.Warnf("Session not found: %s", token)
		status, output = responseJson(nil, getError(irmaserver.ErrorSessionUnknown, ""))
		return
	}
	session.Lock()
	defer session.Unlock()

	// However we return, if the session has been finished or cancelled by any of the handlers
	// then we should inform the user by returning a SessionResult - but only if we have not
	// already done this in the past, e.g. by a previous HTTP call handled by this function
	defer func() {
		if session.finished() && !session.returned {
			session.returned = true
			result = session.result
		}
		sessions.update(token, session)
	}()

	// Route to handler
	switch len(verb) {
	case 0:
		if method == "DELETE" {
			session.handleDelete()
			status = http.StatusOK
			return
		}
		if method == "GET" {
			h := http.Header(headers)
			min := &irma.ProtocolVersion{}
			max := &irma.ProtocolVersion{}
			if err := json.Unmarshal([]byte(h.Get(irma.MinVersionHeader)), min); err != nil {
				status, output = responseJson(nil, session.fail(irmaserver.ErrorMalformedInput, err.Error()))
				return
			}
			if err := json.Unmarshal([]byte(h.Get(irma.MaxVersionHeader)), max); err != nil {
				status, output = responseJson(nil, session.fail(irmaserver.ErrorMalformedInput, err.Error()))
				return
			}
			status, output = responseJson(session.handleGetRequest(min, max))
			return
		}
		status, output = responseJson(nil, session.fail(irmaserver.ErrorInvalidRequest, ""))
		return
	default:
		if method != "POST" {
			status, output = responseJson(nil, session.fail(irmaserver.ErrorInvalidRequest, ""))
			return
		}

		if verb == "commitments" && session.action == irma.ActionIssuing {
			commitments := &gabi.IssueCommitmentMessage{}
			if err := irma.UnmarshalValidate(message, commitments); err != nil {
				status, output = responseJson(nil, session.fail(irmaserver.ErrorMalformedInput, ""))
				return
			}
			status, output = responseJson(session.handlePostCommitments(commitments))
			return
		}
		if verb == "proofs" && session.action == irma.ActionDisclosing {
			proofs := gabi.ProofList{}
			if err := irma.UnmarshalValidate(message, &proofs); err != nil {
				status, output = responseJson(nil, session.fail(irmaserver.ErrorMalformedInput, ""))
				return
			}
			status, output = responseJson(session.handlePostProofs(proofs))
			return
		}
		if verb == "proofs" && session.action == irma.ActionSigning {
			signature := &irma.SignedMessage{}
			if err := irma.UnmarshalValidate(message, signature); err != nil {
				status, output = responseJson(nil, session.fail(irmaserver.ErrorMalformedInput, ""))
				return
			}
			status, output = responseJson(session.handlePostSignature(signature))
			return
		}

		status, output = responseJson(nil, session.fail(irmaserver.ErrorInvalidRequest, ""))
		return
	}
}
<commit_msg>Use http.MethodGet and friends<commit_after>package backend

import (
	"encoding/json"
	"net/http"
	"regexp"

	"github.com/Sirupsen/logrus"
	"github.com/go-errors/errors"
	"github.com/mhe/gabi"
	"github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/irmaserver"
)

func Initialize(configuration *irmaserver.Configuration) error {
	conf = configuration

	if conf.Logger == nil {
		conf.Logger = logrus.New()
		conf.Logger.Level = logrus.DebugLevel
		conf.Logger.Formatter = &logrus.TextFormatter{}
	}

	if conf.IrmaConfiguration == nil {
		var err error
		conf.IrmaConfiguration, err = irma.NewConfiguration(conf.IrmaConfigurationPath, "")
		if err != nil {
			return err
		}
		if err = conf.IrmaConfiguration.ParseFolder(); err != nil {
			return err
		}
	}

	return nil
}

func StartSession(request irma.SessionRequest) (*irma.Qr, string, error) {
	if err := request.Validate(); err != nil {
		return nil, "", err
	}
	action := irma.ActionUnknown
	switch request.(type) {
	case *irma.DisclosureRequest:
		action = irma.ActionDisclosing
	case *irma.SignatureRequest:
		action = irma.ActionSigning
	case *irma.IssuanceRequest:
		action = irma.ActionIssuing
		if err := validateIssuanceRequest(request.(*irma.IssuanceRequest)); err != nil {
			return nil, "", err
		}
	default:
		conf.Logger.Warnf("Attempt to start session of invalid type")
		return nil, "", errors.New("Invalid session type")
	}

	session := newSession(action, request)
	conf.Logger.Infof("%s session started, token %s", action, session.token)
	return &irma.Qr{
		Type: action,
		URL:  session.token,
	}, session.token, nil
}

func GetSessionResult(token string) *irmaserver.SessionResult {
	session := sessions.get(token)
	if session != nil {
		return nil
	}
	return session.result
}

func HandleProtocolMessage(
	path string,
	method string,
	headers map[string][]string,
	message []byte,
) (status int, output []byte, result *irmaserver.SessionResult) {
	// Parse path into session and action
	if len(path) > 0 { // Remove any starting and trailing slash
		if path[0] == '/' {
			path = path[1:]
		}
		if path[len(path)-1] == '/' {
			path = path[:len(path)-1]
		}
	}
	conf.Logger.Debugf("Routing protocol message: %s %s", method, path)
	pattern := regexp.MustCompile("(\\w+)/?(\\w*)")
	matches := pattern.FindStringSubmatch(path)
	if len(matches) != 3 {
		conf.Logger.Warnf("Invalid URL: %s", path)
		status, output = responseJson(nil, getError(irmaserver.ErrorInvalidRequest, ""))
		return
	}

	// Fetch the session
	token := matches[1]
	verb := matches[2]
	session := sessions.get(token)
	if session == nil {
		conf.Logger.Warnf("Session not found: %s", token)
		status, output = responseJson(nil, getError(irmaserver.ErrorSessionUnknown, ""))
		return
	}
	session.Lock()
	defer session.Unlock()

	// However we return, if the session has been finished or cancelled by any of the handlers
	// then we should inform the user by returning a SessionResult - but only if we have not
	// already done this in the past, e.g. by a previous HTTP call handled by this function
	defer func() {
		if session.finished() && !session.returned {
			session.returned = true
			result = session.result
		}
		sessions.update(token, session)
	}()

	// Route to handler
	switch len(verb) {
	case 0:
		if method == http.MethodDelete {
			session.handleDelete()
			status = http.StatusOK
			return
		}
		if method == http.MethodGet {
			h := http.Header(headers)
			min := &irma.ProtocolVersion{}
			max := &irma.ProtocolVersion{}
			if err := json.Unmarshal([]byte(h.Get(irma.MinVersionHeader)), min); err != nil {
				status, output = responseJson(nil, session.fail(irmaserver.ErrorMalformedInput, err.Error()))
				return
			}
			if err := json.Unmarshal([]byte(h.Get(irma.MaxVersionHeader)), max); err != nil {
				status, output = responseJson(nil, session.fail(irmaserver.ErrorMalformedInput, err.Error()))
				return
			}
			status, output = responseJson(session.handleGetRequest(min, max))
			return
		}
		status, output = responseJson(nil, session.fail(irmaserver.ErrorInvalidRequest, ""))
		return
	default:
		if method != http.MethodPost {
			status, output = responseJson(nil, session.fail(irmaserver.ErrorInvalidRequest, ""))
			return
		}

		if verb == "commitments" && session.action == irma.ActionIssuing {
			commitments := &gabi.IssueCommitmentMessage{}
			if err := irma.UnmarshalValidate(message, commitments); err != nil {
				status, output = responseJson(nil, session.fail(irmaserver.ErrorMalformedInput, ""))
				return
			}
			status, output = responseJson(session.handlePostCommitments(commitments))
			return
		}
		if verb == "proofs" && session.action == irma.ActionDisclosing {
			proofs := gabi.ProofList{}
			if err := irma.UnmarshalValidate(message, &proofs); err != nil {
				status, output = responseJson(nil, session.fail(irmaserver.ErrorMalformedInput, ""))
				return
			}
			status, output = responseJson(session.handlePostProofs(proofs))
			return
		}
		if verb == "proofs" && session.action == irma.ActionSigning {
			signature := &irma.SignedMessage{}
			if err := irma.UnmarshalValidate(message, signature); err != nil {
				status, output = responseJson(nil, session.fail(irmaserver.ErrorMalformedInput, ""))
				return
			}
			status, output = responseJson(session.handlePostSignature(signature))
			return
		}

		status, output = responseJson(nil, session.fail(irmaserver.ErrorInvalidRequest, ""))
		return
	}
}
<|endoftext|>


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

package main

import (
	"errors"
	"os/exec"
	"time"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/juju"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/utils"
)

// SSHCommand is responsible for launching a ssh shell on a given unit or machine.
type SSHCommand struct {
	SSHCommon
}

// SSHCommon provides common methods for SSHCommand, SCPCommand and DebugHooksCommand.
type SSHCommon struct {
	cmd.EnvCommandBase
	Target    string
	Args      []string
	apiClient *api.Client
}

const sshDoc = `
Launch an ssh shell on the machine identified by the <target> parameter.
<target> can be either a machine id  as listed by "juju status" in the
"machines" section or a unit name as listed in the "services" section.
Any extra parameters are passsed as extra parameters to the ssh command.

Examples

Connect to machine 0:

    juju ssh 0

Connect to the second mysql unit:

    juju ssh mysql/0
`

func (c *SSHCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "ssh",
		Args:    "<target> [<ssh args>...]",
		Purpose: "launch an ssh shell on a given unit or machine",
		Doc:     sshDoc,
	}
}

func (c *SSHCommand) Init(args []string) error {
	if len(args) == 0 {
		return errors.New("no target name specified")
	}
	c.Target, c.Args = args[0], args[1:]
	return nil
}

// Run resolves c.Target to a machine, to the address of a i
// machine or unit forks ssh passing any arguments provided.
func (c *SSHCommand) Run(ctx *cmd.Context) error {
	if c.apiClient == nil {
		var err error
		c.apiClient, err = c.initAPIClient()
		if err != nil {
			return err
		}
		defer c.apiClient.Close()
	}
	host, err := c.hostFromTarget(c.Target)
	if err != nil {
		return err
	}
	args := []string{"-l", "ubuntu", "-t", "-o", "StrictHostKeyChecking no", "-o", "PasswordAuthentication no", host}
	args = append(args, c.Args...)
	cmd := exec.Command("ssh", args...)
	cmd.Stdin = ctx.Stdin
	cmd.Stdout = ctx.Stdout
	cmd.Stderr = ctx.Stderr
	return cmd.Run()
}

// initAPIClient initialises the state connection.
// It is the caller's responsibility to close the connection.
func (c *SSHCommon) initAPIClient() (*api.Client, error) {
	var err error
	c.apiClient, err = juju.NewAPIClientFromName(c.EnvName)
	return c.apiClient, err
}

// attemptStarter is an interface corresponding to utils.AttemptStrategy
type attemptStarter interface {
	Start() attempt
}

type attempt interface {
	Next() bool
}

type attemptStrategy utils.AttemptStrategy

func (s attemptStrategy) Start() attempt {
	return utils.AttemptStrategy(s).Start()
}

var sshHostFromTargetAttemptStrategy attemptStarter = attemptStrategy{
	Total: 5 * time.Second,
	Delay: 500 * time.Millisecond,
}

func (c *SSHCommon) hostFromTarget(target string) (string, error) {
	var addr string
	var err error
	// A target may not initially have an address (e.g. the
	// address updater hasn't yet run), so we must do this in
	// a loop.
	for a := sshHostFromTargetAttemptStrategy.Start(); a.Next(); {
		addr, err = c.apiClient.PublicAddress(target)
		if err == nil {
			break
		}
	}
	if err != nil {
		return "", err
	}
	logger.Infof("Resolved public address of %q: %q", target, addr)
	return addr, nil
}

// AllowInterspersedFlags for ssh/scp is set to false so that
// flags after the unit name are passed through to ssh, for eg.
// `juju ssh -v service-name/0 uname -a`.
func (c *SSHCommon) AllowInterspersedFlags() bool {
	return false
}
<commit_msg>make the unit example consistent<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"errors"
	"os/exec"
	"time"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/juju"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/utils"
)

// SSHCommand is responsible for launching a ssh shell on a given unit or machine.
type SSHCommand struct {
	SSHCommon
}

// SSHCommon provides common methods for SSHCommand, SCPCommand and DebugHooksCommand.
type SSHCommon struct {
	cmd.EnvCommandBase
	Target    string
	Args      []string
	apiClient *api.Client
}

const sshDoc = `
Launch an ssh shell on the machine identified by the <target> parameter.
<target> can be either a machine id  as listed by "juju status" in the
"machines" section or a unit name as listed in the "services" section.
Any extra parameters are passsed as extra parameters to the ssh command.

Examples

Connect to machine 0:

    juju ssh 0

Connect to the first mysql unit:

    juju ssh mysql/0
`

func (c *SSHCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "ssh",
		Args:    "<target> [<ssh args>...]",
		Purpose: "launch an ssh shell on a given unit or machine",
		Doc:     sshDoc,
	}
}

func (c *SSHCommand) Init(args []string) error {
	if len(args) == 0 {
		return errors.New("no target name specified")
	}
	c.Target, c.Args = args[0], args[1:]
	return nil
}

// Run resolves c.Target to a machine, to the address of a i
// machine or unit forks ssh passing any arguments provided.
func (c *SSHCommand) Run(ctx *cmd.Context) error {
	if c.apiClient == nil {
		var err error
		c.apiClient, err = c.initAPIClient()
		if err != nil {
			return err
		}
		defer c.apiClient.Close()
	}
	host, err := c.hostFromTarget(c.Target)
	if err != nil {
		return err
	}
	args := []string{"-l", "ubuntu", "-t", "-o", "StrictHostKeyChecking no", "-o", "PasswordAuthentication no", host}
	args = append(args, c.Args...)
	cmd := exec.Command("ssh", args...)
	cmd.Stdin = ctx.Stdin
	cmd.Stdout = ctx.Stdout
	cmd.Stderr = ctx.Stderr
	return cmd.Run()
}

// initAPIClient initialises the state connection.
// It is the caller's responsibility to close the connection.
func (c *SSHCommon) initAPIClient() (*api.Client, error) {
	var err error
	c.apiClient, err = juju.NewAPIClientFromName(c.EnvName)
	return c.apiClient, err
}

// attemptStarter is an interface corresponding to utils.AttemptStrategy
type attemptStarter interface {
	Start() attempt
}

type attempt interface {
	Next() bool
}

type attemptStrategy utils.AttemptStrategy

func (s attemptStrategy) Start() attempt {
	return utils.AttemptStrategy(s).Start()
}

var sshHostFromTargetAttemptStrategy attemptStarter = attemptStrategy{
	Total: 5 * time.Second,
	Delay: 500 * time.Millisecond,
}

func (c *SSHCommon) hostFromTarget(target string) (string, error) {
	var addr string
	var err error
	// A target may not initially have an address (e.g. the
	// address updater hasn't yet run), so we must do this in
	// a loop.
	for a := sshHostFromTargetAttemptStrategy.Start(); a.Next(); {
		addr, err = c.apiClient.PublicAddress(target)
		if err == nil {
			break
		}
	}
	if err != nil {
		return "", err
	}
	logger.Infof("Resolved public address of %q: %q", target, addr)
	return addr, nil
}

// AllowInterspersedFlags for ssh/scp is set to false so that
// flags after the unit name are passed through to ssh, for eg.
// `juju ssh -v service-name/0 uname -a`.
func (c *SSHCommon) AllowInterspersedFlags() bool {
	return false
}
<|endoftext|>